Terraform
Intermediate 14 min read Terraform

Planning & Validation in CI

Review the diff, gate the danger

The plan is the review artifact: 'what will this change?' becomes a PR artifact instead of a leap. Validation (terraform validate, fmt, lint, checkov/tfsec) plus policy-as-code gates make apply a safe, human-verifiable step.

terraforming-planfmtvalidatecheckovsentinelpolicy as codeplan workspace
Prerequisites: Terraform Modules
Before we start

What you will learn

  • Run fmt/validate/lint and integrate plan review into PRs
  • Apply compose/comment plan bot patterns
  • Add static security checks (checkov, tfsec, provider policies)
In 60 seconds

Automation produces a plan on each PR, posts it to the PR, then gates apply on approval. fmt/validate catch syntax; scan tools catch security/structure smells; policy-as-code (OPA/Sentinel/provider policies) enforces org invariants.

Introduction

What is this?

Local hygiene: terraform fmt -recursive -check, terraform validate (in init'd dir), terraform console, .tflint, crossplane kubesec-esque. CI: init → validate → fmt check → plan (with -out and dir isolation) → comment plan via Terraform Cloud / Atlantis / GitHub Action plan-comment; apply gated in approvals. Providers' plan renderers (yellow/red diffs). Security scanning: checkov, tfsec (outdated), Trivy config scans, Snyk IaC, provider policies (AWS SCP) as backstop. Policy-as-code: OPA rego on plan files, Sentinel, Caspian Policies, Semgrep rules for HCL. Plan workspace / plan comment diff size guard.

Motivation

Why do you need it?

  • A reviewed plan is the single highest-leverage infra practice.
  • fmt/validate catches 90% of 'what even is this' mistakes before review.
  • Scanners find public-internet SG rules, plaintext secrets, missing encryption.
  • Policy-as-code enforces org invariants no human review reliably checks.
Think about it

Mental model

Imagine

An architect submitting plans for a building permit, with an inspector on-site

Think of

Plan review + policy gates

The permit office (PR/CI) reads the plans (plan diff); the inspector (scanner/policy) also checks the materials against the code (security/standard) before the build (apply) proceeds.

Mechanics

How it works

  • Push → CI init/integrate validate + fmt check → branch plan.
  • A plan bot (Atlantis/Cloud) comments a formatted diff on the PR.
  • Reviewers read ACTION/NOKEY lines; policy-as-code rules also evaluated at plan time.
  • Approval triggers apply (rarely auto; used for greenlighted stacks).
  • Wanttev: plan output stored, apply re-runs plan to confirm none drifted mid-flight.

Plan-gated apply.

PR push
CI validate/fmt
Plan
Policy check
Review
Apply
Hands-on

Examples

CI plan stage (GitHub Action sketch)

CI plan stage (GitHub Action sketch)
- name: Terraform plan
run: |
terraform init -backend-config=env/${ENV}.backend
terraform fmt -recursive -check
terraform validate
terraform plan -out=tfplan -var-file=env/${ENV}.tfvars
- name: Security scan
run: checkov -d . --download-external-modules false
# apply job: requires review + holds until plan matches

Init→format→validate→plan→scan pipeline; apply is a separate, approved job that reuses the plan.

Real world

In production

  • Atlantis/Terraform Cloud with plan PR comments across 50+ repos.
  • Checkov enforces no world-open SG and encrypted S3 globally.
  • Sentinel/OPA rule: prod plans must not exceed an env size delta or touch prod locks.
Guidelines

Best practices

  • Make plan output an artifact of every PR — readable, config'd for your env's non-output respects.
  • Gate applies with branch protection + required review; auto-apply only for ephemeral stacks.
  • Run scanners at PR, not only weekly; block on high/medium config findings.
  • Add policy-as-code where humans repeat the same 'this is bad' review comment.
  • Re-run plan at apply time and ensure it matches the approved one.
Watch out

Common mistakes

  • Applying from memory without a plan (the 'prod turn' that eats itself).
  • fmt/validate skipped; reviewers drown in whitespace diffs.
  • Policy-as-code in a repo nobody reads → rules never enforced.
  • Assuming scanners replace human review (they catch patterns, not intent).
When things break

Debugging tips

  • Plan contains noise/resources not yours → module unpin or stale state branch; verify backend key.
  • Policy rule flapping → read the rego and adapt the file/annotation rather than disabling.
  • Plan bot posts nothing → init creds/privaterepo reach; check CI logs.
  • fmt -check failing on CI but not locally → line endings/editor config; add a template rule.
Interviews

Interview checkpoint

Test yourself

Quick quiz

0/2
1

Which step technically guarantees syntax correctness?

2

A policy-as-code rule evaluates:

Go deeper

Official documentation