Applying from laptops doesn't scale; automation makes every change reviewed, logged and repeatable.
A typical pull-request workflow:
1. On every PR: terraform fmt -check, terraform init, terraform validate, linters (TFLint), security scanners (Trivy, Checkov), then terraform plan -out=tfplan, posting a plan summary to the PR. 2. Reviewers read the plan, not only the code. 3. On merge (or approval): terraform apply tfplan with the reviewed plan, or a fresh plan plus a manual approval gate.
Practical details:
- Set
TF_IN_AUTOMATION=1(less chatty output) and-input=falseso nothing waits for a prompt. - Credentials: use OIDC federation from the CI system (GitHub Actions, GitLab) to assume a cloud role, not long-lived access keys. Give plan jobs read-only roles and apply jobs the write role.
- Serialize applies per state (CI concurrency groups) in addition to state locking.
- Cache provider plugins (
TF_PLUGIN_CACHE_DIR) to speed up init. - Policies as code (OPA/conftest on
terraform show -json tfplan, Sentinel in HCP Terraform) can block forbidden changes such as public buckets. - Run a scheduled
plan -detailed-exitcodeto detect drift: exit code 2 means changes are pending.
Tools like HCP Terraform, Atlantis, Spacelift and env0 implement this workflow for you, including locking and PR comments.
Example
# .github/workflows/terraform.yml (core steps)
name: terraform
on: [pull_request]
permissions: {id-token: write, contents: read, pull-requests: write}
jobs:
plan:
runs-on: ubuntu-latest
env: {TF_IN_AUTOMATION: "1"}
defaults: {run: {working-directory: envs/prod}}
steps:
- uses: actions/checkout@v5
- uses: hashicorp/setup-terraform@v3
- uses: aws-actions/configure-aws-credentials@v4
with: {role-to-assume: "arn:aws:iam::123456789012:role/tf-plan", aws-region: eu-west-1}
- run: terraform fmt -check -recursive
- run: terraform init -input=false
- run: terraform validate
- run: terraform plan -input=false -out=tfplan
- run: terraform show -no-color tfplan > plan.txt # post as a PR commentRemember
- Plan on PRs, review the plan, apply the saved plan on merge
- OIDC short-lived credentials; read-only for plan, write for apply
- -detailed-exitcode on a schedule detects drift
Watch out for
- Storing long-lived admin cloud keys as CI secrets for Terraform.