eview from a formality into a control plane.
Core Solution
Implementing an infrastructure code review pipeline requires shifting from syntax validation to impact-driven evaluation. The following architecture separates concerns: automated policy enforcement, deterministic plan analysis, and structured manual review.
Step 1: Define Review Boundaries and Risk Tiers
Not all infrastructure carries equal risk. Classify resources into tiers based on blast radius and compliance requirements:
- Tier 1: Networking, IAM, encryption, compliance boundaries
- Tier 2: Compute, storage, databases
- Tier 3: Auxiliary resources (DNS records, monitoring alerts, logging sinks)
Map review requirements to tiers. Tier 1 changes require mandatory policy gates, cost estimation, and senior platform engineer approval. Tier 3 changes can rely on automated checks and peer review.
Step 2: Integrate Policy-as-Code Scanning
Static analysis must run before human review. Use Open Policy Agent (OPA) or Checkov to enforce organizational standards. Policies should cover:
- Encryption at rest and in transit
- Public access restrictions
- Tagging and cost allocation
- Least privilege IAM
- Resource size limits
Example OPA policy (policy/networking.rego):
package terraform.security.networking
deny[msg] {
resource := input.resource.aws_security_group[name]
resource.value.ingress[_].cidr_blocks[_] == "0.0.0.0/0"
not resource.value.tags.security == "internal"
msg := sprintf("Security group %s allows public ingress without internal tag", [name])
}
Step 3: Generate and Review Execution Plans
IaC review must be plan-driven. The plan output is the only deterministic representation of what will change. Configure CI to:
- Initialize state with remote backend
- Run
terraform plan -out=tfplan
- Serialize plan to JSON
- Post plan summary as PR comment
- Block merge if plan contains destructive changes outside maintenance windows
Example GitHub Actions step:
- name: Generate Terraform Plan
run: terraform plan -out=tfplan -json > plan.json
env:
TF_VAR_environment: ${{ github.event.pull_request.base.ref }}
- name: Post Plan Summary
uses: actions/github-script@v7
with:
script: |
const plan = require('./plan.json');
const summary = plan.resource_changes
.filter(c => c.change.actions.includes('delete') || c.change.actions.includes('create'))
.map(c => `- ${c.change.actions.join(', ')}: ${c.address}`)
.join('\n');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Infrastructure Plan Summary\n${summary}\n\n⚠️ Destructive changes require explicit approval.`
});
Step 4: Implement Cost and Security Gates
Cost estimation must run alongside policy checks. Tools like Infracost or Terraform Cloud cost estimation provide pre-deployment financial impact. Block merges when projected costs exceed tier thresholds or when security policies fail.
Architecture decision: Run policy and cost scans in parallel to reduce CI latency. Cache provider plugins and use workspaces to isolate state. Store policy definitions in a versioned repository alongside IaC to prevent compliance drift.
Step 5: Structure Manual Review Around Infrastructure Patterns
Human review should focus on:
- State consistency and backend configuration
- Dependency ordering and implicit resource graphs
- Environment parity and variable scoping
- Rollback strategy and drift detection readiness
Reviewers must verify that the plan matches the PR description, that no implicit deletions exist, and that tags align with cost allocation requirements.
Pitfall Guide
-
Reviewing syntax over semantics: Checking HCL formatting or variable names misses deployment impact. IaC review must evaluate the execution plan, not just source code. Best practice: Always require plan output in PRs and treat it as the source of truth.
-
Ignoring state context: Merging changes without understanding current state causes drift, resource conflicts, or accidental replacements. Best practice: Use remote state with locking, and run terraform plan against the target environment before review.
-
Hardcoding secrets or weak defaults: Embedding credentials, using default security groups, or disabling encryption creates immediate compliance violations. Best practice: Enforce secret injection via vaults, use strict default policies, and scan for plaintext credentials in CI.
-
Treating all resources equally: A DNS record change carries different risk than an IAM policy update. Applying uniform review standards wastes time or misses critical changes. Best practice: Implement risk-tiered review gates with automated escalation for Tier 1 resources.
-
Skipping policy versioning: Policies evolve. If policy definitions are not versioned alongside IaC, reviews become inconsistent. Best practice: Store policies in a dedicated repository with semantic versioning, and pin CI to specific policy versions.
-
Relying on manual-only reviews: Human review scales poorly and introduces subjectivity. Best practice: Automate 80% of validation (policy, cost, syntax, plan diff) and reserve manual review for architectural decisions and exception handling.
-
Not validating environment parity: Changes tested in dev often fail in prod due to quota limits, regional constraints, or permission boundaries. Best practice: Run plan validation against staging environments with production-like constraints before merge.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Multi-account AWS landing zone | OPA + Infracost + tiered approval | Isolates blast radius, enforces guardrails, prevents cross-account sprawl | Reduces overprovisioning by 30-40% |
| Kubernetes cluster provisioning | Pulumi + Conftest + plan diff automation | Declarative state tracking, policy enforcement on CRDs, deterministic drift detection | Lowers idle resource costs by 22% |
| Cost-sensitive startup | Checkov + GitHub Actions plan comments + manual exception flow | Lightweight, zero-license overhead, fast feedback loop | Prevents 15-25% monthly overruns |
| Regulated enterprise (HIPAA/SOC2) | Terraform Cloud + Sentinel + signed policy versions + audit trail | Compliance reporting, immutable plan history, role-based merge gates | Avoids $50k+ audit remediation costs |
Configuration Template
.github/workflows/infra-review.yml
name: Infrastructure Code Review
on:
pull_request:
paths:
- 'infrastructure/**'
jobs:
policy-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@v12
with:
directory: infrastructure
framework: terraform
quiet: true
soft_fail: false
plan-and-cost:
runs-on: ubuntu-latest
needs: policy-scan
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init -backend-config="key=${{ github.event.repository.name }}.tfstate"
- name: Generate Plan
run: terraform plan -out=tfplan -json > plan.json
- name: Post Plan Summary
uses: actions/github-script@v7
with:
script: |
const plan = require('./plan.json');
const changes = plan.resource_changes
.map(c => `- ${c.change.actions.join(', ')}: ${c.address}`)
.join('\n');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Infrastructure Plan\n${changes}\n\n✅ Policy scan passed. Review plan before merge.`
});
pre-commit-config.yaml
repos:
- repo: https://github.com/bridgecrewio/checkov
rev: v3.2.0
hooks:
- id: checkov
args: [--framework, terraform, --soft-fail, --quiet]
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.83.0
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_tflint
Quick Start Guide
- Install
checkov, tflint, and terraform locally. Add the pre-commit-config.yaml to your repository root and run pre-commit install.
- Create
.github/workflows/infra-review.yml with the template above. Replace backend configuration with your remote state setup.
- Commit a test IaC change (e.g., add an
aws_s3_bucket without encryption). Push to a PR and verify that Checkov blocks the merge and the plan comment posts automatically.
- Integrate cost estimation by adding
infracost breakdown --path=. --format=json to the workflow, or enable Terraform Cloud cost estimation if using HCP. Set budget thresholds in your PR branch protection rules.