Review checklist
This is the checklist to run down — by eye, on any diff, yours or an AI’s — after the tools in Verification Tools have run and come back clean. It fits on one printed page on purpose: a checklist nobody actually opens during a review is worthless. Every line links the entry that explains why, so this page stays a list, not a re-run of the course. The groups follow the same order as How to read Terraform you did not write, plus a final Tooling group covering how each of the other seven actually gets verified.
flowchart LR A["Versions & providers"] --> T["validate/plan catch some<br/>(deprecation warnings, module+count errors)"] B["State & backend"] --> P["Partial: remote_state hard-fails loudly,<br/>backend-locking drift is silent"] V["Variables"] --> M["Mostly human review<br/>(nullable/ephemeral behave silently)"] R["Resources & lifecycle"] --> P2["Partial: for_each-on-unknown caught,<br/>prevent_destroy-on-wrong-resource is not"] Mo["Modules"] --> P Tg["Terragrunt live tree"] --> M C["CI & policy"] --> P2
Versions & providers
Section titled “Versions & providers”- Is every
required_providersconstraint~>, not>=? A floating constraint resolves to whatever the registry’s current newest release is on the dayinitruns. See the AI Bug Catalog, entry 1. - Is
.terraform.lock.hclcommitted — not gitignored alongside.terraform/? See entry 4. - Does
terraform validate -jsoncome back with zero warnings, not just zero errors? A deprecated argument (acl,managed_policy_arns) still validates and plans successfully. See entry 2. - Does any module declare its own
providerblock? It validates fine alone and only fails once a caller needscount/for_each/depends_on. See entry 3.
State & backend
Section titled “State & backend”- Does the backend’s locking mechanism match what this Terraform/backend version actually supports —
use_lockfile(S3, stable since 1.11) rather than the now-deprecateddynamodb_table? See the AI Bug Catalog, entry 5, and The State File. - Does every
terraform_remote_statedata source point at akey/paththat has actually been applied? There is no mock fallback — a missing state file hard-fails the whole plan. See entry 6.
Variables
Section titled “Variables”- Does a
nullable = falsevariable’s default ever silently stand in for an explicitnullpassed from a caller, with nothing inplan/applyoutput calling out the substitution? See the AI Bug Catalog, entry 7. - Does any
ephemeralvariable carry a literal secretdefault? It still leaks into a saved plan’sshow -jsonoutput even though it never reaches state. See entry 8.
Resources & lifecycle
Section titled “Resources & lifecycle”- Is
countused over any list that could be reordered, wherefor_eachis the only safe choice? Removing a middle element shifts every later index. See the AI Bug Catalog, entry 10. - Does
lifecycle { prevent_destroy = true }sit on the resource that actually matters — checked directly, not assumed from a nearby resource’s error message? See entry 9, proven with a realplan -destroythat destroyed the unprotected database with zero resistance. - Does any
for_eachderive its keys from another managed resource’s computed attribute? A real, current “Invalid for_each argument” rejection — see entry 11. - Is
null_resourceused whereterraform_data(1.4+) removes an entire extra provider dependency for equivalent behavior? See entry 12. - Does any argument look copied from a similar-but-different resource type (
force_destroyongoogle_compute_instance, real ongoogle_storage_bucketbut not here)? A real “Unsupported argument” rejection — see entry 13.
Modules
Section titled “Modules”- Does every non-registry
sourcepin a revision —?ref=<tag-or-sha>for agit::source, an explicitversionfor a registry module? Without it, Terraform tracks the default branch’sHEAD, silently. See the AI Bug Catalog, entry 14. - Does any
depends_onon a module call exist to “fix” an ordering complaint that’s actually a genuine reference cycle? Nodepends_oncan suppress a realError: Cycle. See entry 15.
Terragrunt live tree
Section titled “Terragrunt live tree”- Does anything reference
run-all/plan-all? Both are unknown commands as of the Terragrunt 1.1 CLI redesign — a real, immediate rejection. See the AI Bug Catalog, entry 16. - Do two environments’
terragrunt.hclfiles ever carry the identicalaccount_id/region/subscription/project value ininputs?terragrunt hcl validatecannot catch a syntactically valid copy-paste. See entry 17, proven with a real byte-identicaldev/proddiff.
CI & policy
Section titled “CI & policy”- Does
-auto-approveever run against a fresh plan, rather than a saved, reviewedplan -outfile? See the AI Bug Catalog, entry 18, proven with a real side-by-side apply. - Is every third-party GitHub Action pinned to a full commit SHA, not a mutable tag like
@master/@main? See entry 19. - Does any Rego policy use pre-v1
deny[msg] { ... }syntax? It fails to even load under current OPA/conftest — see entry 20, proven with a realrego_parse_error. - Does every
.tflint.hclrule reference something this small, IAM-focusedtflint-ruleset-google(six rules total) actually offers — rather than assuming a resource-shape check other clouds’ larger rulesets have? See entry 21, proven with a real, immediate “Rule not found” rejection here. - Is a real secret ever committed in a
.tfvarsfile, or left interraform.tfstateas plaintext despite an output markedsensitive? See entries 22–23.
Tooling
Section titled “Tooling”- Does
terraform validate -json | jq '.diagnostics[]'get checked for warnings, not just a nonzero exit code? See Verification Tools. - Does CI run a real
terraform plan -detailed-exitcodeand inspect the saved plan’sshow -jsonoutput with a real policy tool (conftest, checkov), not just eyeball the plan text? See Verification Tools. - Does
terragrunt hcl validate/renderrun in CI beforerun --all plan, catching syntax errors before a 10-unit tree partially applies? See Verification Tools.