Tips and Tricks – Use Terraform Modules for Reusable Infrastructure

After managing infrastructure for 50+ AWS accounts, I’ve learned that Terraform modules are the foundation of scalable infrastructure as code. This guide shows how to build reusable Terraform modules that actually work in production.

1. Why Terraform Modules?

Without modules, Terraform becomes unmaintainable:

  • Copy-paste proliferation: Same code duplicated across projects
  • Inconsistent configs: Slight differences cause production issues
  • Hard to update: Changing 20 copies of VPC code manually
  • No standards enforcement: Each team implements differently

2. Module Structure

# Standard Terraform module layout
terraform-aws-vpc/
├── main.tf           # Primary resource definitions
├── variables.tf      # Input variables
├── outputs.tf        # Output values
├── versions.tf       # Provider version constraints
├── README.md         # Documentation
└── examples/         # Usage examples
    └── complete/
        ├── main.tf
        └── terraform.tfvars

3. Building a Production-Ready Module

3.1 VPC Module Example

# modules/vpc/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = var.enable_dns_hostnames
  enable_dns_support   = var.enable_dns_support

  tags = merge(
    var.tags,
    {
      Name = var.name
    }
  )
}

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidrs)

  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = merge(
    var.tags,
    {
      Name = "${var.name}-private-${var.availability_zones[count.index]}"
      Tier = "private"
    }
  )
}

# modules/vpc/variables.tf
variable "name" {
  description = "Name prefix for VPC resources"
  type        = string
}

variable "cidr_block" {
  description = "CIDR block for VPC"
  type        = string
  
  validation {
    condition     = can(cidrhost(var.cidr_block, 0))
    error_message = "Must be valid IPv4 CIDR block"
  }
}

variable "private_subnet_cidrs" {
  description = "CIDR blocks for private subnets"
  type        = list(string)
}

variable "tags" {
  description = "Tags to apply to all resources"
  type        = map(string)
  default     = {}
}

# modules/vpc/outputs.tf
output "vpc_id" {
  description = "ID of the VPC"
  value       = aws_vpc.main.id
}

output "private_subnet_ids" {
  description = "IDs of private subnets"
  value       = aws_subnet.private[*].id
}

3.2 Using the Module

# main.tf
module "vpc" {
  source = "git::https://github.com/myorg/terraform-aws-vpc.git?ref=v1.2.0"

  name                 = "production-vpc"
  cidr_block           = "10.0.0.0/16"
  private_subnet_cidrs = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  availability_zones   = ["us-east-1a", "us-east-1b", "us-east-1c"]

  tags = {
    Environment = "production"
    Team        = "platform"
  }
}

4. Production Best Practices

  • Semantic versioning: Tag releases (v1.0.0, v1.1.0)
  • Input validation: Validate variables with validation blocks
  • Comprehensive outputs: Export everything consumers might need
  • Examples directory: Show common usage patterns
  • Terraform Registry: Publish to private registry

5. Module Composition

# Compose modules for complex infrastructure
module "vpc" {
  source = "./modules/vpc"
  name   = "production"
  # ... config
}

module "eks_cluster" {
  source = "./modules/eks"
  
  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids
  # ... config
}

module "rds" {
  source = "./modules/rds"
  
  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids
  # ... config
}

6. Conclusion

Terraform modules enable reusable, consistent infrastructure across environments. Essential for teams managing multi-account cloud deployments.

Written for platform engineers and DevOps teams building infrastructure at scale.


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.