How to Use Terraform Modules - Build Reusable Infrastructure
Create and use Terraform modules for reusable infrastructure code. Covers module structure, inputs, outputs, versioning, and the Terraform Registry.
Terraform
Learn the best way to organize Terraform projects. Covers file structure, modules, environments, and naming conventions for scalable infrastructure code.
A well-structured Terraform project is easier to maintain, debug, and scale. This guide covers proven patterns for organizing your Terraform code, from simple single-environment setups to complex multi-environment architectures.
For small projects or getting started:
project/
├── main.tf # Primary resources
├── variables.tf # Input variable declarations
├── outputs.tf # Output declarations
├── terraform.tfvars # Variable values
├── providers.tf # Provider configuration
└── versions.tf # Version constraintsresource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = local.common_tags
}
resource "aws_subnet" "public" {
count = length(var.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnets[count.index]
availability_zone = var.azs[count.index]
}provider "aws" {
region = var.region
}terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}For projects with dev, staging, and production:
project/
├── modules/
│ ├── networking/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── compute/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── database/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ │ ├── main.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ └── prod/
│ ├── main.tf
│ ├── terraform.tfvars
│ └── backend.tf
└── README.md# modules/networking/main.tf
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(var.tags, { Name = "${var.name}-vpc" })
}
# modules/networking/variables.tf
variable "vpc_cidr" {
type = string
description = "CIDR block for the VPC"
}
variable "name" {
type = string
description = "Name prefix for resources"
}
# modules/networking/outputs.tf
output "vpc_id" {
value = aws_vpc.this.id
}# environments/prod/main.tf
module "networking" {
source = "../../modules/networking"
vpc_cidr = "10.0.0.0/16"
name = "prod"
tags = local.common_tags
}
module "compute" {
source = "../../modules/compute"
vpc_id = module.networking.vpc_id
instance_type = "t3.large"
}Consistent naming makes code readable:
# Resources: snake_case, descriptive
resource "aws_instance" "web_server" {}
resource "aws_s3_bucket" "application_logs" {}
# Variables: snake_case
variable "instance_type" {}
variable "vpc_cidr_block" {}
# Outputs: snake_case, prefixed by resource
output "vpc_id" {}
output "web_server_public_ip" {}
# Modules: snake_case
module "networking" {}
module "web_application" {}data.tf filelocals.tfGood project structure pays dividends as your infrastructure grows. Start simple, use modules for reusability, and maintain consistent conventions across your team.
Create and use Terraform modules for reusable infrastructure code. Covers module structure, inputs, outputs, versioning, and the Terraform Registry.
Master Terraform lifecycle meta-arguments. Covers prevent_destroy, create_before_destroy, ignore_changes, and replace_triggered_by with examples.
Use Terraform dynamic blocks to eliminate repetitive nested blocks. Covers security group rules, IAM policies, and tag generation patterns.
Master conditional expressions in Terraform. Learn ternary operators, conditional resource creation, count and for_each conditionals with examples.