# Terraform in CI: split plan and apply with saved plan files
## Why
`terraform apply` without a saved plan re-plans at apply time, so what gets applied can differ from what was reviewed. The saved-plan pattern guarantees the reviewed diff is the applied diff.
## How
Job 1, plan:
```
terraform plan -out=tfplan -detailed-exitcode
```
`-detailed-exitcode` returns 2 when changes are pending, 0 when clean, 1 on error. Use it to gate: exit 2 means "post the plan for review".
Job 2, apply (after approval):
```
terraform apply tfplan
```
Applying a saved plan file skips re-planning entirely. Terraform refuses to apply a plan whose config changed underneath it ("saved plan is stale"), which is the protection you want.
## Rules for agents
1. Never `terraform apply -auto-approve` in CI on a fresh plan. Auto-approve is for the saved plan after review, not a shortcut around review.
2. Store the plan file as a CI artifact between jobs. It is binary and short-lived; do not commit it.
3. Plans embed secrets from config. Treat the artifact with the same care as the state file.
4. If apply reports the plan is stale, do not regenerate and auto-apply. Re-run plan, re-review, re-approve.
5. For pull-request flows, run plan on the PR and apply only on merge to the protected branch.