ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

Infrastructure Testing Patterns

Test infrastructure code with the same rigor as application code. Covers Terraform plan testing, Pulumi unit tests, integration testing with real cloud resources, compliance testing, and the patterns that prevent infrastructure failures in production.

Infrastructure code provisions servers, networks, databases, and security policies. A bug in application code shows an error message. A bug in infrastructure code opens a security hole, deletes a database, or creates an unintended $50K/month resource. Infrastructure testing is not optional — it is essential.


Testing Pyramid for Infrastructure

                    ╱╲
                   ╱  ╲
                  ╱ E2E ╲         Cloud deployment tests
                 ╱────────╲       (expensive, slow, comprehensive)
                ╱Integration╲     Terraform plan validation
               ╱──────────────╲   (moderate cost, real providers)
              ╱   Unit Tests    ╲  Policy, schema, logic tests
             ╱────────────────────╲ (fast, free, many)
            ╱     Static Analysis  ╲ Linting, formatting, scanning
           ╱────────────────────────╲ (instant, free)

Static Analysis

# Terraform
terraform fmt -check                    # Formatting
terraform validate                       # Syntax + schema
tflint                                   # Best practices
tfsec                                    # Security scanning
checkov --directory .                    # Compliance policies

# Pulumi
pulumi preview                           # Dry-run
eslint ./infra/**/*.ts                   # Code linting

Unit Testing (Policy as Code)

OPA/Conftest

# policy/terraform.rego
package terraform

deny[msg] {
  resource := input.planned_values.root_module.resources[_]
  resource.type == "aws_s3_bucket"
  not resource.values.server_side_encryption_configuration
  msg := sprintf("S3 bucket '%s' must have encryption", [resource.name])
}

deny[msg] {
  resource := input.planned_values.root_module.resources[_]
  manageable_resources := {"aws_instance", "aws_s3_bucket", "aws_rds_db_instance"}
  manageable_resources[resource.type]
  not resource.values.tags.Team
  msg := sprintf("Resource '%s' must have a Team tag", [resource.address])
}
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
conftest test plan.json --policy policy/

Pulumi Unit Tests

import * as pulumi from "@pulumi/pulumi";
import { describe, it, expect } from "vitest";

pulumi.runtime.setMocks({
  newResource: (args) => ({ id: `${args.name}-id`, state: args.inputs }),
  call: (args) => args.inputs,
});

describe("S3 Bucket", () => {
  it("should have encryption enabled", async () => {
    const { bucket } = await import("./s3");
    const encryption = await new Promise<any>((resolve) => {
      bucket.serverSideEncryptionConfiguration.apply(resolve);
    });
    expect(encryption).toBeDefined();
    expect(encryption.rule.applyServerSideEncryptionByDefault.sseAlgorithm)
      .toBe("aws:kms");
  });

  it("should block public access", async () => {
    const { publicAccessBlock } = await import("./s3");
    const blockPublicAcls = await new Promise<boolean>((resolve) => {
      publicAccessBlock.blockPublicAcls.apply(resolve);
    });
    expect(blockPublicAcls).toBe(true);
  });
});

Integration Testing with Terratest

package test

import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/gruntwork-io/terratest/modules/aws"
    "github.com/stretchr/testify/assert"
)

func TestS3BucketCreation(t *testing.T) {
    t.Parallel()
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../modules/s3",
        Vars: map[string]interface{}{
            "bucket_name": "test-bucket-" + random.UniqueId(),
            "environment": "test",
        },
    })
    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    bucketName := terraform.Output(t, terraformOptions, "bucket_name")
    region := terraform.Output(t, terraformOptions, "bucket_region")
    
    encryption := aws.GetS3BucketEncryption(t, region, bucketName)
    assert.Equal(t, "aws:kms", encryption)
    
    versioning := aws.GetS3BucketVersioning(t, region, bucketName)
    assert.Equal(t, "Enabled", versioning)
}

Anti-Patterns

Anti-PatternConsequenceFix
No infrastructure testingBugs found in productionTesting pyramid from static to integration
Testing only with terraform planPlan looks fine but apply failsIntegration tests with real resources
No policy enforcementSecurity misconfigurations deployedOPA/Conftest policies in CI
Manual infrastructure reviewsSubjective, inconsistentAutomated policy checks + human review
Integration tests without cleanupOrphaned test resources, cost wastedefer destroy, test resource tagging

Infrastructure testing is the safety net between your Terraform code and your production environment. Every untested change is a potential outage, security hole, or surprise bill.

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →