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.
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:
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/validate → test → plan + 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.
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.
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
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.
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?
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 solutionChapter 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