Analyzing Terraform Plan Blast Radius With AI Before You Apply
A plan that destroys and recreates a database reads almost the same as one that tweaks a tag. AI can surface the blast radius hiding in your plan JSON.
- #terraform
- #ai
- #plan
- #review
- #safety
The scariest plan I ever applied looked completely routine. Forty-odd changes, mostly tag updates, scrolling past in green and yellow in a CI log. Buried at line 612 was a -/+ on an RDS instance — a destroy-then-create on the production database, triggered by a provider default that quietly changed. I caught it. The next person might not. The problem isn’t that Terraform hides this; it’s that a 900-line plan flattens a catastrophic change and a cosmetic one into the same visual texture.
This is the kind of needle-in-a-haystack reading that AI is genuinely good at — if you feed it structured data instead of terminal output. Treat the model as a fast junior reviewer doing a first pass over the plan: it flags the scary parts, ranks them, and explains them. It does not apply anything. It never gets state-write access or cloud credentials. A human reads its summary and makes the call.
Get the plan as JSON, not as scrollback
The colored terminal plan is for humans skimming. For machine analysis, export the structured form:
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > plan.json
That JSON has a resource_changes array where every entry carries an actions list — ["create"], ["update"], ["delete"], or the dangerous ["delete", "create"] (replacement). The signal you care about is right there, structured and unambiguous, instead of inferred from -/+ symbols.
Filter to the changes that can hurt you
Before involving any model, narrow the input with jq so you’re not paying to analyze no-op tags:
jq '[.resource_changes[]
| select(.change.actions != ["no-op"])
| {address, actions: .change.actions,
replace: (.change.actions == ["delete","create"])}]' \
plan.json > changes.json
Now changes.json is a tight list of every real change with replacements explicitly marked. This is what you hand the AI. Pre-filtering keeps the model focused and your token bill small.
Pro Tip: A replacement on a stateful resource — database, volume, queue, anything holding data — is a different risk class than replacing a stateless one. Ask the model to sort replacements by whether the resource type stores data, and check that ranking by hand.
Ask the model to rank, not to approve
The prompt that works frames the AI as a triage step, not a gatekeeper:
“Here is the filtered change set from a Terraform plan. Group the changes by risk. Highest risk: any replacement of a stateful resource (databases, volumes, snapshots), anything deleted, and anything affecting networking or IAM. Lowest risk: tags, descriptions, in-place attribute updates. For each high-risk item, name the resource, the action, and the likely reason for replacement based on the
before/aftervalues. Do not recommend applying or not applying — just rank and explain.”
Forbidding the apply/don’t-apply verdict is intentional. The model doesn’t know your maintenance window, your data’s value, or whether that database is a throwaway. It surfaces; you decide.
Make it explain why something replaces
The most useful thing AI adds is translating the before/after diff into a root cause. Give it one resource’s full change object:
jq '.resource_changes[]
| select(.address == "aws_db_instance.main")
| .change' plan.json
A model reading that block will typically spot that, say, availability_zone moved from null to a concrete value, or engine_version crossed a major boundary, and explain that this specific attribute forces replacement. That’s the difference between “the DB is being recreated, scary” and “the DB is being recreated because the provider upgrade changed the default engine version — pin it and the replacement disappears.” The second one is actionable.
Wire it in without giving it the keys
The clean architecture is a read-only side channel. CI generates plan.json, a separate step posts the filtered change set to an analysis service, and the model’s ranked summary lands as a PR comment for humans:
# .gitlab-ci.yml (excerpt)
plan:
script:
- terraform plan -out=tfplan.binary
- terraform show -json tfplan.binary > plan.json
- jq -f filter.jq plan.json > changes.json
artifacts:
paths: [changes.json]
# A downstream, credential-free job reads changes.json and
# posts the AI risk summary. It can apply nothing.
The job that talks to the model has no Terraform backend config and no cloud credentials. It reads JSON and writes a comment. That boundary is the whole safety story: even a fully compromised analysis step can’t touch infrastructure.
This pairs naturally with whatever you already use for reviewing diffs — the code review dashboard is where I keep the human sign-off step, so the AI summary and the approval live in one place.
Account for the changes the plan can’t show
A plan’s blast radius isn’t only the resources in resource_changes — it’s also the things downstream of them that Terraform won’t flag. Replacing a security group can break every instance that references it, even though those instances show as no-ops. Ask the model to reason about second-order effects from the change set plus a little context:
“For each replacement in this change set, list what else might be affected that doesn’t appear as a change: dependent resources, anything referencing this resource’s ID or ARN, and any data sources that read it. Base this only on the addresses present; flag where you’d need the full config to be sure.”
The model won’t catch everything — it’s working from a slice — but it reliably surfaces the “this replacement looks isolated but it’s referenced by three other modules” class of risk that a flat plan read misses entirely. You then confirm against the real dependency graph:
terraform graph -type=plan | grep 'aws_security_group.shared'
The AI points you at where to look; terraform graph gives you the authoritative answer. That pairing — model for the hypothesis, deterministic tooling for the proof — is the whole ethos of using AI on plans.
Tune the risk model to your environment
The default risk ranking (“stateful replacements are scary”) is a fine start, but your environment has its own danger zones. Maybe your highest-risk change is anything touching the shared transit gateway, or a specific production database module. Encode that in the prompt rather than relying on generic judgment:
“In addition to the standard risk rules, treat any change under
module.shared_networkor toaws_db_instance.billingas CRITICAL regardless of the action type.”
Versioning these environment-specific rules alongside the generic prompt means the bot’s notion of “blast radius” matches your actual operational reality — and it improves over time as you learn which changes hurt. The model executes the policy; you write the policy.
A few hard rules
- The model ranks and explains. It never approves, and the pipeline is built so it can’t apply.
- Stateful replacements get a human’s full attention every time, regardless of how the AI scored them. The model is a smoke detector, not a fire marshal.
- Keep the prompts versioned. I store mine in the prompt library; the Terraform prompt pack has a blast-radius prompt tuned to the
plan.jsonschema above.
Conclusion
Terraform already tells you everything in the plan — it just tells you all of it at once, flat. Exporting JSON, filtering to real changes, and letting an AI rank the blast radius turns a wall of green-and-yellow into a short list of “look here first.” The model does the tedious reading; you keep the judgment and the credentials. Catch the line-612 database replacement before you apply, not in the postmortem. There’s more on plan review in the Terraform category.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.