This is a suite of terraform and OpenTofu related GitHub Actions that can be used together to build effective Infrastructure as Code workflows.
GitHub Actions are a way to make automated workflows that trigger when events occur on your GitHub repository, using a YAML file that lives in your repo. These actions can be used to easily perform Terraform or OpenTofu tasks as part of your workflow.
Currently, there is just experimental support for OpenTofu, see here
See the documentation for the available actions:
- dflook/terraform-plan
- dflook/terraform-apply
- dflook/terraform-output
- dflook/terraform-remote-state
- dflook/terraform-validate
- dflook/terraform-fmt-check
- dflook/terraform-fmt
- dflook/terraform-check
- dflook/terraform-new-workspace
- dflook/terraform-destroy-workspace
- dflook/terraform-destroy
- dflook/terraform-version
- dflook/terraform-unlock-state
- dflook/terraform-test
These terraform actions can be added as steps to your own workflow files.
GitHub reads workflow files from .github/workflows/
within your repository.
See the Workflow documentation for details on writing workflows.
Here are some examples of how the terraform actions can be used together in workflows.
Terraform plans typically need to be reviewed by a human before being applied. Fortunately, GitHub has a well established method for requiring human reviews of changes - a Pull Request.
We can use PRs to safely plan and apply infrastructure changes.
You can make GitHub enforce this using branch protection, see the dflook/terraform-apply action for details.
In this example we use two workflows:
This workflow runs on changes to a PR branch. It generates a terraform plan and attaches it to the PR as a comment.
name: Create terraform plan
on: [pull_request]
permissions:
contents: read
pull-requests: write
jobs:
plan:
runs-on: ubuntu-latest
name: Create a plan for an example terraform configuration
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: terraform plan
uses: dflook/terraform-plan@v1
with:
path: my-terraform-config
This workflow runs when the PR is merged into the main branch, and applies the planned changes.
name: Apply terraform plan
on:
push:
branches:
- main
permissions:
contents: read
pull-requests: write
jobs:
apply:
runs-on: ubuntu-latest
name: Apply terraform plan
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: terraform apply
uses: dflook/terraform-apply@v1
with:
path: my-terraform-config
This workflow runs on every push to non-main branches and checks the terraform configuration is valid. For extra strictness, we check the files are in the canonical format.
This can be used to check for correctness before merging.
name: Lint
on:
push:
branches-ignore:
- main
jobs:
validate:
runs-on: ubuntu-latest
name: Validate terraform configuration
steps:
- name: Checkout
uses: actions/checkout@v4
- name: terraform validate
uses: dflook/terraform-validate@v1
with:
path: my-terraform-config
fmt-check:
runs-on: ubuntu-latest
name: Check formatting of terraform files
steps:
- name: Checkout
uses: actions/checkout@v4
- name: terraform fmt
uses: dflook/terraform-fmt-check@v1
with:
path: my-terraform-config
This workflow runs every morning and checks that the state of your infrastructure matches the configuration.
This can be used to detect manual or misapplied changes before they become a problem. If there are any unexpected changes, the workflow will fail.
name: Check for infrastructure drift
on:
schedule:
- cron: "0 8 * * *"
jobs:
check_drift:
runs-on: ubuntu-latest
name: Check for drift of example terraform configuration
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check for drift
uses: dflook/terraform-check@v1
with:
path: my-terraform-config
There may be times when you expect terraform to plan updates without any changes to your terraform configuration files. Your configuration could be consuming secrets from elsewhere, or renewing certificates every few months.
This example workflow runs every morning and applies any outstanding changes to those specific resources.
name: Rotate TLS certificates
on:
schedule:
- cron: "0 8 * * *"
jobs:
rotate_certs:
runs-on: ubuntu-latest
name: Rotate TLS certificates in example terraform configuration
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Rotate certs
uses: dflook/terraform-apply@v1
with:
path: my-terraform-config
auto_approve: true
target: |
acme_certificate.certificate
kubernetes_secret.certificate
Perhaps you don't want to spend engineer time making formatting changes. This workflow will automatically create or update a PR that fixes any terraform formatting issues.
name: Check terraform file formatting
on:
push:
branches:
- main
jobs:
format:
runs-on: ubuntu-latest
name: Check terraform file are formatted correctly
steps:
- name: Checkout
uses: actions/checkout@v4
- name: terraform fmt
uses: dflook/terraform-fmt@v1
with:
path: my-terraform-config
- name: Create Pull Request
uses: peter-evans/create-pull-request@v2
with:
commit-message: terraform fmt
title: Reformat terraform files
body: Update terraform files to canonical format using `terraform fmt`
branch: automated-terraform-fmt
Testing of software changes often requires some supporting infrastructure, like databases, DNS records, compute environments etc. We can use these actions to create dedicated resources for each PR which is used to run tests.
There are two workflows:
This workflow runs with every change to a PR.
It deploys the testing infrastructure using a terraform workspace dedicated to this branch, then runs integration tests against the new infrastructure.
name: Run integration tests
on: [pull_request]
jobs:
run_tests:
runs-on: ubuntu-latest
name: Run integration tests
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Use branch workspace
uses: dflook/terraform-new-workspace@v1
with:
path: my-terraform-config
workspace: ${{ github.head_ref }}
- name: Deploy test infrastrucutre
uses: dflook/terraform-apply@v1
id: test-infra
with:
path: my-terraform-config
workspace: ${{ github.head_ref }}
auto_approve: true
- name: Run tests
run: |
./run-tests.sh --endpoint "${{ steps.test-infra.outputs.url }}"
This workflow runs when a PR is closed and destroys any testing infrastructure that is no longer needed.
name: Destroy testing workspace
on:
pull_request:
types: [closed]
jobs:
cleanup_tests:
runs-on: ubuntu-latest
name: Cleanup after integration tests
steps:
- name: Checkout
uses: actions/checkout@v4
- name: terraform destroy
uses: dflook/terraform-destroy-workspace@v1
with:
path: my-terraform-config
workspace: ${{ github.head_ref }}
If you use CircleCI, check out OVO Energy's ovotech/terraform
CircleCI orb.
If you use Jenkins, you have my sympathy.