| name | iac-terraform-modules-eng |
| description | Build reusable Terraform and OpenTofu modules and provider configurations for multi-cloud infrastructure, Kubernetes, CI/CD, databases, networking, security, observability, and virtualization. Use when creating infrastructure modules, generating module documentation with terraform-docs, standardizing provisioning, migrating from Terraform to OpenTofu, or implementing IaC patterns across 40+ providers. |
Terraform & OpenTofu Module Library
Production-ready Terraform and OpenTofu module patterns for multi-cloud infrastructure and 40+ providers including AWS, Azure, GCP, Kubernetes, Cloudflare, Vault, Grafana, and more. Modules are compatible with both Terraform and OpenTofu.
Purpose
Create reusable, well-tested Terraform and OpenTofu modules for common infrastructure patterns across cloud providers, SaaS platforms, and on-premises virtualization. Support both Terraform (HashiCorp BSL) and OpenTofu (MPL 2.0) workflows.
When to Use
- Build reusable infrastructure components for AWS, Azure, GCP
- Configure Kubernetes clusters with Helm, Talos, or managed services
- Set up CI/CD pipelines with GitHub, GitLab, or Buildkite
- Manage databases: MongoDB Atlas, CockroachDB, Elastic, Pinecone
- Configure networking: Cloudflare, DNS, CDN, VPN (ZeroTier)
- Implement secrets management with HashiCorp Vault
- Deploy to PaaS platforms: Vercel, Heroku, DigitalOcean, Linode, Vultr
- Configure observability with Grafana stack
- Manage on-premises infrastructure: vSphere, VMC, Proxmox
- Orchestrate workflows with Ansible, Kestra, or Prefect
- Implement feature flags with Flagsmith
- Establish organizational Terraform/OpenTofu standards
- Migrate existing Terraform configurations to OpenTofu
- Leverage OpenTofu-specific features (early evaluation, provider-defined functions)
- Generate and maintain module documentation (terraform-docs)
Module Structure
terraform-modules/
├── aws/
│ ├── vpc/
│ ├── eks/
│ ├── rds/
│ └── s3/
├── azure/
│ ├── vnet/
│ ├── aks/
│ └── storage/
└── gcp/
├── vpc/
├── gke/
└── cloud-sql/
Standard Module Pattern
module-name/
├── main.tf # Main resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Provider versions
├── README.md # Documentation (generated by terraform-docs)
├── .terraform-docs.yml # terraform-docs configuration
├── examples/ # Usage examples
│ └── complete/
│ ├── main.tf
│ └── variables.tf
└── tests/ # Terratest files
└── module_test.go
AWS VPC Module Example
main.tf:
locals {
nat_gateway_count = var.create_nat_gateway ? (var.single_nat_gateway ? 1 : length(var.availability_zones)) : 0
}
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(
{ Name = var.name },
var.tags
)
}
# Public Subnets
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = merge(
{
Name = "${var.name}-public-${count.index + 1}"
Tier = "public"
},
var.tags
)
}
# Private Subnets
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(
{
Name = "${var.name}-private-${count.index + 1}"
Tier = "private"
},
var.tags
)
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
count = var.create_internet_gateway ? 1 : 0
vpc_id = aws_vpc.main.id
tags = merge(
{ Name = "${var.name}-igw" },
var.tags
)
}
# Elastic IPs for NAT Gateways
resource "aws_eip" "nat" {
count = local.nat_gateway_count
domain = "vpc"
tags = merge(
{ Name = "${var.name}-nat-eip-${count.index + 1}" },
var.tags
)
depends_on = [aws_internet_gateway.main]
}
# NAT Gateways
resource "aws_nat_gateway" "main" {
count = local.nat_gateway_count
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = merge(
{ Name = "${var.name}-nat-${count.index + 1}" },
var.tags
)
depends_on = [aws_internet_gateway.main]
}
# Public Route Table
resource "aws_route_table" "public" {
count = var.create_internet_gateway ? 1 : 0
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main[0].id
}
tags = merge(
{ Name = "${var.name}-public-rt" },
var.tags
)
}
resource "aws_route_table_association" "public" {
count = length(var.public_subnet_cidrs)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public[0].id
}
# Private Route Tables (one per NAT Gateway)
resource "aws_route_table" "private" {
count = local.nat_gateway_count > 0 ? length(var.private_subnet_cidrs) : 0
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[var.single_nat_gateway ? 0 : count.index].id
}
tags = merge(
{ Name = "${var.name}-private-rt-${count.index + 1}" },
var.tags
)
}
resource "aws_route_table_association" "private" {
count = local.nat_gateway_count > 0 ? length(var.private_subnet_cidrs) : 0
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
variables.tf:
variable "name" {
description = "Name of the VPC"
type = string
}
variable "cidr_block" {
description = "CIDR block for VPC"
type = string
validation {
condition = can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", var.cidr_block))
error_message = "CIDR block must be valid IPv4 CIDR notation."
}
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
}
variable "public_subnet_cidrs" {
description = "CIDR blocks for public subnets"
type = list(string)
default = []
}
variable "private_subnet_cidrs" {
description = "CIDR blocks for private subnets"
type = list(string)
default = []
}
variable "enable_dns_hostnames" {
description = "Enable DNS hostnames in VPC"
type = bool
default = true
}
variable "enable_dns_support" {
description = "Enable DNS support in VPC"
type = bool
default = true
}
variable "create_internet_gateway" {
description = "Create an Internet Gateway for public subnets"
type = bool
default = true
}
variable "create_nat_gateway" {
description = "Create NAT Gateway(s) for private subnets"
type = bool
default = true
}
variable "single_nat_gateway" {
description = "Use a single NAT Gateway for all AZs (cost savings)"
type = bool
default = false
}
variable "tags" {
description = "Additional tags"
type = map(string)
default = {}
}
versions.tf:
# Compatible with both Terraform >= 1.5.0 and OpenTofu >= 1.6.0
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
versions.tofu.tf (OpenTofu-specific, optional):
# Use for OpenTofu-specific features like early evaluation
terraform {
required_version = ">= 1.8.0"
}
outputs.tf:
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "vpc_cidr_block" {
description = "CIDR block of VPC"
value = aws_vpc.main.cidr_block
}
output "public_subnet_ids" {
description = "IDs of public subnets"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "IDs of private subnets"
value = aws_subnet.private[*].id
}
output "internet_gateway_id" {
description = "ID of the Internet Gateway"
value = try(aws_internet_gateway.main[0].id, null)
}
output "nat_gateway_ids" {
description = "IDs of NAT Gateways"
value = aws_nat_gateway.main[*].id
}
output "public_route_table_id" {
description = "ID of public route table"
value = try(aws_route_table.public[0].id, null)
}
output "private_route_table_ids" {
description = "IDs of private route tables"
value = aws_route_table.private[*].id
}
Best Practices
- Use semantic versioning for modules
- Document all variables with descriptions
- Provide examples in examples/ directory
- Use validation blocks for input validation
- Output important attributes for module composition
- Pin provider versions in versions.tf
- Use locals for computed values
- Implement conditional resources with count/for_each
- Test modules with Terratest
- Tag all resources consistently
OpenTofu Compatibility
OpenTofu is an open-source fork of Terraform (MPL 2.0 licensed) that maintains HCL compatibility while adding new features. All modules in this library work with both tools.
Key Differences
| Feature | Terraform | OpenTofu |
|---|---|---|
| License | BSL 1.1 | MPL 2.0 |
| State Encryption | Enterprise only | Built-in (1.7+) |
| Early Evaluation | No | Yes (1.8+) |
| Provider-defined Functions | Limited | Extended support |
| CLI Command | terraform |
tofu |
| Registry | registry.terraform.io | registry.opentofu.org |
Migration from Terraform to OpenTofu
# Install OpenTofu
brew install opentofu
# Initialize (uses existing .terraform.lock.hcl)
tofu init
# Validate configuration
tofu validate
# Plan (state file is compatible)
tofu plan
# Apply
tofu apply
OpenTofu-Specific Features
State Encryption (1.7+):
terraform {
encryption {
key_provider "pbkdf2" "main" {
passphrase = var.state_encryption_passphrase
}
method "aes_gcm" "main" {
keys = key_provider.pbkdf2.main
}
state {
method = method.aes_gcm.main
}
}
}
Early Evaluation (1.8+):
# Variables can be used in backend configuration
terraform {
backend "s3" {
bucket = var.state_bucket # Works in OpenTofu 1.8+
key = var.state_key
region = var.aws_region
}
}
Module Composition
module "vpc" {
source = "../../modules/aws/vpc"
name = "production"
cidr_block = "10.0.0.0/16"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
public_subnet_cidrs = [
"10.0.1.0/24",
"10.0.2.0/24",
"10.0.3.0/24"
]
private_subnet_cidrs = [
"10.0.11.0/24",
"10.0.12.0/24",
"10.0.13.0/24"
]
create_nat_gateway = true
single_nat_gateway = false # HA: one NAT per AZ
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
module "rds" {
source = "../../modules/aws/rds"
identifier = "production-db"
engine = "postgres"
engine_version = "15.4"
instance_class = "db.t3.large"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
tags = {
Environment = "production"
}
}
module "eks" {
source = "../../modules/aws/eks"
cluster_name = "production"
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
node_groups = {
default = {
instance_types = ["t3.large"]
min_size = 2
max_size = 10
desired_size = 3
}
}
tags = {
Environment = "production"
}
}
Module Documentation
Generate README.md automatically from module code using terraform-docs:
# Install terraform-docs
brew install terraform-docs
# Generate documentation
terraform-docs markdown table . > README.md
# Generate with custom config
terraform-docs -c .terraform-docs.yml .
.terraform-docs.yml:
formatter: markdown table
sections:
show:
- header
- inputs
- outputs
- providers
- requirements
content: |-
{{ .Header }}
## Usage
```hcl
module "example" {
source = "path/to/module"
# ... variables
}
{{ .Requirements }} {{ .Providers }} {{ .Inputs }} {{ .Outputs }}
See `references/terraform-docs.md` for complete patterns including pre-commit hooks, CI/CD integration, and custom templates.
## Reference Files
### Module Tooling
- `references/terraform-docs.md` - Module documentation generation, templates, pre-commit hooks
### OpenTofu
- `references/opentofu.md` - OpenTofu migration, state encryption, early evaluation, registry
### Cloud Providers
- `references/aws-modules.md` - AWS module patterns (VPC, EKS, RDS, S3, ALB, Lambda)
- `references/azure-modules.md` - Azure module patterns (VNet, AKS, SQL, Storage)
- `references/gcp-modules.md` - GCP module patterns (VPC, GKE, Cloud SQL, Cloud Run)
### CI/CD & Version Control
- `references/cicd-providers.md` - GitHub, GitLab, Buildkite provider patterns
### Kubernetes & Containers
- `references/kubernetes-providers.md` - Kubernetes, Helm, Talos, Coder provider patterns
### Databases & Data
- `references/database-providers.md` - MongoDB Atlas, CockroachDB, Elastic, Pinecone, Atlas, Airbyte patterns
### Networking & CDN
- `references/networking-providers.md` - Cloudflare, DNS, NS1, ZeroTier, Fastly, Akamai patterns
### Security
- `references/security-providers.md` - HashiCorp Vault, TLS provider patterns
### Utility Providers
- `references/utility-providers.md` - Template, Time, Local, HTTP provider patterns
### Orchestration & Feature Flags
- `references/orchestration-providers.md` - Ansible AAP, Kestra, Prefect, Flagsmith patterns
### Observability
- `references/observability-providers.md` - Grafana, Grafana Cloud, Synthetic Monitoring, Adaptive Metrics
### Platform as a Service
- `references/platform-providers.md` - Vercel, Heroku, DigitalOcean, Linode, Vultr patterns
### Virtualization
- `references/virtualization-providers.md` - vSphere, VMware Cloud, Proxmox patterns
## Testing
```go
// tests/vpc_test.go
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestVPCModule(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../examples/complete",
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vpcID := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcID)
}
Related Skills
multi-cloud-architecture- For architectural decisionscost-optimization- For cost-effective designs