Testing and Validation

The day untested Terraform destroyed production taught me that infrastructure needs tests just like code


Table of Contents


Introduction: The Untested Deployment

It was a Wednesday morning. I had just merged a "simple" change to our Terraform configurationβ€”updating a variable validation rule. The code review was quick. The tests? Well... there weren't any.

"It's just validation," I thought. "What could go wrong?"

I deployed to production.

Everything broke.

Turns out, my validation rule was stricter than I intended. It rejected ALL environment names, including "production". The deployment pipeline failed halfway through, leaving infrastructure in an inconsistent state. Services couldn't start. Alerts flooded in.

My manager's question haunted me: "Did you test this?"

I hadn't.

That incident sparked a journey into infrastructure testing. I learned that Terraform code needs testing just as much as application codeβ€”maybe more, because infrastructure failures affect everything.

I discovered:

  • Validation catches syntax errors before apply

  • Linting enforces best practices

  • Unit tests verify module behavior

  • Integration tests ensure components work together

  • Policy tests enforce security and compliance

This article is everything I learned about testing Terraform, from basic validation to comprehensive test suites that prevent production disasters.


Why Test Infrastructure Code?

The Cost of Infrastructure Bugs

Application bug:

  • One service down

  • Rollback and redeploy

  • Minutes to hours of downtime

Infrastructure bug:

  • Multiple services down

  • Database corruption risk

  • Network isolation

  • Hours to days of recovery

  • Potential data loss

What Testing Prevents

1. Syntax errors

2. Invalid configurations

3. Resource conflicts

4. Security violations

5. Compliance violations

Testing Benefits

spinner

Levels of Testing

Testing Pyramid for Infrastructure

spinner

1. Linting (Fast, many tests)

  • Code formatting

  • Best practices

  • Security checks

2. Validation (Fast)

  • Syntax validation

  • Type checking

  • Basic logic

3. Unit Tests (Medium speed)

  • Module behavior

  • Input/output contracts

  • Edge cases

4. Integration Tests (Slower)

  • Module interactions

  • Real resources

  • End-to-end workflows

5. Policy Tests (Continuous)

  • Security compliance

  • Cost controls

  • Organizational standards


Input Validation

Validate inputs before Terraform even runs.

Basic Validation

Test:

Error:

Complex Validation Rules

Port range:

String format:

Email address:

IP address:

Multiple Validation Rules

Precondition and Postcondition Checks

Preconditions (Terraform 1.2+):

Postconditions:


terraform validate

Basic syntax and configuration validation.

What It Checks

  • βœ… Syntax errors

  • βœ… Invalid resource types

  • βœ… Invalid attribute names

  • βœ… Type mismatches

  • βœ… Missing required attributes

  • βœ… Variable validation rules

What It Doesn't Check

  • ❌ Resource conflicts

  • ❌ State consistency

  • ❌ Provider authentication

  • ❌ Network connectivity

  • ❌ Resource quotas

Usage

Success:

Failure:

Common Errors Caught by Validate

1. Invalid resource type:

2. Missing required argument:

3. Type mismatch:

4. Validation rule violation:

Validate in CI/CD


terraform fmt - Code Formatting

Consistent code formatting.

What It Does

  • Standardizes indentation

  • Aligns equals signs

  • Sorts arguments

  • Removes trailing whitespace

  • Ensures consistent style

Usage

Check formatting:

Format files:

Format recursively:

Show diff:

Before and After

Before:

After:

In CI/CD

Pre-commit Hook

.git/hooks/pre-commit:

Make executable:


terraform plan - The First Test

terraform plan is your first integration test.

What Plan Tests

  • βœ… Configuration validity

  • βœ… Provider connectivity

  • βœ… Resource conflicts

  • βœ… State consistency

  • βœ… Dependency resolution

  • βœ… Change impact

Safe Plan Testing

Plan in CI/CD

Plan Assertions

Check plan output for expected changes:


Static Analysis and Linting

Enforce best practices with linting tools.

TFLint

Install:

Usage:

Configuration (.tflint.hcl):

Checkov

Install:

Usage:

What it checks:

  • Security best practices

  • Compliance standards

  • Common misconfigurations

Example output:

tfsec

Install:

Usage:

What it checks:

  • Security vulnerabilities

  • Encryption settings

  • Access controls

  • Network exposure

Terrascan

Install:

Usage:

Compliance frameworks:

  • CIS Benchmarks

  • NIST

  • HIPAA

  • GDPR


Unit Testing with Terratest

Test modules in isolation.

What is Terratest?

Terratest is a Go library for testing infrastructure code.

Install Go

Project Structure

Simple Terratest Example

Module (main.tf):

Test (test/module_test.go):

Initialize:

Run test:

Complex Test Example


Integration Testing

Test multiple modules working together.

Integration Test Structure

Integration Test Example

fixtures/complete/main.tf:

test/integration_test.go:


Contract Testing

Test module interfaces remain stable.

What is Contract Testing?

Ensures modules don't break their public interface (inputs/outputs).

Contract Test Example


Policy as Code

Enforce organizational policies automatically.

Open Policy Agent (OPA)

Install:

Policy (policy.rego):

Test policy:

Sentinel (Terraform Cloud)

Policy (sentinel.hcl):

Policy rule (enforce-mandatory-tags.sentinel):


Real-World Example: Comprehensive Testing Pipeline

Complete testing setup for production.

Project Structure

Validation Script

scripts/validate.sh:

Linting Script

scripts/lint.sh:

Test Script

scripts/test.sh:

Makefile

GitHub Actions Workflow

.github/workflows/terraform-ci.yml:


CI/CD for Terraform

GitOps Workflow

spinner

Pipeline Stages

Stage 1: Pull Request Validation

Stage 2: Merge to Main (Deploy)


Test-Driven Development (TDD) for Infrastructure

Write tests before writing infrastructure code.

TDD Process

  1. Write failing test

  2. Write minimal code to pass

  3. Refactor

  4. Repeat

Example: TDD for a Module

Step 1: Write test first (Red)

Run: Test fails (module doesn't exist yet)

Step 2: Write minimal code (Green)

Run: Test passes

Step 3: Refactor

Add validation, better structure, documentation.

Step 4: Add more tests, repeat


Common Testing Patterns

Pattern 1: Parameterized Tests

Pattern 2: Fixture-Based Testing

Pattern 3: Golden File Testing

Compare output to expected "golden" file:


Testing Best Practices

1. Test Pyramid

Many: Fast, cheap unit tests Some: Medium-speed integration tests Few: Slow, expensive end-to-end tests

2. Test What Matters

Don't test Terraform itselfβ€”test your logic:

Good:

Bad:

3. Parallel Tests

4. Clean Up

Always destroy test infrastructure:

5. Use Unique Names

Avoid conflicts between parallel tests:

6. Fast Feedback

Run fast tests first:

7. Test in Isolation

Each test should be independent:


What I Learned About Testing Infrastructure

1. Testing Prevents Disasters

That Wednesday morning incident taught me: untested infrastructure code is production roulette.

Tests catch errors before they reach production.

2. Validation is Your First Line of Defense

Input validation catches 80% of errors before Terraform even runs.

Always validate inputs with clear error messages.

3. The Test Pyramid Applies to Infrastructure

  • Many fast validation/lint checks

  • Some unit tests

  • Few integration tests

Don't skip the fast checks to write complex tests.

4. terraform plan IS a Test

Treat plan as your integration test.

If plan looks wrong, something is wrong.

5. Policy as Code Prevents Accidents

Automated policy checks prevent:

  • Security violations

  • Compliance issues

  • Cost overruns

  • Accidental deletions

6. CI/CD Makes Testing Automatic

Manual testing gets skipped.

Automated testing in CI/CD ensures every change is tested.

7. TDD Works for Infrastructure

Writing tests first clarifies what you're building.

It's slower initially but faster overall.


Next Steps

Congratulations! You've mastered Terraform testing:

βœ… Input validation βœ… terraform validate and fmt βœ… Static analysis and linting βœ… Unit testing with Terratest βœ… Integration testing βœ… Policy as code βœ… Comprehensive testing pipeline βœ… CI/CD for Terraform βœ… TDD for infrastructure

Practice Exercises

Exercise 1: Add Validation

Exercise 2: Set Up Linting

Exercise 3: Write Unit Tests

Coming Up Next

In Article 9: Security and Secrets Management - Protecting Your Infrastructure, we'll explore:

  • Securing Terraform state

  • Secret management strategies

  • HashiCorp Vault integration

  • Encryption patterns

  • Security scanning

  • Compliance and auditing

Testing ensures quality. Security ensures safety.


This Week's Challenge: Add comprehensive testing to your Terraform projects. Set up validation, linting, and at least one automated test. Integrate into CI/CD.

See you in Article 9! πŸ§ͺ


"Testing infrastructure felt optional until that Wednesday morning. Now, every line of Terraform code gets validated, linted, and tested before it touches production. No exceptions." - Me, converted to infrastructure testing

Last updated