hcl Infrastructure as Code June 15, 2025

Terraform Module Starter

A complete Terraform module structure template with variables, outputs, README, and examples. Copy this to start any new module.

terraformmodulesiactemplate

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
}