Table of Contents
Why Test Terraform Code?
Infrastructure bugs are expensive. A misconfigured security group can expose your database. A wrong CIDR can break networking. Testing catches these before they hit production.
Level 1: Built-in Validation
Variable Validation Rules
variable "instance_type" {
type = string
validation {
condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
error_message = "Instance type must be t3.micro, t3.small, or t3.medium."
}
}
variable "environment" {
type = string
validation {
condition = can(regex("^(dev|staging|prod)$", var.environment))
error_message = "Environment must be dev, staging, or prod."
}
}
Preconditions and Postconditions
resource "aws_instance" "web" {
instance_type = var.instance_type
ami = data.aws_ami.ubuntu.id
lifecycle {
precondition {
condition = data.aws_ami.ubuntu.architecture == "x86_64"
error_message = "AMI must be x86_64 architecture."
}
postcondition {
condition = self.public_ip != ""
error_message = "Instance must have a public IP."
}
}
}
Level 2: terraform test (Native Testing)
# tests/vpc_test.tftest.hcl
run "create_vpc" {
command = plan
assert {
condition = aws_vpc.main.cidr_block == "10.0.0.0/16"
error_message = "VPC CIDR should be 10.0.0.0/16"
}
assert {
condition = aws_vpc.main.enable_dns_hostnames == true
error_message = "DNS hostnames should be enabled"
}
}
Run with: terraform test
Level 3: Terratest (Go-Based Integration Tests)
func TestVpcModule(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../modules/vpc",
Vars: map[string]interface{}{
"vpc_cidr": "10.0.0.0/16",
"environment": "test",
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vpcId := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcId)
}
Level 4: Policy as Code
Open Policy Agent (OPA)
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_security_group_rule"
resource.change.after.cidr_blocks[_] == "0.0.0.0/0"
resource.change.after.from_port == 22
msg := "SSH must not be open to the world"
}
Testing Strategy
| Level | Tool | Speed | Cost | When |
|---|---|---|---|---|
| Validation | variable validation | Instant | Free | Every plan |
| Static | terraform validate, tflint | Seconds | Free | Every commit |
| Unit | terraform test (plan) | Seconds | Free | Every PR |
| Integration | Terratest | Minutes | Cloud costs | Pre-merge |
| Policy | OPA, Sentinel | Seconds | Free | Every plan |
Learn More
- Terraform for Beginners Course — hands-on testing labs
- Terraform By Example Book — real-world patterns
- Terraform Cheat Sheet — quick command reference


