๐งฑ Infrastructure as Code Maturity and Test Strategy
Intro: IaC programs usually fail in one of two ways: they stay manual and ad hoc, or they become โfully automatedโ without trustworthy validation. This page focuses on the middle ground that actually works: grow IaC maturity together with testing maturity.
What this page includes
- a practical maturity model for Terraform- or IaC-centered teams
- a test stack from format and validation through plan and environment tests
- examples for modern and legacy tooling
- rollout advice for platform, cloud, and Product Security teams
Why maturity matters
A lot of teams say โwe use Infrastructure as Codeโ when they really mean one of these:
- someone wrote Terraform once but production changes still happen manually;
- a few modules exist, but nobody trusts the plans;
- the code is versioned, but review, policy, testing, and evidence are weak;
- the deployment is automated, but drift, exceptions, and documentation are unmanaged.
A practical IaC maturity model
| Level | Name | Typical traits | Main security risk |
|---|---|---|---|
| 0 | Manual or script-heavy | console changes, copy-paste, poor reviewability | no trustworthy source of truth |
| 1 | Basic IaC adoption | Terraform or similar exists, but structure is inconsistent | drift and unsafe patterns spread quickly |
| 2 | Reviewable IaC | modules, pull requests, plan review, linting | security still depends on human reviewers catching everything |
| 3 | Tested IaC | validation, policy checks, plan analysis, environment tests | false confidence if tests only check syntax |
| 4 | Governed IaC | policy exceptions, evidence, reusable safe modules, release control | operational overhead if the policy model is too brittle |
| 5 | Adaptive IaC | drift feedback, evidence reuse, metrics, module-first prevention | complexity can outgrow team capacity if ownership is unclear |
The test stack that works in practice
Layer 1 โ formatting and syntax
terraform fmt -check -recursive
terraform init -backend=false
terraform validate
Layer 2 โ static misconfiguration and policy checks
checkov -d .
trivy config .
docker run --rm -v "$PWD:/path" checkmarx/kics scan -p /path
conftest test --policy policies terraform/
Layer 3 โ plan-aware policy checks
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
jq '.resource_changes[] | {address, actions: .change.actions}' tfplan.json
Layer 4 โ environment or contract tests
Modern pattern:
- terraform test
- Terratest when code-driven integration tests are worth the extra overhead
Legacy or older pattern:
- Kitchen-Terraform
Layer 5 โ post-deploy drift and posture review
Purpose:
- confirm runtime stayed aligned with approved intent;
- detect console changes, weak defaults, or cloud-side deviations.
Practical CI pattern
Fast path for pull requests
Run:
terraform fmt -checkterraform init -backend=falseterraform validatecheckov -d .trivy config .
Medium path for protected branches
Run:
- all fast checks
terraform plan- plan JSON extraction
- custom policy checks on the plan
- docs generation if modules changed
Deep path for risky modules
Run:
- all medium checks
terraform test- selective environment tests
- cloud-side smoke tests in isolated accounts
Modern vs legacy tooling notes
tfsec
You will still see older pipelines using:
tfsec .
This is useful as historical context, but many teams now standardize on:
trivy config .
Kitchen-Terraform
This was a common environment testing pattern in older Terraform programs. It is still educational, but newer teams often prefer:
terraform testfor native module testing- Terratest for richer integration logic
- policy plus plan checks for most everyday gates
Example: pre-commit style local checks
#!/usr/bin/env bash
set -euo pipefail
terraform fmt -check -recursive
terraform init -backend=false >/dev/null
terraform validate
checkov -d .
trivy config .
See ../snippets/terraform/iac-local-checks.sh.
Example: native Terraform tests
terraform test
Use native tests for:
- module input validation expectations;
- output expectations;
- assumptions about resource composition;
- reusable module safety checks.
What Product Security should push for first
If your organization is early:
- stop manual console changes from being the โrealโ deployment path;
- require PR review and plan visibility;
- add one static scanner and one policy layer;
- separate warning-only checks from true blockers;
- treat shared modules as higher-trust assets with stronger tests.
Related pages
- ๐ก๏ธ Security as Policy for Terraform and Infrastructure as Code
- ๐งฑ Terraform Security Scanning and Checkov
- ๐งฑ Terraform Top 10 Misconfigurations
- ๐ฐ๏ธ Cloud Auditing by API and Configuration State
---Author attribution: Ivan Piskunov, 2026 - Educational and defensive-engineering use.