Terraform Cheatsheet

Expressions and Functions

Use this Terraform reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Types of Expressions

# Literal values
42
true
"hello"
["a", "b", "c"]
{ key = "value" }

# References
var.instance_type
local.name_prefix
aws_instance.web.public_ip
module.vpc.vpc_id
data.aws_ami.ubuntu.id
count.index
each.key / each.value
path.module / path.root / path.cwd
terraform.workspace

String Interpolation and Templates

name     = "server-${var.environment}-${count.index}"
endpoint = "https://${aws_lb.api.dns_name}/api"

# Heredoc
user_data = <<-EOT
  #!/bin/bash
  echo "ENV=${var.environment}" >> /etc/app.conf
  systemctl start myapp
EOT

# Heredoc with template directives
hosts = <<-EOT
  %{ for ip in var.private_ips ~}
  ${ip}
  %{ endfor ~}
EOT

Conditional Expression (ternary)

instance_type = var.is_prod ? "t3.large" : "t3.micro"
count         = var.enable_replica ? 2 : 0
subnet_id     = var.use_private ? aws_subnet.private.id : aws_subnet.public.id

for Expressions

# List → list
upper_names = [for s in var.names : upper(s)]

# List → list with filter
long_names = [for s in var.names : s if length(s) > 5]

# Map → map
upper_tags = { for k, v in var.tags : k => upper(v) }

# Map → list
tag_strings = [for k, v in var.tags : "${k}=${v}"]

# List → map (index as key)
indexed = { for i, v in var.names : i => v }

# Resource for_each → map output
instance_ids = { for k, v in aws_instance.app : k => v.id }

Splat Expressions

# Full splat — [*] — the modern form (shorthand for a for expression)
all_ips = aws_instance.web[*].private_ip
# Equivalent: [for o in aws_instance.web : o.private_ip]

# Splats don't nest — [*] only applies at one level.
# For list-of-lists attributes, flatten the result:
all_sg_ids = flatten(aws_instance.web[*].vpc_security_group_ids)

# Legacy attribute-only splat (pre-0.12) — avoid in new code
all_ips_legacy = aws_instance.web.*.private_ip

try() and can()

# try() — returns the first argument that evaluates without error
image = try(var.config.image, "nginx:latest")
port  = try(var.listeners[0].port, 8080)

# can() — true if the expression evaluates without error
validation {
  condition     = can(regex("^ami-", var.ami_id))
  error_message = "ami_id must start with \"ami-\"."
}

For optional object attributes with defaults, prefer optional(type, default) in the variable type over deep try() chains.

Date/Time and ID Functions

FunctionExampleNotes
timestamp()"2026-07-05T12:00:00Z"changes every apply — pair with ignore_changes
plantimestamp()fixed at plan timestable within one plan/apply (1.5+)
formatdate(spec, ts)formatdate("YYYY-MM-DD", timestamp())
timeadd(ts, dur)timeadd(timestamp(), "24h")
timecmp(a, b)-1 / 0 / 1compare timestamps
uuid()random UUIDnew every run — prefer the random_uuid resource

String Functions

FunctionExampleResult
upper(s)upper("hello")"HELLO"
lower(s)lower("WORLD")"world"
trimspace(s)trimspace(" hi ")"hi"
trim(s, cutset)trim("!!hi!!", "!")"hi"
chomp(s)removes trailing newline
split(sep, s)split(",", "a,b,c")["a","b","c"]
join(sep, list)join(", ", ["a","b"])"a, b"
replace(s, sub, rep)replace("a-b", "-", "_")"a_b"
format(fmt, ...)format("%s-%d", "app", 1)"app-1"
formatlist(fmt, list)formatlist("item-%s", ["a","b"])["item-a","item-b"]
substr(s, off, len)substr("hello", 1, 3)"ell"
startswith(s, prefix)startswith("hello", "he")true
endswith(s, suffix)endswith("hello", "lo")true
regex(pattern, s)match and capture groups
regexall(pattern, s)all matches as list

Number Functions

FunctionExampleResult
abs(n)abs(-5)5
ceil(n)ceil(1.2)2
floor(n)floor(1.9)1
max(...)max(3, 1, 4)4
min(...)min(3, 1, 4)1
pow(x, y)pow(2, 8)256
signum(n)signum(-3)-1

Collection Functions

FunctionExampleResult
length(v)length([1,2,3])3
concat(a, b)concat(["a"], ["b"])["a","b"]
flatten(list)flatten([[1,2],[3]])[1,2,3]
distinct(list)distinct([1,1,2])[1,2]
compact(list)removes null/""
slice(list, s, e)slice([1,2,3,4], 1, 3)[2,3]
reverse(list)reverse([1,2,3])[3,2,1]
sort(list)lexicographic sort
contains(list, v)contains(["a","b"], "a")true
index(list, v)position of first match
element(list, i)element(["a","b"], 2)"a" (wraps)
keys(map)sorted list of keys
values(map)list of values (sorted by key)
lookup(map, k, d)lookup(m, "x", "default")
merge(m1, m2)merge maps (m2 wins)
zipmap(keys, vals)construct map from two lists
toset(list)toset(["a","a","b"]){"a","b"}
tolist(set)convert set to list
tomap(obj)convert object to map

Type Conversion

tostring(42)          # "42"
tonumber("42")        # 42
tobool("true")        # true
tolist(toset(["a"]))  # ["a"]

Encoding Functions

FunctionUse
jsonencode(val)encode value as JSON string
jsondecode(str)parse JSON string
yamlencode(val)encode as YAML
yamldecode(str)parse YAML
base64encode(str)base64 encode
base64decode(str)base64 decode
base64gzip(str)gzip then base64
filebase64(path)read file and base64 encode

Filesystem Functions

file("config/app.conf")           # read file as string
filebase64("certs/ca.pem")        # read and base64 encode
fileset(path.module, "files/*")   # list files matching glob

# templatefile() — render a template file with variables
user_data = templatefile("${path.module}/init.tftpl", {
  env  = var.environment
  port = 8080
})
# init.tftpl — same ${} interpolation and %{ for }/%{ if } directives as heredocs
#!/bin/bash
echo "ENV=${env}" >> /etc/app.conf
systemctl start "app-${port}"

IP / CIDR Functions

cidrsubnet("10.0.0.0/16", 8, 0)   # => "10.0.0.0/24"
cidrsubnet("10.0.0.0/16", 8, 1)   # => "10.0.1.0/24"
cidrhost("10.0.0.0/24", 5)        # => "10.0.0.5"
cidrnetmask("10.0.0.0/24")        # => "255.255.255.0"

Useful Patterns

# Default value if null
name = coalesce(var.override_name, "default-name")
# First non-null, non-empty from list
region = coalesce(var.region, "us-east-1")

# Conditional resource creation
count = var.create_bucket ? 1 : 0

# Map lookup with default
size = lookup(local.size_map, var.environment, "t3.micro")

# Build subnet CIDRs dynamically
subnets = [for i in range(3) : cidrsubnet(var.vpc_cidr, 8, i)]

# Merge with override
final_tags = merge(local.default_tags, var.extra_tags)

# one() — assert list has exactly one element
vpc_id = one(data.aws_vpcs.selected.ids)