TL;DR
Master Terraform modules to create reusable, maintainable infrastructure components. Learn about module creation, versioning, and best practices for infrastructure as code.
$1
Terraform modules are containers for multiple resources that are used together. They help you organize and reuse your Terraform code across different projects and environments.
$1
$1
`` module-name/
├── main.tf # Main module configuration
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Version constraints
└── README.md # Documentation
plaintext
`
$1
` resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = var.vpc_name
Environment = var.environment
}
} resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnets[count.index]
tags = {
Name = "${var.vpc_name}-public-${count.index + 1}"
}
}
hcl
`modules/vpc/main.tf
$1
` variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
} variable "vpc_name" {
description = "Name tag for VPC"
type = string
} variable "environment" {
description = "Environment tag"
type = string
} variable "public_subnets" {
description = "List of public subnet CIDR blocks"
type = list(string)
}
hcl
`modules/vpc/variables.tf
$1
` output "vpc_id" {
description = "ID of the created VPC"
value = aws_vpc.main.id
} output "public_subnet_ids" {
description = "List of public subnet IDs"
value = aws_subnet.public[*].id
}
hcl
`modules/vpc/outputs.tf
$1
$1
` module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
vpc_name = "my-vpc"
environment = "production"
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
}
hcl
`
$1
` module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "3.15.1"
bucket = "my-unique-bucket"
acl = "private"
versioning = {
enabled = true
}
}
hcl
`
$1
$1
` module "vpc" {
source = "../vpc"
vpc_cidr = var.vpc_cidr
vpc_name = var.vpc_name
environment = var.environment
public_subnets = var.public_subnets
} module "ec2_instance" {
source = "../ec2-instance"
subnet_id = module.vpc.public_subnet_ids[0]
instance_type = var.instance_type
environment = var.environment
}
hcl
`modules/web-app/main.tf
$1
` resource "aws_security_group" "this" {
name = var.name
description = var.description
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
hcl
`modules/security-group/main.tf
$1
` resource "aws_db_instance" "this" {
count = var.create_database ? 1 : 0
identifier = var.identifier
engine = var.engine
instance_class = var.instance_class
allocated_storage = var.allocated_storage
# ... other configuration ...
}
hcl
`modules/rds/main.tf
$1
$1
` terraform {
required_version = ">= 1.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
hcl
`versions.tf
$1
`markdown
README.md
VPC Module
This module creates a VPC with public and private subnets.
$1
` module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
vpc_name = "my-vpc"
}
hcl
`
$1
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| vpc_cidr | CIDR block for VPC | string | n/a | yes |
| vpc_name | Name tag for VPC | string | n/a | yes |
$1
| Name | Description |
|------|-------------|
| vpc_id | ID of the created VPC |
`
$1
` variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be one of: dev, staging, prod."
}
}
hcl
`variables.tf
$1
$1
` module "vpc" {
source = "../../"
vpc_cidr = "10.0.0.0/16"
vpc_name = "example-vpc"
environment = "dev"
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
} output "vpc_id" {
value = module.vpc.vpc_id
}
hcl
`examples/complete/main.tf
$1
` 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)
}
hcl
`test/vpc_test.go
$1
$1
` terraform {
required_version = ">= 1.0.0"
}hcl
`modules/vpc/main.tf
Add semantic versioning tags in Git
git tag -a "v1.0.0" -m "Initial stable release"
git push origin v1.0.0
$1
` module "vpc" {
source = "app.terraform.io/example-corp/vpc/aws"
version = "~> 2.0.0" # Allows 2.0.x but not 2.1.0
vpc_cidr = "10.0.0.0/16"
}
hcl
`
$1
$1
` provider "aws" {
alias = "us_east_1"
region = "us-east-1"
} provider "aws" {
alias = "us_west_2"
region = "us-west-2"
} resource "aws_vpc" "east" {
provider = aws.us_east_1
cidr_block = var.east_vpc_cidr
} resource "aws_vpc" "west" {
provider = aws.us_west_2
cidr_block = var.west_vpc_cidr
}
hcl
`modules/multi-region/main.tf
$1
` locals {
common_tags = {
Environment = var.environment
Project = var.project_name
Terraform = "true"
}
} resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = merge(local.common_tags, {
Name = "${var.project_name}-vpc"
})
}
hcl
``modules/template/main.tf
$1
Terraform modules are essential for:
$1
1. Create your first module
2. Implement testing strategies
3. Publish to a module registry
4. Explore advanced patterns
5. Integrate with CI/CD
$1
Why This Matters
Understanding the business and technical context helps you make informed decisions rather than blindly following patterns.
Trade-offs to Consider
Every architectural decision involves trade-offs. Consider your specific requirements, team expertise, and scale when evaluating options.
When NOT to Use This
Knowing when a solution doesn't apply is as valuable as knowing when it does. Consider alternatives for your specific situation.
Decision Framework
Use this framework to evaluate whether this approach is right for your use case based on your specific constraints and requirements.