Skip to main content

Testing Terraform Code

1. Using Terraform Validate

The terraform validate command checks whether a Terraform configuration is valid and can be applied without errors. It does not interact with remote systems or resources, making it a fast way to catch syntax and configuration issues early in the development process.

Command Description Options
terraform validate Validates the Terraform configuration files in the current directory. -no-color - Disables color output
-json - Outputs the result in JSON format

Example: Running terraform validate

terraform validate
Note: terraform validate does not require a backend or state file to be present, but it will report errors if required variables are missing or if there are syntax issues in the configuration files.

2. Using Terraform Test Framework

The Terraform Test Framework provides a structured way to test your Terraform configurations. It allows you to define test cases that validate the behavior of your infrastructure code.

Component Description Notes
test block Defines a test case within a .tf file Must be inside a test block in a Terraform file
check block Defines assertions to validate the state of resources Can be used multiple times within a single test
variables Input variables for the test configuration Can reference values from the main configuration

Example: Basic Test Case

terraform {
  required_version = ">= 1.5.0"
}

provider "aws" {
  region = "us-west-2"
}

resource "aws_s3_bucket" "test_bucket" {
  bucket = "my-test-bucket"
}

test "bucket_exists" {
  providers = {
    aws = aws
  }

  check "bucket_name" {
    assert {
      condition = aws_s3_bucket.test_bucket.bucket == "my-test-bucket"
      error_message = "Bucket name does not match expected value"
    }
  }
}
Note: The Terraform Test Framework is available starting from Terraform version 1.5.0.

Workflow for Writing Tests

  1. Create a .tf file with test blocks
  2. Define providers and resources needed for testing
  3. Add check blocks to assert desired states
  4. Run terraform test command to execute tests

3. Writing Unit Tests

Example: Test a simple data source

data "aws_ami" "latest_amazon_linux" {
  most_recent = true
  owners      = ["137112412987"]
  filters {
    name   = "amzn2-ami-hvm-*"
    values = ["*x86_64"]
  }
}

output "ami_id" {
  value = data.aws_ami.latest_amazon_linux.id
}
Test Type Description Tool Notes
Data Source Test Validate data source outputs test block in Terraform Use data blocks to simulate real resources
Resource Test Validate resource creation and attributes test block in Terraform Use resource blocks to simulate real resources
Note: Terraform's built-in test block is available in version 1.6+.

Example: Using test block to validate resource creation

resource "aws_s3_bucket" "example" {
  bucket = "my-example-bucket"
}

test "bucket_creation" {
  providers = {
    aws = aws
  }

  step "create_bucket" {
    config = aws_s3_bucket.example
  }

  check "bucket_exists" {
    condition = aws_s3_bucket.example.id != ""
    error_message = "Bucket was not created"
  }
}

4. Writing Integration Tests

Example: Basic Integration Test Structure

package integration

import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/terraform"
)

func TestTerraformExample(t *testing.T) {
    terraformOptions := &terraform.Options{
        TerraformDir: "./example",
        Vars: map[string]interface{}{
            "instance_type": "t2.micro",
        },
    }

    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    // Assert that the instance was created
    instanceId := terraform.Output(t, terraformOptions, "instance_id")
    if instanceId == "" {
        t.Fatal("Expected instance to be created")
    }
}
Test Type Description Tools Notes
Infrastructure as Code Testing Terraform code using real infrastructure terratest, testinfra Requires provisioned infrastructure
State Validation Verifying that Terraform state matches expectations terraform plan, terraform show Use -out to capture plan output
Resource Validation Checking that resources are created with correct attributes terraform output, aws ec2 describe-instances Validate resource properties in state

Best Practices for Integration Tests

  • Use terratest for robust test frameworks
  • Test real infrastructure with mocked or stubbed services
  • Validate outputs and state after each test run
  • Use defer terraform.Destroy(t, options) to clean up resources
  • Isolate test environments with unique names or tags
Note: Integration tests require access to cloud providers or local infrastructure.
Warning: Running integration tests can incur costs if using cloud providers.

5. Using Terraform Plan Tests

Terraform provides a built-in testing framework to validate your configurations. Plan tests are used to verify that the plan output matches expectations.

Field Description Example
plan Specifies the path to the plan file or the plan itself. plan = "example.tfplan"
expectations Defines the expected outcomes from the plan. expectations = ["resource_count == 2"]

Example: Basic Plan Test

terraform {
  required_version = ">= 1.0"
}

provider "aws" {
  region = "us-west-2"
}

resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

resource "aws_instance" "db" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

# Test block
test "plan_test" {
  plan = "example.tfplan"

  expectation "resource_count" {
    condition = length(aws_instance) == 2
    error_message = "Expected 2 instances"
  }
}
Note: The plan test framework is available starting in Terraform v1.5.
Warning: Ensure that the plan file is generated with terraform plan -out=example.tfplan.

Plan Test Summary

  • Use terraform plan -out=plan.tfplan to generate a plan file.
  • Define tests in .tftest.hcl files.
  • Each test can have multiple expectations to validate plan outcomes.

6. Implementing Policy Tests

Example: Basic Policy Test

package main

import (
    "testing"
    "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccPolicyBasic(t *testing.T) {
    resource.Test(t, resource.TestCase{
        Providers: testProviders,
        Steps: []resource.TestStep{
            {
                Config: testConfigBasic,
                Check: resource.ComposeTestCheckFunc(
                    resource.TestCheckResourceAttr("aws_instance.test", "ami", "ami-12345678"),
                ),
            },
        },
    })
}
Property Description Values Notes
resource.Test Function to run a test case func(t *testing.T, testCase resource.TestCase) Core testing function for Terraform resources
Providers Map of providers to use in tests map[string]terraform.ResourceProvider Must include all providers used in config
Steps List of test steps to run []resource.TestStep Each step can have Config, Check, and Destroy
Config Configuration to apply in this test step string Can use HCL or JSON format
Check Assertions to run after applying config resource.TestCheckFunc Use built-in checks or custom functions
Note: Policy tests ensure that Terraform configurations meet specific requirements and constraints before deployment. These tests are crucial for maintaining infrastructure quality and preventing unwanted changes.
Warning: Ensure that test configurations do not create real resources in production environments. Always use isolated test environments or mocks.

Key Concepts

  • Test Framework: Uses Go's testing package with Terraform-specific helpers
  • Resource Testing: Validates that resources are created with expected attributes
  • State Management: Tests can check state after resource creation or destruction
  • Provider Isolation: Each test uses its own provider configuration to avoid interference

7. Using Mock Providers

Mock providers in Terraform allow you to simulate the behavior of real providers during testing, enabling faster and more reliable test runs without requiring actual cloud resources or external dependencies.

Property Description Example
mock_provider Defines a mock provider configuration to simulate real provider behavior. mock_provider "aws" { }
mock_resource Represents a mocked resource that mimics the structure of a real resource. mock_resource "aws_instance" { }

Example: Defining a Mock Provider

provider "aws" {
  region = "us-west-2"
  mock_provider "aws" {
    # Mock configuration
  }
}

Example: Using Mock Resources

resource "aws_instance" "mocked_instance" {
  mock_resource "aws_instance" {
    # Mocked resource definition
  }
}
Note: Mock providers are primarily used in unit testing and integration tests to isolate the behavior of Terraform modules from external dependencies.

8. Testing Module Outputs

Property Description Example
output.name Name of the output variable instance_id
output.value Value of the output variable aws_instance.example.id
output.sensitive Boolean indicating if the output is sensitive true

Example: Define and Test Module Output

output "instance_id" {
  value = aws_instance.example.id
  sensitive = false
}

output "private_ip" {
  value = aws_instance.example.private_ip
  sensitive = true
}

Example: Test Output in Terraform

data "terraform_remote_state" "prod" {
  backend = "local"
  config = {
    path = "../prod/terraform.tfstate"
  }
}

output "test_output" {
  value = data.terraform_remote_state.prod.outputs.instance_id
}

9. Implementing End-to-End Tests

Example: Basic E2E Test Setup

#!/bin/bash
# test.sh
set -e

# Initialize Terraform
terraform init -backend=false

# Plan and apply
terraform plan -out=tfplan
terraform apply -auto-approve tfplan

# Validate deployment
echo "Validating deployment..."
Test Type Description Tools Notes
Infrastructure Validation Ensures deployed infrastructure matches expected state terraform show, terraform plan Use -out to capture plan for validation
State Consistency Verifies that state file reflects actual infrastructure terraform refresh, terraform state list Check for drift between desired and actual state
Integration Testing Tests interaction between multiple resources testinfra, inspec Validate connectivity, access control, and behavior
Note: E2E tests should run in a clean environment to avoid interference from previous test runs.

Workflow: E2E Test Execution

  1. Initialize Terraform with clean state
  2. Apply infrastructure changes using terraform apply
  3. Run validation checks against deployed resources
  4. Clean up infrastructure after test completion

10. Automating Test Runs

Example: CI/CD Pipeline with Terraform Tests

name: Terraform CI
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
      - name: Run Terraform Init
        run: terraform init
      - name: Run Terraform Validate
        run: terraform validate
      - name: Run Terraform Test
        run: terraform test
Step Description Command Notes
Checkout Code Retrieve source code from repository actions/checkout@v3 Required for all CI steps
Setup Terraform Install Terraform CLI hashicorp/setup-terraform@v2 Ensures correct version
Init Initialize Terraform working directory terraform init Downloads required providers
Validate Check configuration syntax terraform validate Catches configuration errors early
Test Execute test suite terraform test Runs all defined tests
Note: Terraform's built-in test command requires Terraform v1.6+.

Best Practices for Test Automation

  • Run tests on every code change in CI/CD
  • Use terraform test for declarative test definitions
  • Include validation and linting in the pipeline
  • Set up test environments with isolated state

Was this article helpful?