--- name: terraform-module-patterns description: "Detailed guide on Terraform module design patterns, directory structures, state management, and environment separation strategies. Use this skill for 'Terraform modules', 'module structure', 'state management', 'remote state', 'environment separation', 'workspace', 'terragrunt', 'module patterns', and other Terraform module design tasks. Enhances the IaC design capabilities of infra-architect and drift-detector. Note: actual terraform apply and infrastructure provisioning execution are outside the scope of this skill." --- # Terraform Module Patterns — Terraform Module Design Pattern Guide Patterns and best practices for designing reusable and maintainable Terraform modules. ## Directory Structure Patterns ### Pattern 1: Module Layer Separation ``` infrastructure/ ├── modules/ # Reusable modules │ ├── networking/ │ │ ├── vpc/ │ │ │ ├── main.tf │ │ │ ├── variables.tf │ │ │ ├── outputs.tf │ │ │ └── README.md │ │ ├── security-group/ │ │ └── load-balancer/ │ ├── compute/ │ │ ├── ecs-service/ │ │ ├── lambda/ │ │ └── ec2-asg/ │ └── data/ │ ├── rds/ │ ├── elasticache/ │ └── s3/ ├── environments/ # Per-environment configuration │ ├── dev/ │ │ ├── main.tf # Module invocations │ │ ├── terraform.tfvars # Environment variables │ │ └── backend.tf # State storage │ ├── staging/ │ └── prod/ └── global/ # Cross-environment (IAM, DNS) ├── iam/ └── route53/ ``` ### Pattern 2: Terragrunt-based DRY ``` infrastructure/ ├── modules/ # Same as above ├── terragrunt.hcl # Root config (backend, provider) └── environments/ ├── terragrunt.hcl # Common variables ├── dev/ │ ├── terragrunt.hcl # include root + env vars │ ├── vpc/ │ │ └── terragrunt.hcl # module source + inputs │ ├── ecs/ │ └── rds/ └── prod/ ``` ## Module Design Principles ### 1. Single Responsibility Module ```hcl # Good: VPC module handles only VPC module "vpc" { source = "./modules/networking/vpc" cidr_block = "10.0.0.0/16" az_count = 3 } # Bad: One module for all infrastructure module "everything" { # Anti-pattern! source = "./modules/full-stack" } ``` ### 2. Input/Output Design ```hcl # variables.tf — Validation required variable "instance_type" { type = string description = "EC2 instance type" default = "t3.medium" validation { condition = can(regex("^t3\\.", var.instance_type)) error_message = "Only t3 family is allowed." } } variable "environment" { type = string validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Must be one of dev, staging, prod." } } # outputs.tf — Expose only values needed by other modules output "vpc_id" { value = aws_vpc.main.id description = "ID of the created VPC" } ``` ### 3. Conditional Resources ```hcl variable "enable_monitoring" { type = bool default = true } resource "aws_cloudwatch_metric_alarm" "cpu" { count = var.enable_monitoring ? 1 : 0 # ... } ``` ## State Management Patterns ### Remote State Configuration ```hcl # backend.tf terraform { backend "s3" { bucket = "company-terraform-state" key = "environments/prod/vpc/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "terraform-locks" encrypt = true } } ``` ### State Separation Strategies | Strategy | Separation Basis | Pros | Cons | |----------|-----------------|------|------| | **Per-environment** | dev/staging/prod | Environment isolation, independent deployment | Need data source for cross-env references | | **Per-layer** | network/compute/data | Reduced blast radius | Complex reference management | | **Per-team** | Team-owned resources | Autonomy | Shared resource management difficulty | | **Per-lifecycle** | Change frequency | Stable resource protection | Boundary definition difficulty | ### Cross-State References ```hcl # Read VPC ID from network state data "terraform_remote_state" "network" { backend = "s3" config = { bucket = "company-terraform-state" key = "environments/prod/network/terraform.tfstate" region = "ap-northeast-2" } } # Usage resource "aws_ecs_service" "app" { network_configuration { subnets = data.terraform_remote_state.network.outputs.private_subnet_ids } } ``` ## Tagging Strategy ```hcl locals { common_tags = { Environment = var.environment Project = var.project_name ManagedBy = "terraform" Team = var.team_name CostCenter = var.cost_center } } resource "aws_instance" "app" { tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-app" Role = "application" }) } ``` ## Anti-patterns and Solutions | Anti-pattern | Problem | Solution | |-------------|---------|----------| | **Giant state file** | Slow plan/apply, large blast radius | Split by layer | | **Hardcoded values** | Cannot reuse across environments | variables + tfvars | | **Provider inside module** | Lost flexibility | Provider only at root | | **Complex conditions with count** | Resource recreation on index changes | Use for_each | | **Plaintext secrets** | Security violation | SSM/Vault references |