Terraform Module Examples - Technical Reference
When to Use This Skill
- •User asks to "create examples", "add example configurations", or "write example code"
- •Creating or updating
examples/directory structure - •Writing inline examples for README.md (minimal and advanced setups)
- •Need templates for example files (main.tf, variables.tf, outputs.tf, etc.)
- •Determining common scenario directories (with-load-balancer/, service-connect-only/, etc.)
- •Setting up example testing or CI/CD validation
- •Questions about placeholder conventions or example best practices
Prerequisites
- •Understanding of the module's functionality and use cases
- •Knowledge of required vs. optional variables
- •Familiarity with CloudPosse label module integration
- •Understanding of target scenarios (basic, complete, specific use cases)
Overview
This skill provides comprehensive reference for creating runnable Terraform module examples. Examples demonstrate real-world usage patterns and help users understand module implementation.
Example Types
1. Inline Examples (README.md)
Minimal Setup - Required:
module "basic_service" {
source = "github.com/Luscii/terraform-aws-ecs-service"
# Required variables only
name = "my-service"
ecs_cluster_name = "production"
vpc_id = "vpc-12345678"
subnets = ["subnet-12345678", "subnet-87654321"]
task_cpu = 256
task_memory = 512
container_definitions = [{
name = "app"
image = "nginx:latest"
}]
task_role = { name = "task-role", arn = "arn:aws:iam::123456789012:role/task-role" }
execution_role = { name = "exec-role", arn = "arn:aws:iam::123456789012:role/exec-role" }
context = module.label.context
}
Characteristics:
- •Under 30 lines
- •Required variables only
- •Sensible defaults
- •Generic but realistic values
- •No optional features
Advanced Setup - Required:
module "label" {
source = "cloudposse/label/null"
version = "0.25.0"
namespace = "luscii"
environment = "production"
name = "api"
}
module "advanced_service" {
source = "github.com/Luscii/terraform-aws-ecs-service"
name = module.label.name
ecs_cluster_name = "production"
vpc_id = "vpc-12345678"
subnets = ["subnet-12345678", "subnet-87654321"]
task_cpu = 1024
task_memory = 2048
container_definitions = [{
name = "app"
image = "myregistry.io/api:v1.2.3"
port_mappings = [{
containerPort = 8080
protocol = "tcp"
name = "http"
}]
environment = [
{ name = "ENVIRONMENT", value = "production" },
{ name = "LOG_LEVEL", value = "info" }
]
secrets = module.secrets.container_definition
}]
load_balancers = [{
target_group_arn = aws_lb_target_group.api.arn
container_name = "app"
container_port = 8080
}]
scaling = {
min_capacity = 2
max_capacity = 10
}
scaling_target = {
cpu = {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
target_value = 70
}
}
task_role = module.task_role.role
execution_role = module.execution_role.role
context = module.label.context
}
# Output usage demonstration
resource "aws_route53_record" "api" {
zone_id = data.aws_route53_zone.main.zone_id
name = "api.example.com"
type = "A"
alias {
name = module.load_balancer.dns_name
zone_id = module.load_balancer.zone_id
evaluate_target_health = true
}
}
Characteristics:
- •Under 100 lines
- •Production-ready configuration
- •Multiple optional features
- •Output usage demonstrated
- •Integration with other resources
- •CloudPosse label context shown
2. Examples Directory (examples/)
When to Create:
- •Module supports multiple distinct use cases
- •Configurations too complex for inline examples
- •Need tested, runnable configurations
- •Users benefit from complete examples
Directory Structure:
examples/
├── README.md # Navigation to all examples
├── basic/ # Minimal working example
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── versions.tf
│ └── README.md
├── complete/ # Full-featured example
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── versions.tf
│ └── README.md
└── {scenario}/ # Use-case specific
├── main.tf
├── variables.tf
├── outputs.tf
├── versions.tf
└── README.md
File Templates
main.tf
# Provider configuration
provider "aws" {
region = var.region
}
# CloudPosse label module
module "label" {
source = "cloudposse/label/null"
version = "0.25.0"
namespace = var.namespace
environment = var.environment
name = var.name
}
# Reference parent module with relative path
module "example" {
source = "../../"
# Configuration specific to this scenario
name = module.label.name
# ... other variables
context = module.label.context
}
# Supporting resources (data sources, IAM roles, etc.)
data "aws_vpc" "this" {
id = var.vpc_id
}
# Output usage demonstration
resource "aws_route53_record" "example" {
# ... configuration
}
Best Practices:
- •Use
source = "../../"for parent module - •Include provider configuration
- •Use variables for customizable values
- •Add comments for key decisions
- •Keep self-contained and runnable
variables.tf
variable "namespace" {
type = string
description = "Namespace for resource naming"
default = "example"
}
variable "environment" {
type = string
description = "Environment name (dev, staging, prod)"
default = "dev"
}
variable "name" {
type = string
description = "Name of the resource"
default = "demo"
}
variable "region" {
type = string
description = "AWS region for resources"
default = "eu-west-1"
}
variable "vpc_id" {
type = string
description = "VPC ID where resources will be created"
default = null
}
variable "subnet_ids" {
type = list(string)
description = "List of subnet IDs for resource placement"
default = []
}
Best Practices:
- •Provide sensible defaults
- •Include clear descriptions
- •Make infrastructure IDs customizable
- •Allow region configuration
- •Document expected values
outputs.tf
output "id" {
value = module.example.id
description = "Unique identifier of the created resource"
}
output "arn" {
value = module.example.arn
description = "ARN of the created resource"
}
output "name" {
value = module.example.name
description = "Name of the created resource"
}
output "url" {
value = module.example.url
description = "URL to access the resource"
}
output "security_group_id" {
value = module.example.security_group_id
description = "Security group ID for network configuration"
}
Best Practices:
- •Expose key module outputs
- •Include descriptions
- •Show integration points
- •Document output usage
versions.tf
terraform {
required_version = ">= 1.3"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.0"
}
}
}
Best Practices:
- •Match parent module requirements
- •Use >= for flexibility
- •Include all required providers
README.md (Example-specific)
# {Scenario Name}
## Purpose
Brief description of what this example demonstrates and when to use it.
## What This Example Deploys
- Resource type 1 (e.g., ECS Fargate service with 2 tasks)
- Resource type 2 (e.g., Application Load Balancer)
- Resource type 3 (e.g., Auto-scaling configuration)
- Integration point (e.g., CloudWatch log group)
## Prerequisites
- Existing VPC with subnets
- ECS cluster (if applicable)
- IAM roles (if applicable)
- Valid AWS credentials configured
## Usage
```bash
# Set required variables (if not using defaults)
export TF_VAR_vpc_id="vpc-12345678"
export TF_VAR_subnet_ids='["subnet-12345678","subnet-87654321"]'
# Initialize Terraform
terraform init
# Review the plan
terraform plan
# Apply the configuration
terraform apply
Configuration
Key configuration points:
- •Feature A: Explanation (e.g., "Load Balancer deployed in public subnets")
- •Feature B: Explanation (e.g., "Scales 2-10 tasks based on CPU > 70%")
- •Feature C: Explanation (e.g., "Health checks on /health endpoint")
Testing
After deployment:
# Get output values terraform output # Test functionality (example) curl http://$(terraform output -raw alb_dns_name)/
Cleanup
terraform destroy
Notes
- •Important limitations or assumptions
- •Prerequisites details
- •Timing considerations (e.g., "Auto-scaling may take 3-5 minutes")
### examples/README.md (Root)
```markdown
# Examples
This directory contains examples demonstrating various ways to use this module.
## Available Examples
### [Basic Example](./basic/)
Minimal configuration showing the essential inputs required to use this module.
**Use this when:** You want the simplest possible setup to get started.
**Demonstrates:**
- Required variables only
- Minimal working configuration
- Basic module usage
---
### [Complete Example](./complete/)
Full-featured configuration demonstrating all major features and optional parameters.
**Use this when:** You need a production-ready configuration with common features.
**Demonstrates:**
- All important optional features
- Integration with other AWS services
- Output usage
- Production best practices
---
### [{Scenario Name}](./{scenario}/)
Brief description of the scenario.
**Use this when:** Specific use case description.
**Demonstrates:**
- Key feature 1
- Key feature 2
- Integration pattern
---
## Running Examples
Each example is self-contained. To run an example:
```bash
cd {example-directory}
terraform init
terraform plan
terraform apply
Prerequisites
Common prerequisites for all examples:
- •Terraform >= 1.3
- •AWS Provider >= 6.0
- •Valid AWS credentials configured
- •[List specific AWS resources needed]
Cleaning Up
To remove resources created by an example:
cd {example-directory}
terraform destroy
## Common Scenario Directories ### ECS Service Module - `basic/` - Minimal service configuration - `complete/` - Production-ready with all features - `with-load-balancer/` - Service behind ALB - `service-connect-only/` - Internal service using Service Connect - `scheduled-task/` - Scheduled ECS task - `with-autoscaling/` - Auto-scaling enabled ### Load Balancer Module - `basic/` - Simple load balancer - `complete/` - Full-featured ALB - `public-alb/` - Internet-facing - `internal-alb/` - Internal only - `with-waf/` - WAF integration ### Secrets Module - `basic/` - Simple secrets - `new-secrets/` - Creating new secrets - `existing-secrets/` - Using existing secrets - `mixed/` - Combination of new and existing ## Placeholder Conventions ### In Code Comments ```terraform # Replace with your VPC ID vpc_id = "vpc-12345678" # Replace with your subnet IDs subnets = ["subnet-12345678", "subnet-87654321"]
In README
Use angle brackets for placeholders:
- •
<VPC_ID>- "e.g., vpc-12345678" - •
<SUBNET_ID>- "e.g., subnet-12345678" - •
<REGION>- "e.g., eu-west-1"
Provide example values and where to find them.
In Variables
variable "vpc_id" {
type = string
description = "VPC ID where resources will be created. Find in VPC console or use data source."
default = null # Or sensible default
}
Testing Checklist
Before committing examples:
- •Initialize:
terraform initsucceeds - •Validate:
terraform validatepasses - •Format:
terraform fmtapplied - •Plan:
terraform plancompletes without errors - •Apply: (Optional) Test actual deployment
- •Documentation: README complete and accurate
- •Variables: All have descriptions and defaults
- •Outputs: All documented
- •Source: Uses
source = "../../" - •Versions: Constraints specified
CI/CD Integration Example
# .github/workflows/validate-examples.yml
name: Validate Examples
on:
pull_request:
paths:
- 'examples/**'
- '*.tf'
jobs:
validate:
runs-on: ubuntu-latest
strategy:
matrix:
example: [basic, complete]
steps:
- uses: actions/checkout@v3
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: "1.3"
- name: Validate Example
working-directory: examples/${{ matrix.example }}
run: |
terraform init
terraform validate
terraform fmt -check
Example Quality Standards
Working Code
- •All examples tested and working
- •Run
terraform planandterraform apply - •Include necessary data sources
Self-Contained
- •Independently runnable
- •All required configurations included
- •No external state dependencies (except documented prerequisites)
Realistic
- •Production-like configurations
- •Realistic naming conventions
- •Actual use cases demonstrated
Documented
- •Clear purpose explanation
- •Why specific choices made
- •Complete usage instructions
- •Prerequisites listed
- •Cleanup instructions provided
Anti-Patterns
❌ DON'T
- •
Empty Examples:
- •No placeholder examples
- •Every example must be complete
- •
Over-Complicated:
- •Don't demonstrate everything in one example
- •Keep focused on specific use case
- •
Undocumented:
- •Don't assume understanding
- •Document why, not just what
- •
Hardcoded Values:
- •Use variables for customization
- •Provide defaults but allow overrides
- •
Missing Context:
- •Don't skip CloudPosse label
- •Show how module fits in infrastructure
- •
Untested:
- •Don't commit unvalidated examples
- •Run at least
terraform plan
✅ DO
- •
Test Thoroughly:
- •Validate all examples work
- •Format code consistently
- •Check all files present
- •
Document Completely:
- •Explain purpose clearly
- •List prerequisites
- •Provide cleanup steps
- •
Keep Current:
- •Update with module changes
- •Update provider versions
- •Re-test after updates
- •
Use Patterns:
- •Follow standard structure
- •Use
source = "../../" - •Include all 5 required files
- •
Be Realistic:
- •Show actual use cases
- •Use production-like values
- •Demonstrate integrations
Maintenance
Update Triggers
Update examples when:
- •Module interface changes
- •New features added
- •Provider versions updated
- •Terraform version updated
- •Breaking changes introduced
Version Management
# Pin external modules
module "label" {
source = "cloudposse/label/null"
version = "0.25.0" # Exact version
}
# Reference parent with relative path
module "example" {
source = "../../" # Always relative
}
Dependency Management
- •Minimize external dependencies
- •Document required resources clearly
- •Provide data source examples for lookups
- •Use variables for resource references