terraform-review
Skill0 starsChanged 2 months agoApache-2.0View on GitHub
Review a Terraform or OpenTofu plan and its code before apply - find resource replacements, data loss, drift, hardcoded secrets, unpinned providers, missing lifecycle guards, and blast radius that the diff does not make obvious. Use whenever reviewing a terraform plan output, an infrastructure pull request, or a module before it is used in production.
7 other projects use a version of this.
Adopted by
Most recently taken up by
- abevz/devops-skillstheir own version, first seen Oct 15, 2026
- skwashd/skillstheir own version, first seen Oct 15, 2026
- gvago/unofficial-qodo-review-skillstheir own version, first seen Sep 24, 2026
- NotHarshhaa/devops-skillstheir own version, first seen Sep 19, 2026
- gmanch94/claude-code-aidlc-templatetheir own version, first seen Jul 11, 2026
- Jinbae10/skillstheir own version, first seen Jul 7, 2026
Used with
Projects with it also use observability.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
OpenAISkillobservability
Vercel Observability expert guidance — Drains (logs, traces, speed insights, web analytics), Web Analytics, Speed Insights, runtime logs, custom events, OpenTelemetry integration, and monitoring dashboards. Use when instrumenting, debugging, or optimizing application performance and user experience on Vercel.
Also in 3 projects that use this
Other versions7 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 7
- gmegidish/skills★ 13same name
- NotHarshhaa/devops-skills★ 8same name
- gmanch94/claude-code-aidlc-template★ 2same name
- gvago/unofficial-qodo-review-skills★ 2same name
- skwashd/skills★ 2same name
- abevz/devops-skills★ 0same name
- Jinbae10/skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository26 other files it gives its agents.
- AGENTS.mdAGENTS.md
- aws-production-debuggingSkill
- github-actions-debuggingSkill
- postgresql-troubleshootingSkill
- container-image-reviewSkill
- docker-debuggingSkill
- incident-triageSkill
- postmortem-analysisSkill
- production-debuggingSkill
- kubernetes-networkingSkill
- kubernetes-production-readinessSkill
- kubernetes-resource-debuggingSkill
- kubernetes-troubleshootingSkill
- linux-network-debuggingSkill
- linux-performanceSkill
- linux-troubleshootingSkill
- dns-troubleshootingSkill
- http-debuggingSkill
- tls-debuggingSkill
- observability-debuggingSkill
- prometheus-troubleshootingSkill
- slo-designSkill
- secrets-management-reviewSkill
- technology-jobSkill
- terraform-safe-changesSkill
- terraform-troubleshootingSkill
The file
We split this skill into the rules it gives an agent
4 rules. 0 are common, 1 is distinctive, and 3 are very specific to this project.
0
Common
Most of the 7 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 25
Read a plan for what it will destroy, not for what it will create.
- Very specific: line 73
Always plan to a file. A plan you review and an apply you run from a re-planned state are two different changes.
- Very specific: line 245
Run this after the execution completes.
- Very specific: line 263
Bad: Use -target to avoid an unwanted change.
“Common” is measured: kept by at least 6 in 10 of the 7 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Terraform Review
Read a plan for what it will destroy, not for what it will create.
Purpose
Use this when:
- Reviewing a
terraform planbefore it is applied. - Reviewing an infrastructure pull request.
- Assessing a module before adopting it.
- Someone asks "is this change safe?"
Do not use this when:
- You are executing the change — use
terraform-safe-changes, which covers the apply procedure. - The plan or apply is erroring — use
terraform-troubleshooting.
What this skill assumes: access to the plan output, and ideally the machine-readable plan
(terraform show -json). Reviewing the human-readable diff alone is error-prone on large plans;
the JSON form is what you should reason from.
Operating Procedure
- Read the summary line first.
Plan: 3 to add, 1 to change, 2 to destroy. The destroy count is the only number that can end your day badly. - Enumerate every destroy and every replace. A replace (
-/+) is a destroy followed by a create; for a database, a disk, or anything holding state, that is data loss. - For each replacement, find the forcing attribute. Terraform prints
# forces replacement. Decide whether that attribute had to change. - Assess blast radius. What depends on the replaced resource? A replaced security group means every attached instance loses its rules for the duration.
- Review the code, not only the diff: provider pinning, secrets, lifecycle blocks, count/ for_each keys, remote state.
- Check for drift. Anything the plan wants to change that nobody in the PR intended is drift — someone changed it in the console, and applying will revert it.
- Produce a verdict: safe, safe-with-conditions, or do-not-apply, with the specific reason.
Everything you state must be traceable to the plan or the code. If the plan does not show a value
((known after apply)), say so — do not assert what it will be.
Investigation
Step 1 - generate a reviewable plan
terraform plan -out=tfplan -lock-timeout=5m
terraform show -json tfplan > tfplan.jsonAlways plan to a file. A plan you review and an apply you run from a re-planned state are two
different changes; terraform apply tfplan guarantees you apply exactly what was reviewed.
-lock-timeout avoids the common failure where a concurrent run holds the state lock and the plan
fails after several minutes.
Step 2 - extract the destructive actions
jq -r '.resource_changes[]
| select(.change.actions | index("delete"))
| "\(.change.actions | join(",")) \(.address)"' tfplan.jsonThis is the highest-value command in the skill. It lists every resource that will be deleted, including replacements, which the human-readable output scatters across hundreds of lines.
jq -r '.resource_changes[]
| select(.change.actions == ["delete","create"] or .change.actions == ["create","delete"])
| .address' tfplan.jsonReplacements specifically. ["create","delete"] is create_before_destroy, which is safer;
["delete","create"] means there is a gap with nothing there.
Step 3 - find why each replacement is forced
terraform show tfplan | grep -B3 'forces replacement'Then ask, for each one, whether the change was intended. The common causes:
| Forcing attribute | Usually means | What to do |
name / identifier changed | A naming convention change, or a variable typo | Rename via moved block or import, not replacement |
availability_zone, subnet_id | A network redesign | Confirm the data can move |
engine_version on a major bump | Some providers replace rather than upgrade in place | Use the provider's in-place upgrade path |
user_data on an EC2 instance | Expected for immutable infrastructure | Fine, if the instance is stateless |
An attribute that is (known after apply) upstream | A dependency is being replaced and cascading | Fix the upstream, the cascade disappears |
A replacement forced by an attribute nobody edited is the signature of provider version drift: the provider now reads or normalises that attribute differently. Check whether the provider version changed in the lockfile.
Step 4 - detect drift
terraform plan -refresh-only-refresh-only shows what changed in the real world versus state, with no code changes applied. If
it reports differences, someone modified infrastructure outside Terraform. Decide deliberately:
adopt the change into code, or let the apply revert it. Reverting an emergency console fix during
an unrelated apply is a classic self-inflicted outage.
Step 5 - review the code
grep -rnE '(password|secret|token|api_key|private_key)\s*=\s*"' --include='*.tf' .
grep -rn 'required_providers' -A8 --include='*.tf' . | grep -E 'version'
test -f .terraform.lock.hcl && echo "lockfile present" || echo "NO LOCKFILE"Checklist:
| Item | Bad | Why |
| Provider versions | version = ">= 4.0" | An unbounded constraint means a provider release can change your plan with no code change. Use ~> 4.67. |
| Lockfile | Absent or gitignored | Without it, CI and laptops resolve different provider versions. |
| Secrets | Literal strings in .tf or .tfvars committed | They are in Git history and in state forever. |
| Backend | Local state | No locking, no sharing, no recovery. |
| State encryption | Bucket without encryption/versioning | State contains every secret in plaintext. |
lifecycle | No prevent_destroy on databases and state buckets | The one guard that stops an accidental destroy. |
count | count = length(var.list) on stateful resources | Removing a middle element re-indexes and replaces everything after it. Use for_each with stable keys. |
| Modules | Sourced from a branch (?ref=main) | Not reproducible. Pin to a tag or commit. |
| Hardcoded account IDs / regions | Literals scattered through resources | Blocks reuse and hides which environment is targeted. |
Step 6 - confirm the target
terraform workspace show
terraform state list | head
grep -rn 'backend' -A6 --include='*.tf' .Confirm which environment this plan targets before approving it. A plan that looks reasonable against staging and catastrophic against production is the same text.
Decision Tree
Reviewing a plan
├── destroy count == 0 and no replacements
│ └── Review code quality and drift, then approve
├── replacements present
│ ├── Resource holds data (database, disk, bucket, PVC)
│ │ └── DO NOT APPLY until a verified backup exists and downtime is agreed
│ ├── Resource is in the request path (LB, security group, DNS)
│ │ └── Check create_before_destroy; without it there is a hard outage window
│ └── Resource is stateless and replaceable
│ └── Acceptable; confirm the forcing attribute was intended
├── destroys present without a corresponding create
│ ├── Intended removal → confirm nothing else references it
│ └── Unintended → usually a for_each key change or a moved/renamed resource
└── plan shows changes nobody in the PR made
└── Drift. Resolve before applying, or the apply reverts someone's fix.Common Causes
| Finding | Signal | Action | Risk |
| Silent database replacement | -/+ aws_db_instance with identifier forcing | Rename via moved, never apply | do-not-apply |
count re-indexing | Many unrelated resources replaced after a list edit | Migrate to for_each | review |
| Provider upgrade drift | Replacements with no code change | Pin the provider; read its changelog | review |
| Secret in code | grep hit in .tf | Rotate the secret; move to a secret store | do-not-apply |
| Unpinned module ref | ?ref=main | Pin to a tag | review |
Missing prevent_destroy | No lifecycle block on stateful resources | Add before the next change | review |
| Console drift | -refresh-only shows changes | Adopt or revert deliberately | review |
Safety
Read-only
Everything in this skill. terraform plan, terraform show, terraform state list, jq and
grep do not modify infrastructure.
Two caveats worth stating explicitly, because "plan is read-only" is only mostly true:
terraform planrefreshes state by default, which writes to the state file and takes the state lock. On a shared backend this can block a colleague's apply. Use-lock-timeoutand, where you only want the diff,-refresh=false.terraform planreads every provider credential and can write the resulting values, including secrets, into the plan file.tfplanandtfplan.jsonare sensitive artifacts. Do not commit them or attach them to a public issue.
Potentially disruptive
This skill runs nothing that changes infrastructure. It does, however, tell you to execute the
reviewed plan with terraform apply tfplan — which is a mutating action, and is why this skill
carries the mutating risk tier rather than read-only.
The moment you move from review to execution, switch to terraform-safe-changes, which covers
backups, staged applies, guard rails and rollback.
Destructive
Reviewing is not destructive. The output of a review frequently is: if this review concludes a
resource must be destroyed, that execution belongs to terraform-safe-changes, with a verified
backup, an agreed window, and named approval. Never approve a plan containing a destroy of a
stateful resource on the basis of the diff alone.
Verification
A review is verified by what the execution reports back. This skill ends at the verdict; the
execution belongs to terraform-safe-changes, and these are the numbers to compare against.
Reviewed: plan - 3 to add, 1 to change, 0 to destroy
Executed: apply - 3 added, 1 changed, 0 destroyedAny difference between those two lines means the applied change was not the reviewed change — someone re-planned, a provider updated, or another apply landed in between. Treat it as a finding, not as rounding.
terraform plan -detailed-exitcodeRun this after the execution completes. -detailed-exitcode returns 0 for no changes, 2 for
changes pending, 1 for an error. A clean re-plan returning 0 proves the configuration converged. A
re-plan that still shows changes means the configuration is not idempotent — a resource is fighting
the provider or an external system, and it will produce a diff on every future run.
Then verify the resources themselves in the cloud provider, not only in Terraform. Terraform reports what it believes; the provider API reports what exists.
Anti-Patterns
Bad: Skim the plan, see "mostly adds", approve.
Better: Grep for destroy and forces replacement first. On a 2000-line plan the one
dangerous line is invisible to skimming, which is exactly how production databases get replaced.
Bad: Apply from a fresh plan after reviewing an older one.
Better: terraform plan -out=tfplan then terraform apply tfplan. Between the two, someone
else's merge or a provider update can change what gets applied.
Bad: Use -target to avoid an unwanted change.
Better: -target leaves state partially applied and hides the real problem. Fix the
configuration. -target is for recovering from a failed apply, not for shaping a normal one.
Bad: Ignore drift because the plan "will fix it". Better: Drift is usually somebody's emergency fix. Applying over it reverts the fix, often at the worst possible moment. Find out who changed it and why, first.
Bad: Approve because CI's plan was green. Better: CI proves the plan succeeded, not that its contents are acceptable. Green CI on a plan that destroys a database is still a green plan that destroys a database.
Composition
terraform-safe-changes— the execution procedure once the review passes.terraform-troubleshooting— state, lock and provider errors.secrets-management-review— when the review finds credentials in code or state.aws-production-debugging— when the plan touches live AWS resources and you need to assess real blast radius.
References
- Terraform:
terraform plan— flags, refresh behaviour and exit codes. - Terraform:
movedblocks — how to rename or restructure without replacement. - OpenTofu documentation — the commands in this skill apply to
OpenTofu with
tofusubstituted forterraform.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

