Terraform Plan Review
A diagnostic playbook for Terraform (azurerm or otherwise, TF >= 1.5). Goal: turn a
plan/apply failure or a routine plan into a short, actionable summary —
not a wall of raw output.
1. Locate the root and run the basics
- Find the Terraform root (look for
*.tf+backendconfig — check a project doc likeCLAUDE.md/READMEfirst for the conventional path, e.g.infrastructure/terraform/). - Run, in order, from that directory:
terraform fmt -recursive -check terraform validate terraform plan -var-file=terraform.tfvars -out=plan.out - If
fmt -checkreports files, runterraform fmt -recursive(no-check) to fix them and note which files changed.
2. Summarize the plan
Don't paste the raw plan. Extract and report:
- Counts:
N to add, N to change, N to destroy. - Any destroy or replace (
-/+) — call these out explicitly with the resource address and the attribute that's forcing replacement (# forces replacementlines). These are the highest-risk changes and deserve a sentence on why before applying. - Resources changing for reasons unrelated to the user's actual edit (signs of drift).
3. Triage failures into categories
When validate/plan/apply errors, classify each error before proposing a fix:
a) Permissions gap (not a code bug)
- Symptom:
AuthorizationFailed/ 403 onazurerm_role_assignmentor similar identity/RBAC resources. - Meaning: the identity running Terraform lacks a sufficiently privileged role
(commonly
User Access AdministratororOwner) on the target scope to create role assignments. The Terraform code is usually correct. - Action: report which assignment(s) are blocked, the scope, and the role needed.
Recommend the user/admin grant that role to the executing identity — do not
"fix" this by removing the role assignment resource or adding
skip_service_principal_aad_checkunless the project explicitly calls for it.
b) State drift
- Symptom: plan wants to create a resource that already exists in Azure (or destroy one that's already gone), or shows unexpected changes to attributes nobody edited.
- Action: prefer
import {}blocks (native expressions, TF >= 1.7) over theterraform importCLI:
Place these near the resource definition or in a dedicatedimport { to = azurerm_resource_group.example id = "/subscriptions/.../resourceGroups/example" }imports.tf, runterraform planagain to confirm the diff disappears, and remove the import block once applied (or leave it — it's a no-op after import).
c) Real configuration bug
- Symptom: validate error (bad reference, type mismatch, missing required arg), or a plan diff that doesn't match either category above.
- Action: fix the
.tfsource directly. Re-runvalidate/planto confirm.
4. Before wrapping up
- Re-run
terraform planafter any fix and confirm the diff is now either empty or matches the user's intended change exactly. - If a
plan.out/plan.cacheartifact is consumed by a separateapplyCI stage, don't assume your local plan is what apply will use — check whether apply re-plans or downloads a cached artifact. - Never run
terraform applywithout explicit user confirmation — plans are reversible to review, applies often aren't.