Terratest Your Terraform

Testing Infrastructure Locally with LocalStack

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

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