Overview
Terraform plans look correct and still provision the wrong thing. A bucket created without the required policy. An IAM role with overly broad permissions. A security group left open on the wrong port. These pass terraform plan and break in staging or production — or worse, pass in production and never get caught at all.
Terratest lets you catch them locally in 30 seconds. You write a Go test that applies your Terraform, inspects the actual provisioned state, and tears it down. LocalStack runs a local AWS emulator so you don’t need a real cloud account or incur any cost.
The workshop walked through creating an S3 bucket with a bucket policy and then verifying both with Terratest.
What We Covered
- Why infrastructure testing matters (and why most teams skip it)
- Setting up LocalStack to emulate AWS services locally
- Writing Terraform to provision an S3 bucket with a bucket policy
- Writing a Terratest in Go to verify the provisioned infrastructure
- Running the full test loop locally without touching a real cloud account
Stack
| Tool | Purpose |
|---|---|
| Terraform | Infrastructure-as-code |
| LocalStack | Local AWS cloud emulator |
| Terratest | Go-based infrastructure testing framework |
| Docker | Running LocalStack |
What the Code Looks Like
The Terraform to create a bucket with a policy:
resource "aws_s3_bucket" "workshop" {
bucket = "terratest-workshop-bucket"
}
resource "aws_s3_bucket_policy" "workshop" {
bucket = aws_s3_bucket.workshop.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Deny"
Principal = "*"
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.workshop.arn}/*"
Condition = { Bool = { "aws:SecureTransport" = "false" } }
}]
})
}
The Terratest assertion that verifies it:
func TestS3BucketHasPolicy(t *testing.T) {
opts := &terraform.Options{
TerraformDir: "../terraform",
Vars: map[string]interface{}{
"aws_endpoint": "http://localhost:4566", // LocalStack
},
}
defer terraform.Destroy(t, opts)
terraform.InitAndApply(t, opts)
bucketName := terraform.Output(t, opts, "bucket_name")
policy, err := aws.GetS3BucketPolicyE(t, "us-east-1", bucketName)
require.NoError(t, err, "Bucket policy should exist")
assert.Contains(t, policy, "aws:SecureTransport", "Policy should enforce HTTPS")
}
If the policy isn’t attached, or the condition is wrong, the test fails before the code ever touches a real environment.
Resources
- Workshop repo on GitHub — code used during the session
- Terratest documentation
- LocalStack documentation
If you’d test a function that allocates memory, you should test code that allocates cloud resources.
Event Details
Format: Workshop
Community: Testaholics Anonymous — Past Events