Verdict
Yes
Yes, test infrastructure as code with plan-level tests in CI that assert the settings protecting data and uptime, such as backup retention and deletion protection; do not assert every attribute or create real resources on every change.
Why
- Blast radiususers
- Change frequencyregularly
- Detectabilityeventually
- Reversibilitywith-effort
- Test costmoderate
Yes, test infrastructure as code with plan-level tests that assert the settings protecting data and uptime. The typical case is a product team's Terraform for one production account, changed about once a month. Blast radius is users, because a wrong database or load balancer setting breaks the product, and Change frequency is regularly. Detectability is eventually and Reversibility is with-effort, because an apply accepts backups cut to one day with no visible change, and the fix is an apply plus data repair. Test cost is moderate, because a terraform test file with a mock provider needs no cloud account and about an hour of work, so rule R11 gives Test.
| When | Decision | Why |
|---|---|---|
| The code sets network rules, bucket access or IAM policies that guard customer data | Test mandatory: a policy check fails any plan with a public bucket, an open database port or a wildcard IAM action | Blast radius rises to safety-or-legal, Detectability to never and Reversibility to impossible, because a public bucket serves files with no error and leaked files cannot be recalled |
| The code sets instance types or counts, where one wrong value multiplies the cloud bill | Test mandatory: a plan test fails on an instance type outside an allowed list or a count above a ceiling | Blast radius rises to money and Reversibility to costly, because the provider bills every hour the instances ran |
| The code builds a staging environment that the team shares and no customer uses | Test minimally: one plan-level `terraform test` on each staging pull request, asserting the instance count | Blast radius falls to internal, because a broken staging environment slows only developers |
| The code builds a sandbox account that only you use | Do not test sandbox code; read the plan before each apply | Blast radius falls to none, because you alone bear any failure |
| Import blocks bring hand-made production resources under Terraform one time | Test the import differently: require a zero-change plan after the import and a second reviewer before the apply | Change frequency falls to once, so a test in the suite would never run again |
| The failure shows only after a real apply, such as an IAM policy that lacks one permission a nightly job needs, so the job fails and no data is exposed | Test it differently: apply each change to a staging account first and alert on access-denied errors | Test cost rises to heavy, because only a real apply shows the error; Detectability stays eventually and Reversibility with-effort |
What breaks if you don't test
Without tests, the plan is the only check, and a reviewer must spot the wrong line. A module refactor produces dozens of plan lines, mostly moved blocks and tags, and one line such as ~ backup_retention_period = 14 -> 1 hides among them. The apply succeeds and the service runs as before, until someone needs a restore from ten days ago.
What you lose if you over-test
In my practice, a Terratest run that created and destroyed one RDS instance took 25 minutes, and a failed cleanup once left test instances running for a week. Assertions that copy attribute values from the configuration repeat the file: every intended change needs a second edit, and a value wrong in both places passes.
How to test
Write plan-level tests with terraform test or tofu test and command = plan, run in CI on every pull request. A mock provider lets the plan run without cloud credentials. The minimum set:
- Run
terraform fmt -checkandterraform validate. - In each production root module, assert backup retention, deletion protection, availability zone count and minimum instance count.
- Set
prevent_destroyon databases, buckets and volumes, so Terraform rejects any plan that destroys them. It does not cover a deleted resource block, so review deletions by hand.
When the answer changes
- The code sets network rules, bucket access or IAM policies.
- The code sets instance types or counts that drive the cloud bill.
- The code builds only a sandbox you alone use.
Real incident + Code example
The backups that shrank to one day
On a booking product I worked on, a refactor moved the production PostgreSQL instance into our shared RDS module. The old code set backup_retention_period = 14; the module's variable defaulted to 1, and nobody passed it. Two reviewers approved a 46-line plan of mostly moved blocks. Five weeks later a customer asked us to restore bookings that a bad import had deleted six days earlier. The oldest automated backup was one day old, so we spent two days rebuilding the bookings from application logs. We removed the variable's default and added this test, which fails on the refactored code:
# envs/production/tests/safety_nets.tftest.hcl
mock_provider "aws" {}
run "production_database_keeps_its_safety_nets" {
command = plan
# the database module outputs both values
assert {
condition = module.database.backup_retention_period >= 14
error_message = "Production must keep at least 14 days of database backups."
}
assert {
condition = module.database.deletion_protection
error_message = "The production database must have deletion protection on."
}
}
Related questions
FAQ
- Should you unit test infrastructure code?
Yes, unit test infrastructure code with plan-level tests that need no cloud account, such as
terraform testwithcommand = planand a mock provider. Assert settings that protect data and uptime, not attributes copied from the configuration.- Is terraform plan enough to catch mistakes?
No,
terraform planalone misses mistakes, because a reviewer must spot one wrong line among dozens. Keep reading the plan, and add assertions that fail CI when a protected setting changes.- Should I test Terraform modules?
Yes, test a Terraform module with plan-level tests, one run block per input combination that switches resources on or off. A wrong
countcondition passesterraform validate; an assertion on the planned resources catches it.- Should I scan Terraform for security problems?
Yes, when Terraform guards customer data, fail every plan with a public bucket, an open database port or a wildcard IAM action. Open Policy Agent checks the plan JSON from
terraform show -jsonbefore any apply.