The Terraform Pull Request Review Checklist Every Platform Team Needs
A practical, copy-pasteable checklist for reviewing Terraform pull requests — drift, cost, security, and policy — before they merge, not after.
A Terraform pull request looks like any other code review — a diff, some comments, an approve button — but the blast radius is different. A bad application code review ships a bug. A bad Terraform review can delete a database, open a security group to the world, or triple a cloud bill overnight. Reviewing a Terraform diff line-by-line for logic errors catches typos; it does not catch drift, cost, or a misconfigured IAM policy, because none of those are visible in the raw HCL diff. This checklist is what to actually check.
1. Drift — is the starting state what you think it is?
Before reviewing what a PR changes, confirm the resources it touches haven't already drifted from Terraform's record of them. A plan generated against a stale state file can look clean and still be wrong, because it's comparing against a version of reality that no longer exists.
- Check whether any resource in this diff has drifted since the last apply.
- If drift is present, resolve it (reconcile or reimport) before reviewing the new change on top of it.
- See our drift detection guide for detection methods.
2. Cost — what does this change actually cost per month?
A resource diff doesn't tell you cost impact at a glance — an instance type bump, an added NAT gateway, or a provisioned-throughput change on a database can each add hundreds of dollars a month, and none of them look alarming in a plain terraform plan diff.
- Run the diff through a cost estimator (Infracost or equivalent) and read the monthly delta per resource, not just the total.
- Set an explicit warn/block threshold so a large delta needs a second look, instead of relying on someone noticing.
- Watch for resources that don't show a cost line at all — usage-based pricing (data transfer, request volume) often needs a manual sanity check.
Example threshold config:
# .github/driftguard.yml cost: currency: USD warn_above_monthly_delta: 100 block_above_monthly_delta: 1000
3. Security — static analysis plus a human sanity check
Static analysis (Checkov, tfsec, or similar) catches the categories that recur constantly: public S3 buckets, security groups open to 0.0.0.0/0, unencrypted volumes, IAM policies with wildcard resources. Run it on every PR, not periodically — a rule that only fires in a weekly scan finds the misconfiguration a week after it's already live.
- Static analysis on every PR, blocking on high-severity findings by default.
- A human still reads the IAM diff — static rules catch known bad patterns, not "this role has broader access than this service needs."
- Least-privilege for the reviewing tool itself: whatever reads your live cloud state to check drift or security should use short-lived, read-only credentials — AWS STS AssumeRole, GCP Workload Identity Federation, or Azure federated workload identity — never long-lived keys.
4. Policy — is this change something your team is actually allowed to make?
Cost and security tools catch known bad patterns. Policy is different: it's your team's own rules about what can change, encoded so they're enforced instead of just written in a wiki page nobody reads before merging.
# .github/driftguard.yml
policy:
block:
- "aws_db_instance.*.publicly_accessible"
- "aws_iam_policy.*" # require manual approval on any IAM policy change
warn:
- "aws_instance.*.instance_type"Simple glob patterns cover most teams. For multi-environment rules, team-based access, or time-based restrictions, a full OPA/Rego policy bundle gives you the same enforcement with more expressive logic — see the policy engine docs.
5. Precedent — has something like this broken before?
The most useful signal in a review is often: "we changed something like this six months ago and it caused an incident." That context usually lives in someone's memory, not in the PR. Attaching similar past findings — same resource type, same kind of change, same blast radius — directly to the review turns institutional memory into something every reviewer sees, not just whoever happened to be on call last time.
The checklist
- No drift on any resource this PR touches, or drift is resolved first.
- Monthly cost delta reviewed, not just the plan diff.
- Static security analysis passed (or high-severity findings explicitly acknowledged).
- Change matches your team's policy — no blocked patterns, warnings addressed.
- Checked against past incidents on the same resource type or blast radius.
- IAM / access changes read by a human, not just a linter.
Related reading
DriftGuard runs this whole checklist automatically on every Terraform pull request.