Terraform Patterns
Module Structure
modules/
vpc/
main.tf
variables.tf
outputs.tf
versions.tf
environments/
prod/
main.tf
terraform.tfvars
staging/
main.tf
terraform.tfvars
versions.tf — Pin providers
terraform {
required_version = ">= 1.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "mycompany-tfstate"
key = "prod/app/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
Variables with Validation
variable "environment" {
type = string
description = "Deployment environment"
validation {
condition = contains(["prod", "staging", "dev"], var.environment)
error_message = "environment must be prod, staging, or dev"
}
}
variable "instance_count" {
type = number
default = 2
validation {
condition = var.instance_count >= 1 && var.instance_count <= 10
error_message = "instance_count must be between 1 and 10"
}
}
Loops with for_each
variable "buckets" {
type = map(object({
versioning = bool
region = string
}))
default = {
assets = { versioning = false, region = "us-east-1" }
backups = { versioning = true, region = "us-west-2" }
}
}
resource "aws_s3_bucket" "this" {
for_each = var.buckets
bucket = "${var.environment}-${each.key}"
}
resource "aws_s3_bucket_versioning" "this" {
for_each = { for k, v in var.buckets : k => v if v.versioning }
bucket = aws_s3_bucket.this[each.key].id
versioning_configuration {
status = "Enabled"
}
}
Conditional Resources
resource "aws_cloudwatch_log_group" "this" {
count = var.enable_logging ? 1 : 0
name = "/app/${var.environment}"
retention_in_days = var.environment == "prod" ? 90 : 7
}
Locals for Computed Values
locals {
common_tags = {
Environment = var.environment
ManagedBy = "terraform"
Team = var.team
}
is_prod = var.environment == "prod"
db_size = local.is_prod ? "db.r6g.large" : "db.t3.medium"
}
resource "aws_db_instance" "this" {
instance_class = local.db_size
tags = local.common_tags
}
Data Sources
data "aws_vpc" "default" {
default = true
}
data "aws_subnets" "public" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
tags = { Tier = "public" }
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
Outputs
output "vpc_id" {
value = aws_vpc.this.id
description = "VPC ID"
}
output "db_endpoint" {
value = aws_db_instance.this.endpoint
sensitive = true
}
Workspaces Pattern
terraform workspace new staging
terraform workspace select prod
terraform plan -var-file="environments/${terraform.workspace}.tfvars"
Key Rules
- Always use remote state with locking (S3 + DynamoDB or Terraform Cloud)
- Use
terraform plan -out=tfplan then terraform apply tfplan in CI
- Never commit
.terraform/ or *.tfstate to git
- Use
moved blocks instead of destroy/recreate when refactoring resources
lifecycle { prevent_destroy = true } on critical resources (databases)