-
Notifications
You must be signed in to change notification settings - Fork 0
TerraformTesting
title: Terraform Testing type: technique created: 2026-05-21 last_updated: 2026-05-21 related: ["radar/techniques/LambdaUnitTesting", "radar/techniques/LLMTDDLoop"] sources: ["https://mattias.engineer/posts/terraform-testing-and-validation/"] radar_quadrant: Techniques radar_ring: Assess radar_position: inner
A layered approach to applying TDD discipline to infrastructure-as-code, using Terraform's native validation and test features alongside third-party tools for integration and policy testing.
Terraform testing follows the same pyramid structure as application testing: fast, cheap tests run first; slow, expensive tests run last.
| Layer | Tool | Speed | Scope |
|---|---|---|---|
| Input validation |
variable validation blocks |
Instant | Variable values |
| Preconditions/postconditions |
lifecycle checks |
Plan/apply time | Resource state invariants |
| Unit tests |
terraform test (1.6+) |
Fast | Module outputs in isolation |
| Integration tests | Terratest (Go) | Slow | Real deployed infrastructure |
| Policy checks | OPA / Sentinel | CI time | Plan compliance |
validation blocks on variable declarations reject invalid inputs before plan runs:
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be dev, staging, or prod"
}
}No test framework needed; validation is built into the Terraform language.
lifecycle blocks assert invariants during plan and apply:
resource "aws_instance" "web" {
# ...
lifecycle {
precondition {
condition = var.instance_type != "t2.micro" || var.environment == "dev"
error_message = "t2.micro is only allowed in dev"
}
}
}Native test framework added in Terraform 1.6. Tests are .tftest.hcl files that plan or apply a module and assert on outputs:
run "creates_s3_bucket" {
command = plan
assert {
condition = aws_s3_bucket.main.bucket == "expected-bucket-name"
error_message = "bucket name does not match"
}
}terraform test is the first first-party, low-friction entry point for IaC unit testing; no Go or external framework required.
Go-based integration tests that deploy real infrastructure and run assertions against live resources. Significantly slower (minutes to hours) but tests actual cloud behaviour rather than plan output:
bucketID := terraform.Output(t, terraformOptions, "bucket_id")
aws.AssertS3BucketExists(t, "us-east-1", bucketID)Reserved for critical modules where plan-only testing is insufficient.
OPA or Sentinel rules evaluate the Terraform plan JSON before apply, failing CI on violations:
- "No S3 bucket may be public"
- "All resources must have cost-centre tags"
- "RDS instances must have deletion_protection = true in prod"
Policy rules are versioned alongside infrastructure code and enforced at PR time.
Terraform Testing sits in the Assess ring of the Techniques quadrant, at inner position. First studied via Mattias Åslund's blog (2023-12-26). The native terraform test framework (1.6, released late 2023) removed the primary barrier to entry — previously all IaC testing required Terratest (Go) or cloud-specific tools. That is third-party study, not first-person use: no terraform test has been written for the user's own IaC, and the Trial ring requires that, so it stays at Assess. Inner position reflects a clear, low-friction path via terraform test for any team on Terraform 1.6+, and direct applicability to any infrastructure codebase lacking automated validation. The gate to Trial is at least one module covered by terraform test running in CI.