Terraform in CI/CD & Testing

Terraform / Infrastructure as Code

Chapter 10 · Terraform in CI/CD & Testing

Every command so far has been run by hand. A real team runs Terraform through CI — this chapter ties the whole workflow together using pipelines1's own material, applied specifically to infrastructure changes.

terraform fmt & validate

fmt rewrites files into Terraform's canonical style; fmt -check exits non-zero without changing anything, suitable for a CI gate. validate catches syntax and type errors — without touching real infrastructure or needing full provider credentials, just an initialized backend.

$ terraform fmt -check $ terraform validate Success! The configuration is valid.

The Plan-as-PR-Comment Pattern

A CI job runs terraform plan on every pull request and posts the output as a comment directly on that PR — reviewers see exactly what would change in real infrastructure before ever approving the merge, not just the raw .tf diff. A one-line variable change can produce a plan that recreates a dozen resources; the diff alone would never show that.

A Real GitHub Actions Workflow

Following pipelines1-3's workflow-YAML pattern and pipelines1-6's job-dependency gating:

on: pull_request: push: branches: [main] jobs: plan: if: github.event_name == 'pull_request' steps: - run: terraform fmt -check - run: terraform validate - run: terraform plan -out=tfplan - # post tfplan output as a PR comment apply: if: github.event_name == 'push' needs: [] steps: - run: terraform apply -auto-approve

Policy as Code

Automated rules that block a plan from ever being applied if it violates a defined policy — "no public S3 buckets," "no unencrypted volumes," enforced mechanically rather than relying on a reviewer noticing. Sentinel is HashiCorp's own policy engine (Terraform Cloud/Enterprise); OPA (via conftest) is the open-source alternative usable in any CI pipeline. Both directly enforce the security practices dbsec1/crypto1/owasp1 already covered — the difference is these rules run automatically on every plan, not just when a reviewer happens to remember to check.

terraform test & Terratest

Terraform's native testing framework (1.6+) uses .tftest.hcl files with run blocks and assert conditions, and can execute against mocked providers — fast, free tests with no real infrastructure spun up. Terratest (a third-party, Go-based library, older and still widely used) takes the opposite approach: it actually provisions real infrastructure to test against, then tears it down — slower and billable, but validates genuinely real provider behavior rather than a mock's approximation of it.

Bringing It Together — the Full CI/CD Gate

fmt/validatetestplan + policy check → human review of the posted plan comment → merge → apply. The same test → build → staging → production chain pipelines1-9's own capstone assembled for application code, applied here to infrastructure changes instead.

Automation catches syntax; a human still catches intent
Even with every automated gate in place, the human reading the posted plan output remains the single most valuable checkpoint — automation reliably catches format/policy/syntax violations, but "wait, that's not actually what I meant to change" is still a human judgment call.
Apply only from the reviewed, merged branch — and mind who holds the credentials
Never let CI apply directly from a feature branch or on every push — only from main, after review, mirroring pipelines1-8's protected-environment gating. Worth stating honestly: this CI-driven model is push-based — the CI runner itself needs real deployment credentials, the same tradeoff k8s2-9 named for traditional CI/CD. Tools like Atlantis or Terraform Cloud's own run system move toward a more pull-based model, but the GitHub Actions pattern shown here is push-based by default.

Hands-On Exercises

Exercise 1

Explain why terraform fmt -check and validate run before plan in a CI pipeline rather than after, and what each one catches that the other doesn't.

📄 View solution
Exercise 2

Explain the value of the plan-as-PR-comment pattern — what does it let a human reviewer see that reading the raw .tf file diff alone wouldn't show?

📄 View solution
Exercise 3

Contrast policy as code (Sentinel/OPA) enforcement against a manual code-review checklist for catching something like "no public S3 buckets." Why can automated enforcement catch cases a checklist-based review might miss?

📄 View solution

Chapter 10 Quick Reference

  • terraform fmt -check — style; validate — syntax/type errors, no real infra touched
  • Plan-as-PR-comment — shows the real infrastructure impact before merge, not just the file diff
  • Policy as code — Sentinel (HashiCorp/Enterprise) or OPA/conftest (open), automated policy enforcement on every plan
  • terraform test — native, mockable, fast; Terratest — real infrastructure, slower, more realistic
  • Apply only from the reviewed, merged branch — this CI model is push-based, so the runner holds real credentials