In this blog post, I’ll show you a cool trick – how to share your Terraform Plan right in the comments of your GitHub Pull Request using GitHub Actions. This comes with some handy benefits for your workflow:
- Streamlined Review Process: Easily share the Terraform Plan within the PR comments, saving reviewers from digging into additional logs.
- Enhanced Visibility: Users get a clear view of the plan without the need for extra steps, making the review more efficient.
- User-Friendly Integration: GitHub Actions automates the process, seamlessly incorporating the Terraform Plan into your PR, enhancing collaboration.
How to achieve this
As part of my Action workflow, I have used a step that is part of this marketplace action
Prior to running the GitHub Action
You will need to create some GitHub secrets that will be used:
CLIENT_ID: Client ID of the Azure AD Service Principal responsible for Terraform deployment.CLIENT_SECRET: Client Secret linked to the Azure AD Service Principal for Terraform deployment.DEPLOYMENT_SUBSCRIPTION_ID: Azure subscription ID where the Terraform storage account is located for storing the .tfstate file.SUBSCRIPTION_ID: Azure subscription ID where the Terraform code is intended to be deployed.TENANT_ID: Azure AD tenant ID associated with the deployment.USER_TOKEN: A GitHub Personal Access Token (PAT) is required. This can be created here. Feel free to fine-grain the permissions only for the repository.
Once your secrets are configured, and the workflow is customised, the GitHub Action will trigger automatically on each push or pull request to the specified branches. The Terraform Plan will be updated in the PR comments, providing an overview. (More on this below)
Updating the Pull-Request step
In your GitHub Actions workflow, customize the Update Pull Request step to suit your environment and requirements. This step essentially captures the essential information about the Terraform Plan and updates the PR comments.
- name: Update Pull Request
uses: actions/github-script@v7
with:
github-token: ${{ secrets.USER_TOKEN }}
script: |
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization 🤖\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${{ steps.plan.outputs.stdout }}
\`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
Lets breakdown this step, it will be used to take the Terraform outputs and update the pull-request comments, the main areas to point out:
- It will comment the step outcomes for your
terraform init,terraform fmtandterraform validate terraform planoutcome and if successful, it will also show a terraform plan- Will also include which PAT user account it will push the comment
Looking at the below screenshot – this will be how the comment will look in GitHub, awesome!

How to run the full workflow
Update the env values according to your requirements:
tf_resource_group_name– Resource Group Name of the Terraform state Storage accounttf_storage_account_name– Storage Account name to where the Terraform state file will be storedtf_state_container– Storage Account Container in which the Terraform state file will be storedtf_state_key– Name of the Terraform state file
name: Terraform Plan
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
env:
tf_resource_group_name: "thomasthorntoncloud"
tf_storage_account_name: "thomasthorntontfstate"
tf_state_container: "github-tfplan-output-to-pr"
tf_state_key: "terraform.tfstate"
jobs:
terraform-plan-output:
if: github.event_name == 'pull_request'
defaults:
run:
working-directory: terraform-example-deploy
name: Terraform
environment: production
runs-on: ubuntu-latest
env:
ARM_CLIENT_ID: "${{ secrets.AZURE_CLIENT_ID }}"
ARM_SUBSCRIPTION_ID: "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
ARM_TENANT_ID: "${{ secrets.AZURE_TENANT_ID }}"
steps:
- name: Checkout Code
uses: actions/checkout@v2.5.0
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: true
- name: Terraform Init
id: init
run: terraform init -backend-config="resource_group_name=$tf_resource_group_name" -backend-config="storage_account_name=$tf_storage_account_name" -backend-config="container_name=$tf_state_container" -backend-config="key=$tf_state_key"
env:
ARM_CLIENT_ID: ${{ secrets.CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.TENANT_ID }}
working-directory: ./terraform
- name: Terraform Fmt
id: fmt
run: terraform fmt -check
working-directory: ./terraform
- name: Terraform Validate
id: validate
run: terraform validate -no-color
working-directory: ./terraform
- name: Terraform Plan
id: plan
run: terraform plan -no-color -var deployment_subscription_id=$DEPLOYMENT_SUBSCRIPTION_ID
env:
ARM_CLIENT_ID: ${{ secrets.CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.TENANT_ID }}
DEPLOYMENT_SUBSCRIPTION_ID: ${{ secrets.SUBSCRIPTION_ID }}
working-directory: ./terraform
continue-on-error: false
- name: Update Pull Request
uses: actions/github-script@v7
with:
github-token: ${{ secrets.USER_TOKEN }}
script: |
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization 🤖\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${{ steps.plan.outputs.stdout }}
\`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1
Also note, the above will only run when its as pull request as the below setting is configured.
if: github.event_name == 'pull_request'
With these steps in place, your GitHub Pull Requests will now have an automated update of your Terraform Plan, providing an overview directly in the comments. This not only streamlines your review process but also enhances collaboration and visibility.