Terraform Plan Review: Catch Risky Changes Before Apply

A structured terraform plan review is the cheapest security control in an infrastructure-as-code pipeline: the plan command produces an execution plan that previews exactly which resources Terraform will create, update, or destroy before a single API call is made, and reading that output carefully at pull-request time stops most damaging mistakes from ever reaching production.
The method below works with any provider: read the action symbols first, scrutinize the attributes that historically cause incidents, review the exact saved plan file that automation will apply, and enforce the whole checklist as policy code so the review survives busy weeks.
Read the Action Symbols First
Symbols tell you the severity of a change before you read a single attribute. In plan output, Terraform reports create actions with a plus symbol, destroy actions with a minus symbol, in-place updates with a tilde, and full replacements with a destroy-and-create combination.
| Symbol | Action | What it means for review |
|---|---|---|
| + | Create | New resource; check defaults for public exposure and least privilege |
| – | Destroy | Something disappears; verify it is not a database, bucket or log store |
| ~ | Update in-place | Read the attribute diff; watch for opened ingress and loosened IAM |
| -/+ | Replace | Destructive recreate; confirm data survives or is intentionally discarded |
Triage rows in this order: replacements and destroys carry outage and data-loss potential, in-place updates carry configuration-drift risk, and creates are usually the safest but still deserve a defaults check. The summary line counting adds, changes and destroys is a useful first signal, but never approve on the summary alone.
Attributes That Signal Risk
Most incidents that slip through reviewed plans come from a short list of attributes. Give these priority:
- Network ingress: any new 0.0.0.0/0 CIDR on a security group or firewall rule, especially on administrative ports.
- IAM breadth: wildcard actions or resources, new admin-style policies, or roles assumable by principals outside the account.
- Public exposure: public IP assignment, internet-facing load balancers, or object storage switched to public read.
- Encryption and logging: encryption flags turned off, log retention shortened, audit trails deleted or disabled.
- Deletion protections: termination protection or backup policies removed from stateful resources.
Changes that touch how workloads store and access secrets deserve extra scrutiny — if the plan modifies how Kubernetes secrets are encrypted and authorized, treat the change as high risk regardless of size. The same applies to identity settings: a plan that changes authentication lifetimes deserves the rigor behind the session lifetime controls that contain token theft.
Save the Plan You Review
Review the exact artifact automation will execute. Saving the plan with the out option writes the generated plan to a file on disk, which automation can later execute by passing that file to apply as an extra argument, and HashiCorp documents this two-step workflow as the intended pattern for running Terraform in automation.
Without the out option, the command produces only a speculative plan: a description of effect with no intent to apply. Speculative plans are useful for developers verifying their work before opening a pull request, but other changes landing in the meantime can make the final effect differ, so always re-check the final non-speculative plan before approving an apply.
Automate the Review Gate
Human review decays under pressure, so encode the checklist. Policy engines such as Open Policy Agent turn the review into a gate: OPA policies operate on the JSON plan generated by Terraform, evaluating the same structured data a human reads and blocking runs that violate organization rules around tagging, exposure or IAM breadth.
The mechanics are simple: save the binary plan, convert it with the show command in JSON mode, and evaluate policies against the resource changes. Know the limits — some attributes are unknown at plan time because they are only computed during apply, dynamic blocks depend on runtime evaluation, and some function results are not yet available. Confirm the attribute your policy needs is actually present in the plan JSON before relying on the rule.
Pair the automated gate with a small human rule: any plan containing a replace or destroy action on a stateful resource, or any change to privileged IAM, requires a second reviewer regardless of policy results.
A Working Review Checklist
- Run the plan in CI against the target workspace and save it with the out option.
- Read the summary counts; flag anything with destroys or replacements for human eyes.
- Scan the attribute diff for the high-risk list: ingress, IAM, public exposure, encryption, logging, deletion protection.
- Check that removals of resources absent from the configuration are intentional, not accidental renames.
- Verify changes to sensitive resources (databases, buckets, secret stores) against state and backup policy.
- Evaluate the saved plan JSON against your OPA or equivalent policy bundle.
- Require a second reviewer for stateful destroys and privileged IAM changes.
- Apply the exact saved plan file, never a freshly generated one nobody reviewed.