Crisp answer: Locals are named expressions computed within a module. They avoid repetition and make complex expressions readable by giving them a name.
Declaring locals:
locals {
# Simple value
region = "eu-west-2"
# Computed from a variable
name_prefix = "${var.project}-${var.environment}"
# Conditional
is_production = var.environment == "prod"
# Reusable tags applied to all resources
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
Team = "platform"
}
# Complex expression you don't want repeated
private_subnet_cidrs = [
for i, az in var.availability_zones :
cidrsubnet(var.vpc_cidr, 8, i)
]
}
Using locals:
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = merge(local.common_tags, { Name = "${local.name_prefix}-vpc" })
}
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = local.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = merge(local.common_tags, { Name = "${local.name_prefix}-private-${count.index}" })
}
resource "aws_rds_cluster" "main" {
cluster_identifier = "${local.name_prefix}-db"
deletion_protection = local.is_production
tags = local.common_tags
}
locals vs variables:
variable |
local |
|
|---|---|---|
| Set by | Caller (tfvars, CLI) | Defined inside the module |
| Purpose | External input | Internal computation |
| Changeable per environment | Yes | Only via variables it depends on |
What to say in the interview:
"Locals are named expressions local to the module. I use them for two things: a common_tags map that gets merged into every resource so all infrastructure has consistent tags, and for complex expressions I would otherwise repeat across multiple resource blocks. The key distinction from variables: variables are inputs set by the caller, locals are computed internally. You cannot set a local from the CLI or a tfvars file."