Terraform 20: for_each over a map/set with each.key / each.value
easy⏱ 5 mincourseterraform
One instance per map entry
With for_each = <map>, each instance is addressed by its key — azurerm_servicebus_queue.q["orders"] — not an index. each.key is the map key and each.value is the value (often an object of settings). Adding or removing a key touches only that one instance; the others are untouched.
locals {
queues = {
orders = { max_size_mb = 1024, dlq = true }
billing = { max_size_mb = 2048, dlq = true }
notifications = { max_size_mb = 512, dlq = false }
}
}
resource "azurerm_servicebus_queue" "q" {
for_each = local.queues
name = each.key # "orders", "billing", ...
namespace_id = azurerm_servicebus_namespace.main.id
max_size_in_megabytes = each.value.max_size_mb
dead_lettering_on_message_expiration = each.value.dlq
}
for_each over a set (toarray -> toset)
To fan out over a plain list of names, convert it with toset(...) — for_each requires a set or map, not a list. With a set, each.key and each.value are the same string. This is the go-to for role assignments or secrets keyed by a name.
variable "reader_principal_ids" {
type = set(string)
default = []
}
resource "azurerm_role_assignment" "kv_readers" {
for_each = var.reader_principal_ids # already a set
scope = azurerm_key_vault.orders.id
role_definition_name = "Key Vault Secrets User"
principal_id = each.value # == each.key for a set
}
for_each keys must be known at plan time
The keys of a for_each map/set cannot depend on values only known after apply (e.g. a resource id Terraform hasn't created yet). Key by inputs you control — service names, env names, static ids. Prefer for_each over count whenever items have names: no churn when the collection changes.
Compute for_each instance addresses
Given the map { orders: {...}, billing: {...}, notifications: {...} }, write forEachAddresses(resource, map) returning addresses like azurerm_servicebus_queue.q["orders"] for every key. Log JSON.stringify(result) (expect 3 entries keyed by name, not index).