GitHub Action
Terraform PR Commenter
Adds opinionated comments to PR's based on Terraform fmt
, init
and plan
outputs.
This Docker-based GitHub Action is designed to work in tandem with hashicorp/setup-terraform with the wrapper enabled, taking the output from a fmt
, init
or plan
, formatting it and adding it to a pull request. Any previous comments from this Action are removed to keep the PR timeline clean.
Support (for now) is limited to Linux as Docker-based GitHub Actions can only be used on Linux runners.
This action can only be run after a Terraform fmt
, init
, or plan
has completed, and the output has been captured. Terraform rarely writes to stdout
and stderr
in the same action, so we concatenate the commenter_input
:
- uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: fmt/init/plan # Choose one
commenter_input: ${{ format('{0}{1}', steps.step_id.outputs.stdout, steps.step_id.outputs.stderr) }}
commenter_exitcode: ${{ steps.step_id.outputs.exitcode }}
Name | Requirement | Description |
---|---|---|
commenter_type |
required | The type of comment. Options: [fmt , init , plan ] |
commenter_input |
required | The comment to post from a previous step output. |
commenter_exitcode |
required | The exit code from a previous step output. |
Name | Requirement | Description |
---|---|---|
GITHUB_TOKEN |
required | Used to execute API calls. The ${{ secrets.GITHUB_TOKEN }} already has permissions, but if you're using your own token, ensure it has the repo scope. |
TF_WORKSPACE |
optional | Default: default . This is used to separate multiple comments on a pull request in a matrix run. |
EXPAND_SUMMARY_DETAILS |
optional | Default: true . This controls whether the comment output is collapsed or not. |
Both of these environment variables can be set at job
or step
level. For example, you could collapse all outputs but expand on a plan
:
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
env:
EXPAND_SUMMARY_DETAILS: 'false' # All steps will have this environment variable
steps:
- name: Checkout
uses: actions/checkout@v2
...
- name: Post Plan
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EXPAND_SUMMARY_DETAILS: 'true' # Override global environment variable; expand details just for this step
with:
commenter_type: plan
commenter_input: ${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
commenter_exitcode: ${{ steps.plan.outputs.exitcode }}
...
Single workspace build, full example:
name: 'Terraform'
on:
pull_request:
push:
branches:
- master
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
env:
TF_IN_AUTOMATION: true
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
terraform_version: 0.12.28
- name: Terraform Format
id: fmt
run: terraform fmt -check -recursive
continue-on-error: true
- name: Post Format
if: always() && github.ref != 'refs/heads/master' && (steps.fmt.outcome == 'success' || steps.fmt.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: fmt
commenter_input: ${{ format('{0}{1}', steps.fmt.outputs.stdout, steps.fmt.outputs.stderr) }}
commenter_exitcode: ${{ steps.fmt.outputs.exitcode }}
- name: Terraform Init
id: init
run: terraform init
- name: Post Init
if: always() && github.ref != 'refs/heads/master' && (steps.init.outcome == 'success' || steps.init.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: init
commenter_input: ${{ format('{0}{1}', steps.init.outputs.stdout, steps.init.outputs.stderr) }}
commenter_exitcode: ${{ steps.init.outputs.exitcode }}
- name: Terraform Plan
id: plan
run: terraform plan -out workspace.plan
- name: Post Plan
if: always() && github.ref != 'refs/heads/master' && (steps.plan.outcome == 'success' || steps.plan.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: plan
commenter_input: ${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
commenter_exitcode: ${{ steps.plan.outputs.exitcode }}
- name: Terraform Apply
id: apply
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
run: terraform apply workspace.plan
Multi-workspace matrix/parallel build:
...
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
strategy:
matrix:
workspace: [audit, staging]
env:
TF_WORKSPACE: ${{ matrix['workspace'] }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
env:
TF_IN_AUTOMATION: true
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
terraform_version: 0.12.28
- name: Terraform Init - ${{ matrix['workspace'] }}
id: init
run: terraform init
- name: Post Init - ${{ matrix['workspace'] }}
if: always() && github.ref != 'refs/heads/master' && (steps.init.outcome == 'success' || steps.init.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: init
commenter_input: ${{ format('{0}{1}', steps.init.outputs.stdout, steps.init.outputs.stderr) }}
commenter_exitcode: ${{ steps.init.outputs.exitcode }}
- name: Terraform Plan - ${{ matrix['workspace'] }}
id: plan
run: terraform plan -out ${{ matrix['workspace'] }}.plan
- name: Post Plan - ${{ matrix['workspace'] }}
if: always() && github.ref != 'refs/heads/master' && (steps.plan.outcome == 'success' || steps.plan.outcome == 'failure')
uses: robburger/terraform-pr-commenter@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commenter_type: plan
commenter_input: ${{ format('{0}{1}', steps.plan.outputs.stdout, steps.plan.outputs.stderr) }}
commenter_exitcode: ${{ steps.plan.outputs.exitcode }}
...
"What's the crazy-looking if:
doing there?" Good question! It's broken into 3 logic groups separated by &&
, so all need to return true
for the step to run:
always()
- ensures that the step is run regardless of the outcome in any previous stepsgithub.ref != 'refs/heads/master'
- prevents the step running on amaster
branch(steps.step_id.outcome == 'success' || steps.step_id.outcome == 'failure')
- limits the run to the specificstep_id
only when there's asuccess
orfailed
outcome.
In English: "Always run this step, but only on a pull request and only when the previous step succeeds or fails."
Feel free to head over to the Issues tab to see if the issue you're having has already been reported. If not, open a new one and be sure to include as much relevant information as possible, including code-samples, and a description of what you expect to be happening.