Locals keep configurations readable; functions avoid hard-coding derived values.
A local value names an expression so you can reuse it: define in a locals block, use as local.name. Use locals for things like a naming prefix, a merged tag map, or a computed list, so the logic lives in one place. Unlike variables, locals can't be set from outside.
Terraform has a fixed library of built-in functions (you can't define your own in HCL, though providers can ship provider-defined functions called as provider::name::fn(...)). The ones you'll use most:
- strings:
format,join,split,replace,lower,trimspace,startswith - collections:
merge,concat,flatten,lookup(map, key, default),keys,values,contains,distinct,zipmap,setproduct - numbers:
max,min,ceil - encoding:
jsonencode,yamldecode,base64encode - files:
file,templatefile(path, vars)for rendering config files and cloud-init - networking:
cidrsubnet(prefix, newbits, netnum)andcidrhost - type and errors:
try(expr, fallback),can(expr),coalesce,tostring,tomap
jsonencode is the right way to produce JSON for policies and container definitions: no escaping mistakes.
Test functions in terraform console: cidrsubnet("10.0.0.0/16", 8, 2) returns "10.0.2.0/24".
Example
variable "env" {
type = string
default = "prod"
}
locals {
name_prefix = "acme-${var.env}"
common_tags = merge({ env = var.env, managed_by = "terraform" }, { team = "platform" })
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
subnets = { for i, az in local.azs : az => cidrsubnet("10.0.0.0/16", 8, i) }
policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Effect = "Allow", Action = ["s3:GetObject"], Resource = "*" }]
})
}
output "subnets" { value = local.subnets }
# { "eu-west-1a" = "10.0.0.0/24", "eu-west-1b" = "10.0.1.0/24", "eu-west-1c" = "10.0.2.0/24" }
output "team" { value = lookup(local.common_tags, "team", "none") } # "platform"Remember
- locals name derived values; variables are inputs
- merge, lookup, cidrsubnet, jsonencode, templatefile, try cover most needs
- No user-defined HCL functions; try them out in terraform console
Watch out for
- Building JSON with string interpolation instead of jsonencode and breaking it on a quote.