Terraform Module Design Patterns
Proven patterns for designing reusable, composable Terraform modules for enterprise infrastructure.
Well-designed Terraform modules are the foundation of scalable infrastructure as code. Here are the patterns I use across enterprise deployments.
Module Composition
Favor small, focused modules over monolithic ones:
modules/
├── vpc/ # Networking foundation
├── eks/ # Kubernetes cluster
├── rds/ # Database layer
└── monitoring/ # Observability stack
Each module should do one thing well and expose clear interfaces.
Variable Design
Use objects for related configuration:
variable "vpc_config" {
description = "VPC configuration"
type = object({
cidr_block = string
availability_zones = list(string)
enable_nat_gateway = bool
single_nat_gateway = bool
})
}Provide sensible defaults for optional settings. Required variables should have no defaults.
Output Strategy
Export everything consumers might need:
output "vpc_id" {
description = "The ID of the VPC"
value = aws_vpc.main.id
}
output "private_subnet_ids" {
description = "List of private subnet IDs"
value = aws_subnet.private[*].id
}
output "nat_gateway_ips" {
description = "NAT Gateway public IPs"
value = aws_nat_gateway.main[*].public_ip
}State Management
Use remote state with locking:
terraform {
backend "s3" {
bucket = "terraform-state-prod"
key = "networking/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}Testing Modules
Use Terratest for module validation:
func TestVPCModule(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../modules/vpc",
Vars: map[string]interface{}{
"cidr_block": "10.0.0.0/16",
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vpcId := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcId)
}Conclusion
Invest time in module design upfront. Good modules reduce duplication, enforce standards, and make infrastructure changes predictable and reviewable.
Related Articles
Trunk-Based Development with Modern CI/CD
Ship faster with short-lived branches, feature flags, and continuous integration on the main trunk.
Docker Multi-Stage Builds in CI/CD Pipelines
Shrink images, speed up pipelines, and improve security with multi-stage Dockerfiles in GitLab CI.
Vercel + GitLab: CI/CD for Next.js Portfolios
Combine GitLab for source control and optional CI checks with Vercel for zero-config Next.js deployment.