DEV Community

Cover image for Building a Pipeline to Deploy Terraform Updates
Kaye Alvarado for AWS Community Builders

Posted on

Building a Pipeline to Deploy Terraform Updates

This is the last part of the series for Infrastructure as Code: Deploying a Demo App on EC2 with AWS Image Builder and Terraform. On this part, I will focus more on GitHub Actions and how to utilize this CI/CD platform to deploy updates to your Infrastructure code.

Start with a Pre-defined Workflow File

GitHub Actions already provides a sample template to start. Simply search for the sample template in Actions tab, and click on the Configure button.

Image description

This will create a terraform.yml file with all the required steps for a Terraform deployment.

Image description

Let's walk through each one.
1. The Trigger
This part indicates different GitHub actions to trigger the workflow. The default actions triggers it with a push and pull request action. Since I want to trigger the workflow manually, I added a workflow dispatch action.

name: 'Terraform'

    branches: [ "main" ]
Enter fullscreen mode Exit fullscreen mode

2. Jobs
The next part will be the actions that will run. Below is the initialization of the GitHub runner (ubuntu-latest image) that will be spun up as the workflow runs.

    name: 'Terraform'
    runs-on: ubuntu-latest
    environment: production
Enter fullscreen mode Exit fullscreen mode

The other parts below have definitions in the sample file on what their purpose is:

    # Checkout the repository to the GitHub Actions runner
    - name: Checkout
      uses: actions/checkout@v3

    # Install the latest version of Terraform CLI and configure the Terraform CLI configuration file with a Terraform Cloud user API token
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
        cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}

    # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
    - name: Terraform Init
      run: terraform init

    # Checks that all Terraform configuration files adhere to a canonical format
    - name: Terraform Format
      run: terraform fmt -check

    # Generates an execution plan for Terraform
    - name: Terraform Plan
      run: terraform plan -input=false
Enter fullscreen mode Exit fullscreen mode

One customization that needs to be done is to create the Terraform API token (TF_API_TOKEN) which will be saved in GitHub secrets as it is an authentication header that will be used. We can generate a token by following this Terraform documentation: Type terraform login and type yes on the prompt.

$ terraform login
Terraform will request an API token for using your browser.

If login is successful, Terraform will store the token in plain text in
the following file for use by subsequent commands:

Do you want to proceed?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes


Terraform must now open a web browser to the tokens page for

If a browser does not open this automatically, open the following URL to proceed:


Generate a token using your browser, and copy-paste it into this prompt.

Terraform will store the token in plain text in the following file
for use by subsequent commands:

Token for
  Enter a value:
Enter fullscreen mode Exit fullscreen mode

On the browser, login to Terraform cloud, and you will be redirected to this page. You can type a name to identify this token, and set various expiration dates.

Image description

You can now save this token under GitHub Secrets

Image description

Go back to the GitHub actions template and commit the change:

Image description

Since the push action triggers the workflow, you would see that updating the code will trigger the action:

Image description

3. Create an AWS credential
You'll notice that on the initial run, this would immediately fail on Terraform initialize. This is because we haven't configured any AWS credentials on the workflow. We can follow this documentation, or follow along below:

Before this can be done, we need to create an IAM role. Go to the AWS management console and create a role for Web Identity, adding the fields.

Image description

AWS also made it available to pre-generate the trust policy to be added to the role

    "Version": "2012-10-17",
    "Statement": [
            "Effect": "Allow",
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Principal": {
                "Federated": "arn:aws:iam::111111111111:oidc-provider/"
            "Condition": {
                "StringEquals": {
                    "": [
                "StringLike": {
                    "": [
Enter fullscreen mode Exit fullscreen mode

Now add the GitHub action below and add the ARN of the role created.

- name: Configure AWS credentials from Test account
      uses: aws-actions/configure-aws-credentials@v3
        role-to-assume: arn:aws:iam::11111111111:role/aws-githubactions
        aws-region: us-east-1
Enter fullscreen mode Exit fullscreen mode

You also need to update the permissions of id-token to write

  id-token: write
  contents: read
Enter fullscreen mode Exit fullscreen mode

On this run, it will not automatically apply the Terraform code, but show the plan.

Image description
4. Final Updates
You can now update your pipeline to add specific GitHub Actions triggers. Some best practices for Infrastructure pipeline are below:

  1. Set a manual trigger of the workflow. We do not ever want to accidentally deploy infrastructure without proper review.
  2. Add tests. One Terraform feature is to be able to test the linting of your terraform code. This is one good test to add on top of other tests.
  3. Add approval checks. You can add notifications to your CI/CD pipeline to ensure that someone in authority is able to review the infrastructure being deployed before the infrastructure is actually updated.

If you have any questions or if any part is unclear, feel free to shoot a comment below!

Top comments (1)

tanushree_aggarwal profile image
Tanushree Aggarwal

interesting use case! nicely explained!