Testing Guide
Comprehensive testing framework for AWS Control Tower Landing Zone automation.
Overview
This project includes multiple layers of testing:
- Terraform Validation - Syntax and configuration validation
- Unit Tests - Terratest-based infrastructure tests
- Policy Tests - OPA (Open Policy Agent) policy validation
- Security Scanning - TFSec and Checkov security analysis
- Integration Tests - End-to-end deployment validation
Prerequisites
Required Tools
# Terraform
terraform --version # >= 1.5.0
# Go (for Terratest)
go version # >= 1.21
# OPA (Open Policy Agent)
opa version # >= 0.60.0
# AWS CLI
aws --version # >= 2.0
Optional Tools
# TFLint - Terraform linting
brew install tflint
# TFSec - Security scanning
brew install tfsec
# Checkov - Security and compliance scanning
pip install checkov
# jq - JSON processing
brew install jq
Installation
Install OPA
# macOS
brew install opa
# Linux
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64
chmod +x opa
sudo mv opa /usr/local/bin/
# Verify installation
opa version
Install Go (for Terratest)
# macOS
brew install go
# Linux
wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
# Verify installation
go version
Install Terratest Dependencies
cd tests/terraform
go mod download
go mod tidy
Running Tests
Quick Start - Run All Tests
# Run complete validation suite
make test-all
# Or use the validation script
./scripts/validate-all.sh
Individual Test Suites
1. Terraform Validation
# Format check
terraform fmt -check -recursive
# Validate configuration
terraform init -backend=false
terraform validate
# Using Make
make validate
2. Unit Tests (Terratest)
# Run all unit tests
./scripts/run-terraform-tests.sh
# Run specific test
cd tests/terraform
go test -v -run TestControlTowerDeployment
# Run with timeout
go test -v -timeout 30m
# Using Make
make test-unit
3. OPA Policy Tests
# Run OPA unit tests
./scripts/run-opa-tests.sh
# Run specific test
opa test policies/opa/ -v -r test_s3_bucket_encryption_required
# Validate against Terraform plan
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
opa eval --data policies/opa/terraform.rego --input tfplan.json 'data.terraform.controltower.deny'
# Using Make
make test-opa
4. Security Scanning
# TFSec scan
tfsec . --soft-fail
# Checkov scan
checkov -d . --quiet --compact
# Using Make
make security-scan
5. Linting
# TFLint
tflint --init
tflint
# Using Make
make lint
Test Structure
Terratest Unit Tests
Located in tests/terraform/main_test.go:
TestControlTowerDeployment - Tests main deployment
TestOrganizationalUnits - Tests OU module
TestSCPPolicies - Tests SCP module
TestSecurityModule - Tests security module
TestLoggingModule - Tests logging module
TestNetworkingModule - Tests networking module
TestVariableValidation - Tests input validation
TestOutputs - Tests output values
OPA Policy Tests
Located in policies/opa/:
terraform.rego- Policy definitions (50+ rules)terraform_test.rego- Test cases (30+ tests)
Policy categories:
- KMS Encryption
- S3 Security
- EC2 Security
- RDS Security
- Network Security
- IAM Security
- CloudTrail
- GuardDuty
- Security Hub
- AWS Config
- Tagging
- Load Balancers
- Lambda
- ElastiCache
- Secrets Manager
Writing Tests
Adding Terratest Tests
func TestNewFeature(t *testing.T) {
t.Parallel()
terraformOptions := &terraform.Options{
TerraformDir: "../../modules/my-module",
Vars: map[string]interface{}{
"variable_name": "value",
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// Assertions
output := terraform.Output(t, terraformOptions, "output_name")
assert.Equal(t, "expected_value", output)
}
Adding OPA Policies
# Policy rule
deny[msg] {
resource := resources_by_type("aws_resource_type")[_]
not resource.values.required_property
msg := sprintf("Resource '%s' must have required_property", [resource.address])
}
# Test case
test_resource_property_required {
deny["Resource 'aws_resource_type.test' must have required_property"] with input as {
"planned_values": {
"root_module": {
"resources": [
{
"address": "aws_resource_type.test",
"type": "aws_resource_type",
"values": {}
}
]
}
}
}
}
CI/CD Integration
GitHub Actions
The project includes .github/workflows/terraform-ci.yml:
- Runs on: push, pull_request
- Steps:
1. Terraform format check
2. Terraform validation
3. TFSec security scan
4. OPA policy tests
5. Terratest unit tests (on main branch)
Pre-commit Hooks
Create .git/hooks/pre-commit:
#!/bin/bash
set -e
echo "Running pre-commit checks..."
# Format check
terraform fmt -check -recursive || {
echo "Run: terraform fmt -recursive"
exit 1
}
# Validation
terraform validate
# OPA tests
./scripts/run-opa-tests.sh
echo "✓ Pre-commit checks passed"
Make it executable:
chmod +x .git/hooks/pre-commit
Test Data
Example Terraform Plan JSON
Create tests/fixtures/valid-plan.json:
{
"planned_values": {
"root_module": {
"resources": [
{
"address": "aws_s3_bucket.example",
"type": "aws_s3_bucket",
"values": {
"bucket": "example-bucket",
"tags": {
"Environment": "production",
"ManagedBy": "terraform",
"Project": "control-tower"
}
}
},
{
"address": "aws_s3_bucket_server_side_encryption_configuration.example",
"type": "aws_s3_bucket_server_side_encryption_configuration",
"values": {
"bucket": "aws_s3_bucket.example"
}
}
]
}
}
}
Test against it:
opa eval --data policies/opa/terraform.rego --input tests/fixtures/valid-plan.json 'data.terraform.controltower.deny'
Troubleshooting
Common Issues
OPA Tests Fail
# Check OPA installation
opa version
# Run with verbose output
opa test policies/opa/ -v
# Check specific test
opa test policies/opa/ -v -r test_name
Terratest Timeout
# Increase timeout
go test -v -timeout 60m
# Run specific test
go test -v -run TestName -timeout 30m
Go Module Issues
cd tests/terraform
go mod tidy
go clean -modcache
go mod download
TFSec False Positives
Add exceptions in .tfsec/config.yml:
exclude:
- aws-s3-enable-bucket-logging
Or inline:
resource "aws_s3_bucket" "example" {
#tfsec:ignore:aws-s3-enable-bucket-logging
bucket = "example"
}
Best Practices
Test Organization
- Unit tests - Test individual modules in isolation
- Integration tests - Test module interactions
- End-to-end tests - Test complete deployment
- Policy tests - Validate compliance and security
Test Naming
- Use descriptive names:
TestSecurityModuleKMSEncryption - Follow pattern:
Test<Module><Feature> - OPA tests:
test_<resource>_<requirement>
Test Data
- Use realistic but non-sensitive data
- Create fixtures for common scenarios
- Document test data requirements
Performance
- Use
t.Parallel()for independent tests - Set appropriate timeouts
- Clean up resources in
deferstatements
Coverage
- Test happy paths and error cases
- Validate all critical security controls
- Test variable validation logic
- Verify output values
Continuous Improvement
Adding New Tests
When adding features:
- Write Terratest unit test
- Add OPA policy if security-related
- Update test documentation
- Run full test suite
- Update CI/CD pipeline if needed
Test Maintenance
- Review and update tests with code changes
- Remove obsolete tests
- Keep test data current
- Monitor test execution time
Resources
- Terratest Documentation
- OPA Documentation
- TFSec Rules
- Checkov Policies
- Terraform Testing Best Practices
Support
For testing issues:
- Check this documentation
- Review test output and logs
- Verify tool versions
- Check GitHub issues
- Contact the team
Note: Always run tests before deploying to production. The test suite helps catch issues early and ensures compliance with security policies.