All guides
Practice now
D4 · Terraform Configuration

Expressions, functions and meta-arguments

create multiple instances of a . For expressions and conditionals let you transform data. Built-in functions are essential tools for everyday work and the exam.

count meta-argument

count creates N instances of the resource, indexed from 0 to N-1. Each instance is identified by its numeric index.

# Create 3 EC2 instances
resource "aws_instance" "web" {
  count         = 3                       # integer number
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "web-${count.index}"          # count.index: 0, 1, 2
  }
}

# Address of each instance:
# aws_instance.web[0]
# aws_instance.web[1]
# aws_instance.web[2]

# Access in outputs or references:
output "web_ids" {
  value = aws_instance.web[*].id         # splat: list of all the IDs
}

# Use a variable for the count:
variable "instance_count" { default = 3 }

resource "aws_instance" "app" {
  count = var.instance_count
  # ...
}

# Conditional creation (count = 0 = don't create, count = 1 = create):
resource "aws_eip" "nat" {
  count  = var.create_nat_gateway ? 1 : 0
  domain = "vpc"
}

for_each meta-argument

for_each creates one instance for each element of a set or map. Each instance is identified by its key (string), not by a numeric index.

# for_each with a SET of strings
resource "aws_iam_user" "team" {
  for_each = toset(["alice", "bob", "carol"])   # converts list to set
  name     = each.key                           # each.key == the set element
}

# Address of each instance:
# aws_iam_user.team["alice"]
# aws_iam_user.team["bob"]
# aws_iam_user.team["carol"]

# for_each with a MAP (key → object)
variable "buckets" {
  default = {
    logs    = { region = "us-east-1", versioning = true }
    backups = { region = "us-west-2", versioning = false }
  }
}

resource "aws_s3_bucket" "storage" {
  for_each = var.buckets
  bucket   = "company-${each.key}"             # each.key = "logs" or "backups"
}

resource "aws_s3_bucket_versioning" "storage" {
  for_each = var.buckets
  bucket   = aws_s3_bucket.storage[each.key].id  # reference with the same key

  versioning_configuration {
    status = each.value.versioning ? "Enabled" : "Suspended"
  }
}

# Splat does not work with for_each — use a for expression instead:
output "bucket_ids" {
  value = { for k, v in aws_s3_bucket.storage : k => v.id }
}

count vs for_each

Featurecountfor_each
Accepted value typenumberset(string) or map(any)
Instance identifierNumeric index (0, 1, 2…)String key ("alice", "logs"…)
resource[0], resource[1]resource["key"]
Element accesscount.index (integer)each.key (key) + each.value (value)
What happens when deleting the 2nd of 3⚠️ Terraform renames the 3rd to the 2nd and DESTROYS+RECREATES✅ Only destroys the deleted element, the others do not change
When to use itIdentical resources with no identity of their own (replicas)Resources with a unique identity (users, named buckets)
RecommendationSimple cases or conditional creation (0 or 1)Preferred for most multi-resource cases

⚠️ The problem with count and changing lists

If you have count = 3 with instances [0]=alice, [1]=bob, [2]=carol and you remove bob (the middle element), Terraform sees: [0]=alice, [1]=carol. Since [1] changed from "bob" to "carol", it destroys and recreates resource [1]. With for_each, each resource has a stable key, and removing "bob" only destroys that resource without affecting the others.

for expressions and conditionals

# for expression — generates lists or maps
# [for <item> in <collection> : <expression>]
# {for <key>, <val> in <map> : <new_key> => <new_val>}

variable "users" {
  default = [
    { name = "alice", admin = true },
    { name = "bob",   admin = false },
  ]
}

# List of names
locals {
  names = [for u in var.users : u.name]
  # result: ["alice", "bob"]

  # Only the admins (with if condition)
  admin_names = [for u in var.users : u.name if u.admin]
  # result: ["alice"]

  # Map name → admin
  user_map = {for u in var.users : u.name => u.admin}
  # result: {alice = true, bob = false}
}

# Conditional expression (ternary)
# condition ? value_if_true : value_if_false
resource "aws_instance" "web" {
  instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
}

💡 For expressions vs for_each

For expressions ([for x in list : x.name]) transform collections into locals or values — they do not create resources. The for_each meta-argument creates multiple instances of a resource. They are different concepts despite the similar-sounding names.

Built-in functions by category

Terraform includes dozens of built-in functions. These are the most important ones for the exam, organized by category.

String

format(fmt, ...args)

Formats a string printf-style: format("Hello, %s!", "world")

join(sep, list)

Joins a list into a string: join(",", ["a","b"]) → "a,b"

split(sep, str)

Splits a string into a list: split(",", "a,b") → ["a","b"]

upper(str) / lower(str)

Converts to upper/lower case

trimspace(str)

Removes leading and trailing whitespace and newlines

replace(str, sub, new)

Replaces a substring: replace("hello", "l", "r") → "herro"

Collection

length(list|map|str)

Number of elements: length(["a","b"]) → 2

concat(list1, list2)

Combines lists: concat(["a"],["b"]) → ["a","b"]

flatten(list)

Flattens nested lists: flatten([["a"],["b","c"]]) → ["a","b","c"]

merge(map1, map2)

Combines maps (map2 overrides map1 keys)

lookup(map, key, def)

Looks up a key in a map with a default: lookup({a=1}, "b", 0) → 0

toset(list)

Converts a list to a set (removes duplicates, needed for for_each)

keys(map) / values(map)

List of keys or values of a map

contains(list, val)

True if the list contains the value: contains(["a","b"],"a") → true

Encoding

jsonencode(value)

Converts an HCL value to a JSON string: jsonencode({a="b"}) → {"a":"b"}

jsondecode(str)

Parses a JSON string into an HCL type

base64encode(str)

Encodes a string in Base64

base64decode(str)

Decodes a Base64 string

file(path)

Reads the contents of a file as a string

templatefile(path, vars)

Renders a .tftpl template with variables

Numeric and others

max(n1, n2, ...) / min(...)

Maximum or minimum of the given numbers

abs(n)

Absolute value

ceil(n) / floor(n)

Rounds up / rounds down

coalesce(v1, v2, ...)

First non-null, non-empty value in the list

try(expr, fallback)

Evaluates expr; if it fails, returns fallback (useful for optional attributes)

can(expr)

True if expr does not produce an error (for validation)

Did you get this topic?

Test what you just learned

A module manages 3 subnets with for_each using the keys 'public-a', 'public-b' and 'private-a'. The team decides to remove 'public-b' from the map. What will happen on the next terraform apply?

Ready to test "Expressions, Functions and Meta-Arguments"?

Put what you learned to the test with original Terraform Associate exam questions.

Start Terraform Associate mock exam

Unlimited mock exams with the Pro plan

Log in to track your progress.