Skip to content

Terraform_modules

This chapter covers creating, using, and managing Terraform modules for reusable infrastructure.

Modules are containers for multiple resources that are used together.

┌─────────────────────────────────────────────────────────────────────────────┐
│ Terraform Module Concept │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Without Modules (Monolithic) │ │
│ │ │ │
│ │ main.tf │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ VPC + Subnets + Security Groups + Instances + RDS │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ With Modules (Modular) │ │
│ │ │ │
│ │ main.tf │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ module │ │ module │ │ module │ │ module │ │ │
│ │ │ "vpc" │ │ "sg" │ │ "ec2" │ │ "rds" │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Benefits: │
│ ✓ Reusability - Write once, use many times │
│ ✓ Organization - Better structure and readability │
│ ✓ Abstraction - Hide complexity │
│ ✓ Versioning - Pin specific versions │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Module Directory Structure │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ module-name/ │ │
│ │ │ │
│ │ ├── main.tf # Resources │ │
│ │ ├── variables.tf # Input variables │ │
│ │ ├── outputs.tf # Output values │ │
│ │ ├── README.md # Documentation │ │
│ │ └── versions.tf # Provider versions │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
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}-vpc"
}
)
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.cidr_block, 8, count.index)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = merge(
var.tags,
{
Name = "${var.name}-public-${var.availability_zones[count.index]}"
}
)
}
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.cidr_block, 8, count.index + 10)
availability_zone = var.availability_zones[count.index]
tags = merge(
var.tags,
{
Name = "${var.name}-private-${var.availability_zones[count.index]}"
}
)
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(
var.tags,
{
Name = "${var.name}-igw"
}
)
}
modules/vpc/variables.tf
variable "name" {
description = "Name prefix for resources"
type = string
}
variable "cidr_block" {
description = "VPC CIDR block"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "Availability zones"
type = list(string)
default = ["us-east-1a", "us-east-1b"]
}
variable "enable_dns_hostnames" {
description = "Enable DNS hostnames"
type = bool
default = true
}
variable "enable_dns_support" {
description = "Enable DNS support"
type = bool
default = true
}
variable "tags" {
description = "Tags to apply to resources"
type = map(string)
default = {}
}
modules/vpc/outputs.tf
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "vpc_cidr" {
description = "CIDR block of the 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 "igw_id" {
description = "ID of the Internet Gateway"
value = aws_internet_gateway.main.id
}
┌─────────────────────────────────────────────────────────────────────────────┐
│ Module Sources │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Module Source Types │ │
│ │ │ │
│ │ Local: module "vpc" { │ │
│ │ source = "./modules/vpc" │ │
│ │ } │ │
│ │ │ │
│ │ Terraform Registry: module "vpc" { │ │
│ │ source = "terraform-aws-modules/vpc/aws" │ │
│ │ version = "3.0.0" │ │
│ │ } │ │
│ │ │ │
│ │ Git: module "vpc" { │ │
│ │ source = "github.com/org/repo//modules/vpc" │ │
│ │ } │ │
│ │ │ │
│ │ S3/GCS: module "vpc" { │ │
│ │ source = "s3::https://bucket.s3.amazonaws.com/modules/vpc" │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
main.tf
provider "aws" {
region = "us-east-1"
}
module "vpc" {
source = "./modules/vpc"
name = "prod"
cidr_block = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
tags = {
Project = "MyApp"
Environment = "Production"
}
}
# Use module outputs
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
subnet_id = module.vpc.public_subnet_ids[0]
tags = {
Name = "WebServer"
}
}
# Using official AWS VPC module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = false
tags = {
Terraform = "true"
Environment = "prod"
}
}
# RDS Module
module "rds" {
source = "terraform-aws-modules/rds/aws"
version = "6.0.0"
identifier = "my-database"
engine = "mysql"
engine_version = "8.0"
family = "mysql8.0"
allocated_storage = 20
max_allocated_storage = 100
instance_class = "db.t3.micro"
multi_az = true
username = "admin"
password = "mypassword"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
backup_retention_period = 7
skip_final_snapshot = false
}
┌─────────────────────────────────────────────────────────────────────────────┐
│ Module Composition Example │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Root Module │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ main.tf │ │
│ │ │ │
│ │ module "network" { │ │
│ │ source = "./modules/network" │ │
│ │ } │ │
│ │ │ │
│ │ module "compute" { │ │
│ │ source = "./modules/compute" │ │
│ │ vpc_id = module.network.vpc_id │ │
│ │ subnet_ids = module.network.private_subnet_ids │ │
│ │ security_group = module.network.default_sg │ │
│ │ } │ │
│ │ │ │
│ │ module "database" { │ │
│ │ source = "./modules/database" │ │
│ │ vpc_id = module.network.vpc_id │ │
│ │ subnet_ids = module.network.private_subnet_ids │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Benefits: │
│ ✓ Independent modules can be developed │
│ ✓ Outputs from one module feed into others │
│ ✓ Team can work on different modules │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
# Pin exact version
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
}
# Version constraints
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3.0" # Any 3.x version
name = "my-vpc"
cidr = "10.0.0.0/16"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = ">= 3.0.0, < 4.0.0" # Range
name = "my-vpc"
cidr = "10.0.0.0/16"
}
# Root module
module "app" {
source = "./modules/app"
vpc_id = module.network.vpc_id
subnet_ids = module.network.private_subnet_ids
}
# modules/app/main.tf
module "web" {
source = "./web"
vpc_id = var.vpc_id
subnet_ids = var.subnet_ids
}
module "api" {
source = "./api"
vpc_id = var.vpc_id
subnet_ids = var.subnet_ids
}
Terminal window
# Get module path
terraform console > module.web.module.ec2.aws_instance.main
# List all resources including modules
terraform state list
# Show module output
terraform output -module=module.vpc
# Verbose output
TF_LOG=DEBUG terraform apply

In this chapter, you learned:

  • Module Concept: What are modules and why use them
  • Creating Modules: Structure, variables, outputs
  • Using Modules: Local, registry, git sources
  • Registry Modules: Using pre-built modules
  • Module Composition: Connecting modules together
  • Versioning: Pinning and constraining versions