← Recipes

阅读中文版本

Review Terraform Plans Before Anyone Applies

Publish the plan as a reviewable artifact, require approval, and apply exactly that plan so the approved diff is what reaches the cloud.

~30 minterraformiacreview

Task

Make infrastructure changes reviewable and prevent an apply from containing changes nobody saw in the pull request.

When to use this

A variable default changes and the plan destroys and recreates a database. The apply runs automatically at merge time, and the replacement starts immediately.

Prerequisites

  • Terraform 1.7 or newer
  • Remote state with locking enabled
  • A CI workflow with a manual approval step

Steps

1. Create a saved plan and a readable rendering for reviewers.

terraform plan -input=false -lock-timeout=5m -out=tfplan && terraform show -no-color tfplan > plan.txt

Expected output: plan.txt is attached to the run and lists every add, change, and destroy with totals.

2. Extract destroy actions so stateful replacements are called out in review.

terraform show -json tfplan | jq -r '.resource_changes[] | select(.change.actions | index("delete")) | .address'

Expected output: Every address that would be deleted or replaced is printed for explicit approval.

3. Apply only the reviewed plan after approval; never re-plan at apply time.

terraform apply -input=false tfplan

Expected output: The apply uses the reviewed plan, and a changed state makes Terraform reject the stale plan.

4. Record the plan hash with the deployment record.

sha256sum tfplan

Expected output: The recorded hash matches the approved artifact and the apply log entry.

Troubleshooting

Problem

Terraform refuses the saved plan as stale

Solution

State changed after planning. Re-plan in the same workspace, re-review, and never force an outdated plan.

Problem

Plan output is truncated in the pull request comment

Solution

Attach plan.txt as a build artifact and link it; log truncation hides the destroy section first.

Limitations

  • A plan cannot predict provider-side drift between planning and applying.
  • Saved plans contain sensitive values; store them with restricted access and short retention.

The plan is the review artifact

A Terraform pull request shows the code change, not the effect. The effect is a plan, and the plan is what must be reviewed and approved. Two pipelines typically break this: one that plans in the pull request but re-plans at apply time, and one that applies with -auto-approve at merge. The first can apply something different from what was reviewed; the second applies without review at all. Plan once, store the plan file, approve the artifact, and apply that exact file.

Make destructive changes loud

Most plans are boring, which is why reviewers skim them. Extract the actions that delete or replace resources and print them separately, so a database replacement cannot hide among forty tag updates. A simple jq filter over resource_changes is enough to produce a short list of addresses that deserve a conversation. If a replacement is intended, say so in the pull request and include the migration or backup step.

terraform plan -input=false -lock-timeout=5m -out=tfplan
terraform show -json tfplan \
  | jq -r '.resource_changes[] | select(.change.actions | index("delete")) | .address'

Apply the artifact, keep the evidence

The apply step must consume tfplan and must not run terraform plan again, so a state change between approval and apply produces a stale-plan error rather than an unexpected change. Keep the plan file’s hash in the deployment record; it links the reviewed artifact to the apply that ran. Plans contain sensitive values, so restrict artifact access and keep retention short. Drift between plan and apply is still possible at the provider level, which is why a post-apply refresh belongs in the same pipeline.