Stop running Terraform from laptops. How I automated it.
In many teams, Terraform is still run from someone's laptop. They pull the latest code, run plan, look at it, run apply. It works, until it doesn't: someone applies an old branch, two people apply at once, the laptop has different provider versions, or the only person with production credentials is on holiday.
The fix is a simple, well-known workflow: plan on pull request, apply on merge. Here's how I set it up.
The workflow
- A developer changes infrastructure code on a branch and opens a pull request.
- CI runs
terraform planand posts the plan as a comment on the pull request. - Reviewers look at both the code and the plan.
- After approval, the pull request is merged.
- CI runs
terraform applywith the plan, from the main branch.
Everything is reviewed, everything is logged, nobody needs production credentials on their laptop, and the main branch always reflects what's deployed.
The plan is the real change. Review the plan as carefully as the code.
Option 1: Atlantis
Atlantis is an open-source server built exactly for this workflow. It listens to pull request events, and you control it with comments:
- Open a PR, and Atlantis automatically runs
planand comments the output. - Comment
atlantis applyand it applies, then comments the result. - It locks the affected directories so two PRs can't change the same state at the same time.
Atlantis runs in your own infrastructure (often on Kubernetes), so credentials never leave your environment. It's simple, battle-tested and free. You do have to host and maintain it.
Option 2: GitHub Actions (or any CI)
You can build the same workflow in your normal CI. A simplified GitHub Actions version:
name: terraform
on:
pull_request:
paths: ["infrastructure/**"]
push:
branches: [main]
paths: ["infrastructure/**"]
permissions:
contents: read
id-token: write
pull-requests: write
jobs:
terraform:
runs-on: ubuntu-latest
defaults:
run:
working-directory: infrastructure/live/production/network
steps:
- uses: actions/checkout@<sha> # v4
- uses: hashicorp/setup-terraform@<sha> # v3
- uses: aws-actions/configure-aws-credentials@<sha> # v4
with:
role-to-assume: arn:aws:iam::123456789012:role/terraform-${{ github.event_name == 'push' && 'apply' || 'plan' }}
aws-region: eu-central-1
- run: terraform init -input=false
- run: terraform plan -input=false -out=tfplan
- if: github.event_name == 'pull_request'
run: terraform show -no-color tfplan > plan.txt
# post plan.txt as a PR comment with your preferred action
- if: github.event_name == 'push'
run: terraform apply -input=false tfplan
Things to notice:
-
Two different IAM roles: a read-only role for plans on pull requests, and a role with write access only for applies from
main. The trust policy restricts which branch can assume which role. - OIDC, no stored cloud keys.
- Use GitHub environments with required reviewers for production applies if you want an extra approval step.
One subtlety: the plan on the pull request and the apply after merge can differ if main changed in between. Re-planning on merge (as above) and applying that plan is the safe way.
Option 3: TACOS
"TACOS" (Terraform Automation and Collaboration Software) is the nickname for platforms built for this: HCP Terraform, Spacelift, env0, Scalr, Harness IaCM and others. They add:
- Policy as code (OPA or Sentinel) on every plan.
- Drift detection and scheduled runs.
- Stack dependencies, cost estimation, approvals, audit logs.
- Self-service and RBAC for many teams.
They cost money, but save a lot of building and maintaining for larger organisations.
Approvals
Who can approve infrastructure changes? Some practical rules:
- CODEOWNERS so the right team reviews changes to their part of the infrastructure.
- Required reviews in branch protection.
- Extra approval for production applies, through CI environments or the platform.
- Plans visible to reviewers, ideally summarised: number of resources added, changed and destroyed. A "destroy" in production should stand out.
Secrets and OIDC authentication
Never store long-lived cloud keys in CI for Terraform. Use OIDC with every major cloud, and scope the roles tightly. Combine that with remote, encrypted, locked state, and the most sensitive parts of your setup never touch a developer's laptop.
So, how do I start?
Pick one Terraform stack, ideally a low-risk one. Set up plan-on-PR with a read-only role first, so the team gets used to reviewing plans. Then add apply-on-merge with a separate role. Once it works, roll it out to the other stacks and take away production write access from laptops.
My take: infrastructure changes deserve the same pipeline discipline as application code. Review the plan, merge, and let the robot apply.
— Desly
Originally published on deslypeter.com.
Top comments (0)