[!NOTE] Reference snapshot: this file is kept as a quality example for primitive authors. It is not installed from this repository as an active primitive.
IaC review
When to invoke
- "Review this Terraform module."
- "Why does our plan show drift?"
- "Is this Bicep ready for production?"
Review checklist
Structure
- Modules are composable and have a single responsibility (one module = one logical stack, not one resource).
- No hard-coded values: parameterize everything with sensible defaults.
- Documented inputs (
description,type, andvalidationrules) and outputs. - A README at the module root with a usage example.
State and backends
- Remote state with locking (S3+DynamoDB, Azure Storage with a blob lease, GCS).
- State is never committed to Git;
.gitignorecovers*.tfstate*. - State is separated by environment, with no implicit coupling between environments.
- IAM controls state access, not shared credentials.
Security
- No secrets in code or variable defaults. Use Key Vault / Secrets Manager / SOPS.
- IAM follows least privilege, with no
*:*orResource: "*"unless justified. - Encryption at rest and in transit is enabled for all data stores.
- Public access is explicitly denied unless intentional. Document intentional access in the module README.
-
tfsec/checkov/PSRulereport no findings, or exceptions are documented.
Change safety
-
terraform planis included in PRs as a comment (Atlantis / tfcmt / GH Actions). -
prevent_destroyis set on stateful resources (databases, KV, storage accounts). - Provider versions are pinned (
~>with explicit major and minor versions). - Module versions are pinned.
- Destructive diffs require a second approver.
Drift
- Scheduled drift detection (
terraform plan -detailed-exitcodedaily, or Driftctl). - Drift automatically creates a ticket and never remains silent.
- No manual console changes without subsequently codifying them.
Common findings
countused for lists that can reorder → usefor_eachwith stable keys.depends_oneverywhere → usually signals missing implicit dependencies; remove it unless truly necessary.- Data sources used for values available at plan time → unnecessary API calls and unstable CI.
- Environment differences through
terraform.workspacestring interpolation → fragile; use tfvars or separate stacks.
Output template
## IaC review - <module or stack>
| Area | Finding | Severity | Recommendation |
|---|---|---|---|
| State | Local state, no locking | High | Move to a remote backend with locking |
| Security | Storage account allows public access | High | Set public_network_access_enabled = false |
| Change safety | Provider version unpinned | Medium | Pin with ~> major.minor |
**Blocking findings**: <count>
**Verdict**: approve / request changes
Quality gate
-
terraform fmtandterraform validatepass, and the plan is attached to the PR. - No secrets appear in code, variables, or state; secrets use Key Vault or Secrets Manager.
- Provider and module versions are pinned; stateful resources set
prevent_destroy. -
tfsecorcheckovreports no findings, or every exception is documented. - Every resource carries
project,environment, andownertags.