Terraform Module Starter
A complete Terraform module structure template with variables, outputs, README, and examples. Copy this to start any new module.
Description
A production-ready Terraform module template following HashiCorp’s best practices. Includes proper variable definitions, outputs, validation, and documentation structure.
Module Structure
modules/my-module/
├── main.tf # Main resource definitions
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Provider requirements
├── locals.tf # Local values
├── data.tf # Data sources
├── README.md # Documentation
└── examples/
└── complete/
├── main.tf
└── outputs.tf
versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
variables.tf
# ════════════════════════════════════════════════════════════
# Required Variables
# ════════════════════════════════════════════════════════════
variable "name" {
description = "Name prefix for all resources"
type = string
validation {
condition = length(var.name) <= 32 && can(regex("^[a-z0-9-]+$", var.name))
error_message = "Name must be lowercase alphanumeric with hyphens, max 32 chars."
}
}
variable "environment" {
description = "Environment name (dev, staging, prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
# ════════════════════════════════════════════════════════════
# Optional Variables
# ════════════════════════════════════════════════════════════
variable "enable_feature_x" {
description = "Enable feature X"
type = bool
default = false
}
variable "instance_count" {
description = "Number of instances to create"
type = number
default = 1
validation {
condition = var.instance_count >= 1 && var.instance_count <= 10
error_message = "Instance count must be between 1 and 10."
}
}
variable "config" {
description = "Configuration object"
type = object({
size = optional(string, "small")
timeout = optional(number, 30)
tags = optional(map(string), {})
})
default = {}
}
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}
locals.tf
locals {
# Common naming
name_prefix = "${var.name}-${var.environment}"
# Merge default and custom tags
common_tags = merge(
{
Name = local.name_prefix
Environment = var.environment
ManagedBy = "terraform"
Module = "my-module"
},
var.tags
)
# Conditional logic
size_map = {
small = "t3.micro"
medium = "t3.small"
large = "t3.medium"
}
instance_type = lookup(local.size_map, var.config.size, "t3.micro")
}
main.tf
# ════════════════════════════════════════════════════════════
# Main Resources
# ════════════════════════════════════════════════════════════
resource "aws_instance" "main" {
count = var.instance_count
ami = data.aws_ami.amazon_linux.id
instance_type = local.instance_type
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-${count.index + 1}"
})
lifecycle {
create_before_destroy = true
}
}
# Conditional resource
resource "aws_cloudwatch_metric_alarm" "cpu" {
count = var.enable_feature_x ? var.instance_count : 0
alarm_name = "${local.name_prefix}-cpu-${count.index + 1}"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
dimensions = {
InstanceId = aws_instance.main[count.index].id
}
tags = local.common_tags
}
data.tf
# ════════════════════════════════════════════════════════════
# Data Sources
# ════════════════════════════════════════════════════════════
data "aws_region" "current" {}
data "aws_caller_identity" "current" {}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
outputs.tf
# ════════════════════════════════════════════════════════════
# Outputs
# ════════════════════════════════════════════════════════════
output "instance_ids" {
description = "IDs of created instances"
value = aws_instance.main[*].id
}
output "instance_private_ips" {
description = "Private IPs of created instances"
value = aws_instance.main[*].private_ip
}
output "name_prefix" {
description = "Name prefix used for resources"
value = local.name_prefix
}
# Sensitive output
output "connection_info" {
description = "Connection information (sensitive)"
value = {
region = data.aws_region.current.name
instance = aws_instance.main[0].id
}
sensitive = true
}
README.md Template
# My Module
Brief description of what this module does.
## Usage
\`\`\`hcl
module "example" {
source = "../../modules/my-module"
name = "my-app"
environment = "prod"
enable_feature_x = true
instance_count = 3
config = {
size = "medium"
timeout = 60
}
tags = {
Project = "my-project"
}
}
\`\`\`
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.5.0 |
| aws | >= 5.0 |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| name | Name prefix for all resources | string | n/a | yes |
| environment | Environment name | string | n/a | yes |
| enable_feature_x | Enable feature X | bool | false | no |
| instance_count | Number of instances | number | 1 | no |
## Outputs
| Name | Description |
|------|-------------|
| instance_ids | IDs of created instances |
| instance_private_ips | Private IPs of instances |
Example Usage
# examples/complete/main.tf
module "my_module" {
source = "../../"
name = "example"
environment = "dev"
enable_feature_x = true
instance_count = 2
config = {
size = "medium"
}
tags = {
Owner = "platform-team"
}
}
output "instance_ids" {
value = module.my_module.instance_ids
}