| name | terraform-patterns |
| description | Write production-grade Terraform — module structure, remote state, workspace strategy, variable validation, lifecycle rules, data sources, provider version pinning, and safe plan/apply workflow. Use when asked about "Terraform", "IaC", "infrastructure as code", "Terraform module", "remote state", "terraform plan", "terraform apply", "state locking", "Terraform workspace", "provider version", "terraform import", "destroy protection", "variable validation", or "Terraform best practices". Do NOT use for: Kubernetes resource manifests — see kubernetes-patterns. Do NOT use for: Pulumi or CDK — patterns differ significantly.
|
| origin | yamtam-original |
| license | MIT © 2026 Vũ Văn Tâm |
| version | 1.0.0 |
| compatibility | Terraform ≥ 1.6, OpenTofu ≥ 1.6. AWS/GCP/Azure provider examples. |
When to Use
- Use when: provisioning cloud infrastructure (VPCs, EKS clusters, RDS, S3)
- Use when: converting manual cloud setup to reproducible IaC
- Use when: adding a new module to an existing Terraform repo
- Use when: debugging
terraform plan drift or state corruption
- Do NOT use for: k8s workload manifests — use kubernetes-patterns
- Do NOT use for: Helm chart templating — separate skill
Module Structure
infra/
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ └── versions.tf
│ └── rds/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── environments/
│ ├── production/
│ │ ├── main.tf ← composes modules
│ │ ├── terraform.tfvars ← env-specific values
│ │ └── backend.tf ← remote state config
│ └── staging/
│ └── ...
└── .terraform-version ← pin Terraform CLI version (tfenv)
Provider Version Pinning (versions.tf)
terraform {
required_version = ">= 1.6.0, < 2.0.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40" # allows 5.40.x, blocks 6.x
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Repository = "github.com/org/infra"
}
}
}
Remote State (backend.tf)
# S3 backend with DynamoDB state locking
terraform {
backend "s3" {
bucket = "myorg-terraform-state"
key = "production/main.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-locks" # prevents concurrent apply
}
}
aws s3api create-bucket --bucket myorg-terraform-state --region us-east-1
aws dynamodb create-table \
--table-name terraform-state-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Variable Validation
variable "environment" {
type = string
description = "Deployment environment"
validation {
condition = contains(["production", "staging", "dev"], var.environment)
error_message = "environment must be production, staging, or dev."
}
}
variable "instance_type" {
type = string
default = "t3.medium"
validation {
condition = can(regex("^(t3|t4g|m6i|m7i)\\.", var.instance_type))
error_message = "Only t3, t4g, m6i, m7i families are approved."
}
}
variable "db_password" {
type = string
sensitive = true # redacted from plan output + state display
}
Lifecycle Rules
resource "aws_db_instance" "main" {
identifier = "prod-postgres"
# ...
lifecycle {
prevent_destroy = true # block terraform destroy in production
ignore_changes = [
engine_version, # managed by RDS auto minor upgrade
latest_restorable_time,
]
create_before_destroy = true # for zero-downtime replacement
}
}
resource "aws_security_group" "api" {
lifecycle {
create_before_destroy = true # SG replacement doesn't break running instances
}
}
Data Sources — Read Existing Resources
# Reference resources not managed by this state
data "aws_vpc" "existing" {
tags = { Name = "production-vpc" }
}
data "aws_secretsmanager_secret_version" "db_pass" {
secret_id = "prod/myapp/database"
}
# Use in resources
resource "aws_db_instance" "main" {
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [data.aws_vpc.existing.id]
password = jsondecode(data.aws_secretsmanager_secret_version.db_pass.secret_string)["password"]
}
Safe Plan/Apply Workflow
terraform init
terraform validate
terraform fmt -recursive
terraform plan -out=tfplan
terraform apply tfplan
terraform apply -input=false -auto-approve
terraform apply tfplan
terraform apply -target=aws_instance.web
Outputs — Expose for Other Modules
output "vpc_id" {
description = "VPC ID for use by EKS and RDS modules"
value = aws_vpc.main.id
sensitive = false
}
output "db_endpoint" {
description = "RDS connection endpoint"
value = aws_db_instance.main.endpoint
sensitive = false
}
Anti-Fake-Pass Rules
Before claiming Terraform code is production-ready, you MUST show:
Reference: gates/anti-fake-pass-gate.md