Locals and built-in functions

Terraform · Practical · 10 min

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) and cidrhost
  • 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.

Check yourself

  1. What does cidrsubnet("10.0.0.0/16", 8, 3) return?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.hashicorp.com/terraform/language/functions