Terraform
TerraformIntermediate

Terraform Modules: Building Reusable Infrastructure Components

Admin KC
5 min read
TerraformIaCDevOpsCloudModules

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

``plaintext

module-name/

├── main.tf # Main module configuration

├── variables.tf # Input variables

├── outputs.tf # Output values

├── versions.tf # Version constraints

└── README.md # Documentation

`

$1

`hcl

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

}

}

`

$1

`hcl

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)

}

`

$1

`hcl

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

}

`

$1

$1

`hcl

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

}

`

$1

`hcl

module "s3_bucket" {

source = "terraform-aws-modules/s3-bucket/aws"

version = "3.15.1"

bucket = "my-unique-bucket"

acl = "private"

versioning = {

enabled = true

}

}

`

$1

$1

`hcl

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

}

`

$1

`hcl

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

}

}

}

`

$1

`hcl

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

}

`

$1

$1

`hcl

versions.tf

terraform {

required_version = ">= 1.0.0"

required_providers {

aws = {

source = "hashicorp/aws"

version = "~> 5.0"

}

}

}

`

$1

`markdown

README.md

VPC Module

This module creates a VPC with public and private subnets.

$1

`hcl

module "vpc" {

source = "./modules/vpc"

vpc_cidr = "10.0.0.0/16"

vpc_name = "my-vpc"

}

`

$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

`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."

}

}

`

$1

$1

`hcl

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

}

`

$1

`hcl

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)

}

`

$1

$1

`hcl

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

`

$1

`hcl

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"

}

`

$1

$1

`hcl

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

}

`

$1

`hcl

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"

})

}

``

$1

Terraform modules are essential for:

  • Code reusability
  • Maintainability
  • Standardization
  • Version control
  • Team collaboration
  • $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

  • [Terraform Module Documentation](https://www.terraform.io/docs/modules/index.html)
  • [Terraform Registry](https://registry.terraform.io/)
  • [Module Development Guide](https://www.terraform.io/docs/modules/develop/index.html)
  • 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.