Terraform Modules: Building Reusable Infrastructure Components
terraform

Terraform Modules: Building Reusable Infrastructure Components

Master Terraform modules to create reusable, maintainable infrastructure components. Learn about module creation, versioning, and best practices for infrastructure as code.

March 15, 2024
Admin KC
5 min read

Introduction to Terraform Modules

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.

Module Basics

Module Structure

module-name/ ├── main.tf # Main module configuration ├── variables.tf # Input variables ├── outputs.tf # Output values ├── versions.tf # Version constraints └── README.md # Documentation

Creating a Basic Module

# modules/vpc/main.tf 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}" } }

Module Variables

# modules/vpc/variables.tf 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) }

Module Outputs

# modules/vpc/outputs.tf 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 }

Using Modules

Local Module Usage

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"] }

Remote Module Usage

module "s3_bucket" { source = "terraform-aws-modules/s3-bucket/aws" version = "3.15.1" bucket = "my-unique-bucket" acl = "private" versioning = { enabled = true } }

Advanced Module Concepts

Module Composition

# modules/web-app/main.tf 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 }

Dynamic Block Usage

# modules/security-group/main.tf 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 } } }

Conditional Creation

# modules/rds/main.tf 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 ... }

Module Best Practices

1. Version Constraints

# versions.tf terraform { required_version = ">= 1.0.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } }

2. Documentation

# README.md # VPC Module This module creates a VPC with public and private subnets. ## Usage ```hcl module "vpc" { source = "./modules/vpc" vpc_cidr = "10.0.0.0/16" vpc_name = "my-vpc" }

Inputs

| 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 |

Outputs

| Name | Description | |------|-------------| | vpc_id | ID of the created VPC |

### 3. Input Validation ```hcl # variables.tf 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." } }

Testing Modules

1. Basic Testing

# examples/complete/main.tf 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 }

2. Automated Testing

# test/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) }

Working with Module Registries

Publishing to Registry

# modules/vpc/main.tf terraform { required_version = ">= 1.0.0" } # Add semantic versioning tags in Git # git tag -a "v1.0.0" -m "Initial stable release" # git push origin v1.0.0

Module Versioning

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" }

Advanced Module Patterns

1. Provider Aliasing

# modules/multi-region/main.tf 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 }

2. Module Templates

# modules/template/main.tf 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" }) }

Conclusion

Terraform modules are essential for:

  • Code reusability
  • Maintainability
  • Standardization
  • Version control
  • Team collaboration

Next Steps

  1. Create your first module
  2. Implement testing strategies
  3. Publish to a module registry
  4. Explore advanced patterns
  5. Integrate with CI/CD

Resources

Terraform
IaC
DevOps
Cloud
Modules