Skip to main content

Workflow

To bring it all together, we create a workflow that uses the composites of the test, plan, and apply stage in the final workflow.

Pipeline Trigger and Environment Variables

name: Terraform setup, test, plan, and apply

on:
pull_request:

env:
TERRAFORM_VERSION: "1.9.2"
TERRAFORM_DIRECTORY: "<terraform-directory>"
TF_IN_AUTOMATION: "True"
AWS_REGION: "<aws-region>"
AWS_ROLE_TO_ASSUME: "<aws-role-arn>"
AWS_ROLE_SESSION_NAME: "<aws-role-session-name>"

We start with the first code block in the workflow. In this code block, we specify the name of the workflow, how we want it to be triggered, and the environment variables to be used by the entire workflow.

The name is an arbitrary value that you can decide on. As for the way we want it to be triggered, we have specified it to be triggered whenever a pull request is opened. This allows for the feature of posting comments to the PR to shine and give reviewers a chance to look over the test, plan, and apply results.

Finally, the environment variables specified are applied to the whole workflow. Specify the Terraform directory based on your folder structure, and the AWS Role ARN, AWS Role Session Name and AWS Region which we have specified before.

Permissions

permissions:
id-token: write
contents: read
pull-requests: write
actions: read

The next code block specifies the permissions this pipeline has. The id-token makes use of the GITHUB_TOKEN used for authentication that then contents uses to read the repository contents such as commits.

Also, the pull-requests permission allows the pipeline to write to the open pull request, and finally, actions permissions is used to read the status of the action pipeline.

Test Stage

jobs:
terraform_init_test:
runs-on: ubuntu-latest
if: github.event.review.state != 'approved'
steps:
- uses: actions/checkout@v4

- name: Get PR ID
id: pr-id
shell: bash
env:
GITHUB_REF: ${{ inputs.github_ref }}
run: |
PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT

- name: Terraform Init and Test
uses: ./.github/test
with:
terraform_directory: ${{ env.TERRAFORM_DIRECTORY }}
root_directory: "."
terraform_version: ${{ env.TERRAFORM_VERSION }}
github_token: ${{ secrets.GITHUB_TOKEN }}
pr_id: ${{ steps.pr-id.outputs.PR_NUMBER }}
aws_role_to_assume: ${{ env.AWS_ROLE_TO_ASSUME }}
aws_role_session_name: ${{ env.AWS_ROLE_SESSION_NAME }}
aws_region: ${{ env.AWS_REGION }}

Next up is the test stage. In this code block, we specify where this job runs on and if it runs. These are the first two conditions we have for the stage. Afterwards, we start defining the steps for this stage. First of all, the runner fetches the code through the checkout step, then gets the pull request ID, and finally uses the test stage composite leveraging the environment variables.

Plan Stage

  terraform_plan:
runs-on: ubuntu-latest
if: github.event.review.state != 'approved'
needs: terraform_init_test
steps:
- uses: actions/checkout@v4

- name: Get PR ID
id: pr-id
shell: bash
env:
GITHUB_REF: ${{ inputs.github_ref }}
run: |
PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT

- name: Terraform Init and Plan
uses: ./.github/plan
with:
terraform_directory: ${{ env.TERRAFORM_DIRECTORY }}
terraform_version: ${{ env.TERRAFORM_VERSION }}
github_token: ${{ secrets.GITHUB_TOKEN }}
pr_id: ${{ steps.pr-id.outputs.PR_NUMBER }}
aws_role_to_assume: ${{ env.AWS_ROLE_TO_ASSUME }}
aws_role_session_name: ${{ env.AWS_ROLE_SESSION_NAME }}
aws_region: ${{ env.AWS_REGION }}

In this code block, we have the plan stage where we specify where this job runs on and if it runs. These are the first two conditions we have for the stage. Afterwards, we start defining the steps for this stage. First of all, the runner fetches the code through the checkout step, then gets the pull request ID, and finally uses the plan stage composite leveraging the environment variables.

Apply Stage

  terraform_apply:
runs-on: ubuntu-latest
if: github.event.review.state != 'approved'
needs: terraform_plan
steps:
- uses: actions/checkout@v4

- name: Get PR ID
id: pr-id
shell: bash
env:
GITHUB_REF: ${{ inputs.github_ref }}
run: |
PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT

- name: Terraform Init and Apply
uses: ./.github/apply
with:
terraform_directory: ${{ env.TERRAFORM_DIRECTORY }}
terraform_version: ${{ env.TERRAFORM_VERSION }}
github_token: ${{ secrets.GITHUB_TOKEN }}
pr_id: ${{ steps.pr-id.outputs.PR_NUMBER }}
aws_role_to_assume: ${{ env.AWS_ROLE_TO_ASSUME }}
aws_role_session_name: ${{ env.AWS_ROLE_SESSION_NAME }}
aws_region: ${{ env.AWS_REGION }}

Finally, we have the apply stage. We again specify where this job runs on and if it runs. These are the first two conditions we have for the stage. Afterwards, we start defining the steps for this stage. First of all, the runner fetches the code through the checkout step, then gets the pull request ID, and finally uses the apply stage composite leveraging the environment variables.