Terraform Modules
Module Structure
modules/
└── vpc/
├── main.tf # Resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Required providers/versions
└── README.md
# variables.tf
variable "vpc_cidr" {
type = string
description = "CIDR block for the VPC"
default = "10.0.0.0/16"
validation {
condition = can(cidrhost(var.vpc_cidr, 0))
error_message = "Must be a valid CIDR block."
}
}
variable "tags" {
type = map(string)
description = "Resource tags"
default = {}
}
variable "enable_nat_gateway" {
type = bool
default = true
}
Module Sources
# Local path
module "vpc" {
source = "./modules/vpc"
}
# Terraform Registry (public)
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
}
# GitHub
module "vpc" {
source = "github.com/my-org/terraform-modules//vpc?ref=v2.0.0"
}
# Git over HTTPS
module "vpc" {
source = "git::https://github.com/my-org/modules.git//vpc?ref=v2.0.0"
}
# Git over SSH
module "vpc" {
source = "git::ssh://git@github.com/my-org/modules.git//vpc?ref=main"
}
# Terraform Cloud / Enterprise private registry
module "vpc" {
source = "app.terraform.io/my-org/vpc/aws"
version = "~> 2.0"
}
# S3 bucket
module "vpc" {
source = "s3::https://s3-eu-west-1.amazonaws.com/my-modules/vpc.zip"
}
Calling a Module
# main.tf (root module)
module "production_vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
private_subnets = ["10.0.10.0/24", "10.0.11.0/24"]
enable_nat_gateway = true
tags = {
Environment = "production"
Team = "platform"
}
}
# Use module outputs
resource "aws_security_group" "app" {
vpc_id = module.production_vpc.vpc_id
}
output "vpc_id" {
value = module.production_vpc.vpc_id
}
# Pass provider to module
module "replica" {
source = "./modules/storage"
providers = {
aws = aws.us_west
}
}
Outputs
# modules/vpc/outputs.tf
output "vpc_id" {
description = "The ID of the VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "List of public subnet IDs"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "List of private subnet IDs"
value = aws_subnet.private[*].id
sensitive = false
}
output "nat_gateway_ip" {
description = "Elastic IP for NAT gateway"
value = try(aws_eip.nat[0].public_ip, null)
}
Module Composition & Count/For_each
# Create multiple instances of a module
variable "environments" {
default = {
dev = { cidr = "10.0.0.0/16", az_count = 2 }
staging = { cidr = "10.1.0.0/16", az_count = 2 }
prod = { cidr = "10.2.0.0/16", az_count = 3 }
}
}
module "vpcs" {
for_each = var.environments
source = "./modules/vpc"
name = each.key
vpc_cidr = each.value.cidr
az_count = each.value.az_count
}
# Access specific instance output
output "prod_vpc_id" {
value = module.vpcs["prod"].vpc_id
}
# Combine outputs from all instances
output "all_vpc_ids" {
value = { for k, v in module.vpcs : k => v.vpc_id }
}