Terraform Modules Skill
Design and build production-ready, reusable Terraform modules following industry best practices.
Module Structure
my-module/
├── main.tf # Primary resources
├── variables.tf # Input declarations
├── outputs.tf # Output declarations
├── versions.tf # Provider requirements
├── locals.tf # Computed values
├── README.md # Documentation
├── examples/
│ ├── basic/
│ └── complete/
└── tests/
└── module_test.go
Quick Start Template
versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0, < 6.0"
}
}
}
variables.tf
variable "name" {
type = string
description = "Resource name prefix"
validation {
condition = length(var.name) <= 32
error_message = "Name must be 32 characters or less."
}
}
variable "environment" {
type = string
description = "Deployment environment"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Must be dev, staging, or prod."
}
}
variable "tags" {
type = map(string)
description = "Resource tags"
default = {}
}
main.tf
locals {
common_tags = merge(var.tags, {
Module = "my-module"
Environment = var.environment
})
}
resource "aws_resource" "main" {
name = var.name
tags = local.common_tags
}
outputs.tf
output "id" {
description = "Resource ID"
value = aws_resource.main.id
}
output "arn" {
description = "Resource ARN"
value = aws_resource.main.arn
}
Input Patterns
Required vs Optional
# Required - no default
variable "vpc_id" {
type = string
description = "VPC ID (required)"
}
# Optional with default
variable "instance_type" {
type = string
description = "EC2 instance type"
default = "t3.micro"
}
# Optional nullable
variable "kms_key_arn" {
type = string
description = "KMS key ARN (null = use default)"
default = null
}
Complex Objects
variable "scaling_config" {
type = object({
min_size = number
max_size = number
desired_capacity = optional(number)
metrics = optional(list(string), ["CPUUtilization"])
})
default = {
min_size = 1
max_size = 3
}
validation {
condition = var.scaling_config.min_size <= var.scaling_config.max_size
error_message = "min_size must be <= max_size."
}
}
Output Patterns
Conditional Outputs
output "nat_gateway_ips" {
description = "NAT Gateway IPs (empty if not created)"
value = var.enable_nat ? aws_eip.nat[*].public_ip : []
}
Structured Outputs
output "cluster_config" {
description = "Cluster configuration for kubectl"
value = {
endpoint = aws_eks_cluster.main.endpoint
ca_certificate = aws_eks_cluster.main.certificate_authority[0].data
name = aws_eks_cluster.main.name
}
}
Sensitive Outputs
output "credentials" {
description = "Database credentials"
value = {
username = aws_db_instance.main.username
password = random_password.db.result
}
sensitive = true
}
Module Composition
Root Module Calling Child Modules
module "vpc" {
source = "./modules/vpc"
name = var.project_name
cidr_block = var.vpc_cidr
environment = var.environment
}
module "eks" {
source = "./modules/eks"
cluster_name = var.project_name
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
depends_on = [module.vpc]
}
Using Registry Modules
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2" # Pin exact version
name = var.project_name
cidr = var.vpc_cidr
azs = data.aws_availability_zones.available.names
private_subnets = var.private_subnet_cidrs
public_subnets = var.public_subnet_cidrs
}
for_each with Modules
module "services" {
source = "./modules/ecs-service"
for_each = var.services
name = each.key
container_port = each.value.port
cpu = each.value.cpu
memory = each.value.memory
cluster_id = aws_ecs_cluster.main.id
}
Versioning
Semantic Versioning
# Major: Breaking changes
# Minor: New features, backward compatible
# Patch: Bug fixes
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # >= 5.0.0, < 6.0.0
}
module "internal" {
source = "git::https://github.com/org/modules.git//vpc?ref=v2.3.0"
}
Version Constraints
version = "5.1.2" # Exact version
version = ">= 5.0" # Minimum version
version = "~> 5.0" # Minor version range
version = ">= 5.0, < 6.0" # Explicit range
Troubleshooting
| Error |
Cause |
Solution |
Module not found |
Wrong path/URL |
Verify source path |
Unsupported argument |
Version mismatch |
Check changelog |
Cycle detected |
Circular references |
Restructure dependencies |
Count/for_each conflict |
Both used together |
Use only one |
Debug Commands
# Initialize modules
terraform init -upgrade
# Show module tree
terraform providers
# Validate module inputs
terraform validate
Usage
Skill("terraform-modules")
Related
- Agent: 02-terraform-modules (PRIMARY_BOND)