Overview
Manual testing is essential when you are just starting out, but it does not scale. The moment your infrastructure grows beyond what one person can test in an afternoon, you need automated tests. Infrastructure that is tested automatically on every commit is infrastructure you can deploy with absolute confidence.
In this guide, we will break down the three layers of Terraform automated testing Unit, Integration, and End-to-End (E2E), and build a CI/CD pipeline that runs them automatically using GitHub Actions and secure AWS OIDC authentication.
Prerequisites
To follow along and run this code yourself, you will need:
- An AWS Account, Terraform installed (v1.6.0 or newer for native testing)
- Go installed (v1.21 or newer for Terratest)
- A GitHub account to run the CI/CD pipeline
The Testing Strategy & Tradeoffs
A mature testing strategy doesn't rely on just one tool; it uses a pyramid approach balancing speed, cost, and thoroughness.
1. Unit Tests (terraform test)
Terraform 1.6+ ships with a native testing framework using .tftest.hcl files.
-
How it works: It runs a
terraform planin memory and evaluates your logic, variable validation, and configuration rules. - Tradeoffs: They are lightning-fast (seconds) and completely free because they do not deploy real infrastructure. However, they test less, they cannot catch cloud provider API errors or verify if an EC2 instance actually boots properly.
2. Integration Tests (Terratest)
Integration tests are written in Go using the Gruntwork Terratest library. They test individual, reusable modules in a sandbox.
-
How it works: It runs
terraform apply, waits for the infrastructure to boot, makes real HTTP requests to your Load Balancers to verify the app is running, and strictly enforces aterraform destroyat the end to clean up. - Tradeoffs: Thorough, but slower (5–15 minutes) and incurs minor cloud costs since real AWS resources are provisioned.
3. End-to-End (E2E) Tests (Terratest)
E2E tests validate your final, composed architecture (VPC + DB + App) working together.
- How it works: Deploys your entire production-like stack.
-
Tradeoffs: Highly accurate but very slow (15–30 minutes) and costly. Warning: Never run an E2E test with a
destroycommand against your live production state file. E2E tests should be run in isolated sandbox accounts using randomized naming to prevent collisions.
The Golden Rule: Use all three. Run Unit Tests on every Pull Request for instant feedback. Run Integration Tests when merging to your main branch. Run E2E tests in a sandbox environment before major releases.
Step-by-Step Guide: Running the Tests
Want to see this in action? Clone the repository and try it yourself.
Step 1: Clone the Repository
git clone https://github.com/Vivixell/aws-blue-green-infra/.git
cd aws-blue-green-infra
Step 2: Run the Unit Tests
Navigate to the module directory and run the native Terraform test command. This will validate our strict security group rules and Blue/Green capacity logic without deploying anything.
cd modules/webserver
terraform init
terraform test
You should see a fast, green output indicating all assertions passed.
Step 3: Run the Integration Tests
Integration tests deploy real resources. Ensure you have your AWS credentials configured locally, then execute the Go test suite.
cd ../../test
go mod tidy
# We explicitly target the Integration test to avoid running E2E tests locally
go test -v -timeout 45m -run TestWebserverClusterIntegration ./...
Grab a coffee! This will take about 10 minutes to deploy the VPC and ASG, hit the Load Balancer with HTTP requests, and tear it all back down.
Step 4: The CI/CD Pipeline
Running tests locally is great, but automation is the ultimate goal. In the .github/workflows/terraform-test.yml file, we have a pipeline that ties this all together.
Instead of hardcoding highly privileged AWS Access Keys in GitHub (a major security risk), we use OpenID Connect (OIDC). GitHub requests a temporary, short-lived token directly from AWS.
Here is a snippet of how the pipeline is structured to run Unit Tests on PRs, and Integration tests on merges:
jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- run: terraform init
- run: terraform test
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
if: github.event_name == 'push' # Only runs when merged to main
needs: unit-tests
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- uses: hashicorp/setup-terraform@v3
- uses: actions/setup-go@v4
- run: |
go mod tidy
go test -v -timeout 45m -run TestWebserverClusterIntegration ./...
Conclusion
By implementing terraform test for fast syntax checking, Terratest for real-world validation, and GitHub Actions for continuous integration, you transform your infrastructure from fragile scripts into reliable, enterprise-grade software.
Happy testing!
Top comments (0)