Crisp answer: Terraform has ~100 built-in functions for string manipulation, numeric operations, collection handling, encoding, and filesystem operations. You cannot define your own functions — use locals to compose expressions.
String functions:
lower("HELLO-WORLD") # "hello-world"
upper("hello") # "HELLO"
replace("hello world", " ", "-") # "hello-world"
trimspace(" hello ") # "hello"
split(",", "a,b,c") # ["a", "b", "c"]
join("-", ["web", "prod"]) # "web-prod"
format("%-10s %d", "web", 3) # "web 3"
# String templating functions
substr("hello world", 0, 5) # "hello"
length("hello") # 5
# Check and manipulate
startswith("web-prod", "web") # true
endswith("web-prod", "prod") # true
contains(["a","b","c"], "b") # true
Collection functions:
length(["a", "b", "c"]) # 3
length({a = 1, b = 2}) # 2
flatten([["a", "b"], ["c"]]) # ["a", "b", "c"]
distinct(["a", "b", "a", "c"]) # ["a", "b", "c"]
keys({a = 1, b = 2}) # ["a", "b"]
values({a = 1, b = 2}) # [1, 2]
merge({a = 1}, {b = 2}) # {a = 1, b = 2}
merge(local.common_tags, {Name = "web"}) # merge tags
lookup(var.ami_map, var.region, "ami-default") # get with default
toset(["a", "b", "a"]) # {"a", "b"} (dedup)
tolist(toset(["b", "a"])) # ["a", "b"] (sorted)
Numeric functions:
max(3, 1, 4, 1, 5) # 5
min(3, 1, 4) # 1
abs(-5) # 5
ceil(1.2) # 2
floor(1.8) # 1
IP/CIDR functions:
# Divide a VPC CIDR into subnets:
cidrsubnet("10.0.0.0/16", 8, 0) # "10.0.0.0/24" (add 8 bits, subnet 0)
cidrsubnet("10.0.0.0/16", 8, 1) # "10.0.1.0/24" (add 8 bits, subnet 1)
cidrsubnet("10.0.0.0/16", 8, 2) # "10.0.2.0/24"
# Extract subnet size:
cidrnetmask("10.0.0.0/24") # "255.255.255.0"
cidrhost("10.0.0.0/24", 5) # "10.0.0.5"
Encoding functions:
base64encode("hello world") # "aGVsbG8gd29ybGQ="
base64decode("aGVsbG8gd29ybGQ=") # "hello world"
jsonencode({key = "value"}) # "{\"key\":\"value\"}"
jsondecode("{\"key\":\"value\"}") # {key = "value"}
yamlencode({key = "value"}) # "key: value\n"
File functions:
file("scripts/bootstrap.sh") # Read file contents as string
filebase64("certs/ca.crt") # Read file as base64
templatefile("templates/policy.json.tpl", {
bucket = aws_s3_bucket.main.id
region = var.region
}) # Render a template file with variables
What to say in the interview:
"The functions I reach for most: cidrsubnet for calculating subnet CIDRs from a VPC block, merge for combining common_tags with resource-specific tags, jsonencode for IAM policies inline in Terraform rather than template files, and flatten when I end up with a list of lists that I need to flatten for for_each. The cidrsubnet pattern is particularly useful for generating subnets from a variable VPC CIDR without having to hardcode every subnet CIDR."