Skip to content

TerraformTesting

Dennis Lee edited this page Jul 20, 2026 · 2 revisions

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

Terraform Testing

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.

The Testing Pyramid for Infrastructure

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

Layer 1: Input Validation

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.

Layer 2: Preconditions and Postconditions

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"
    }
  }
}

Layer 3: terraform test (Terraform 1.6+)

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.

Layer 4: Terratest

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.

Layer 5: Policy as Code in CI

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.

Radar Assessment

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.

Clone this wiki locally