Terraform Advanced — Locals and Conditionals
Precompute complex expressions with locals and control resource creation with conditionals.
Locals — Precomputed Expressions
locals {
# Derive values once, use everywhere
env = terraform.workspace
name_prefix = "${var.project}-${local.env}"
# Merge default tags with custom tags
common_tags = merge(
{
Project = var.project
Environment = local.env
ManagedBy = "terraform"
},
var.extra_tags
)
# Flatten nested data structures
subnet_config = flatten([
for az in var.availability_zones : [
for type in ["public", "private"] : {
az = az
type = type
cidr = cidrsubnet(var.vpc_cidr, 8, index(var.availability_zones, az) * 2 + (type == "private" ? 1 : 0))
}
]
])
}
Conditional Resource Creation
# Create resource only in production
resource "aws_cloudwatch_metric_alarm" "cpu" {
count = var.environment == "production" ? 1 : 0
alarm_name = "${local.name_prefix}-cpu-high"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
}
Conditional Expressions in Attributes
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = var.environment == "production" ? "m5.xlarge" : "t3.micro"
root_block_device {
volume_size = var.environment == "production" ? 100 : 20
volume_type = var.environment == "production" ? "gp3" : "gp2"
}
tags = local.common_tags
}
Anti-Pattern: Over-Using Conditionals
# ❌ Anti-pattern: too many conditionals make code unreadable
resource "aws_instance" "app" {
instance_type = (
var.env == "prod" ? "m5.xlarge" :
var.env == "staging" ? "m5.large" :
var.env == "dev" ? "t3.medium" :
"t3.micro"
)
}
# ✅ Best practice: use a map lookup
variable "instance_types" {
default = {
prod = "m5.xlarge"
staging = "m5.large"
dev = "t3.medium"
}
}
resource "aws_instance" "app" {
instance_type = lookup(var.instance_types, var.env, "t3.micro")
}
What’s Next?
Practice HCL patterns.