Terraform State: Creating Managed Resources with For_Each
Scenario
Your microservices architecture defines 3 Pub/Sub topics: orders, payments, and shipping. Instead of writing three resource blocks, or using count (which would break violently if you later removed "orders" from the middle of the list), you should use the for_each meta-argument. for_each tracks resources by a string key, making it safe to add, remove, and modify items dynamically.
- Define a variable topics as a set(string) containing the topic names.
- Use the for_each meta-argument within a google_pubsub_topic block.
- Use each.key to set the name and label of the topic.
- Output the generated topic self-links using a for expression.
Additional Context
Why for_each dominates count: When using `for_each`, Terraform maps the resources in its state file as `google_pubsub_topic.events["orders"]`. If you remove "orders" from the variable set, Terraform knows exactly which topic to destroy, leaving the other two entirely untouched.
Sets vs Lists: `for_each` requires its input to be either a `set(string)` or a `map`. You cannot directly pass a `list` to it because lists are ordered arrays and rely on indices (which brings back the `count` problem). You can convert a list to a set using the `toset()` function.
variables.tf
variable "project_id" { type = string }
variable "topics" {
type = set(string)
description = "A set of topic names to create"
default = ["orders", "payments", "shipping"]
}main.tf
provider "google" {
project = var.project_id
}
resource "google_pubsub_topic" "events" {
# Iterates over every element in the set
for_each = var.topics
# each.key represents the current string from the set
# (For maps, you'd use each.key and each.value)
name = "${each.key}-events"
labels = {
managed_by = "terraform_foreach"
domain = each.key
}
}outputs.tf
# The splat operator [*] DOES NOT WORK with for_each.
# You must use a `for` expression to iterate over the resulting map.
output "topic_ids" {
value = { for key, topic in google_pubsub_topic.events : key => topic.id }
}Workflow Commands
terraform init
terraform apply -var="project_id=my-gcp-project-123"
# Observe the state architecture (tracked by string keys)
terraform state list
# Outputs:
# google_pubsub_topic.events["orders"]
# google_pubsub_topic.events["payments"]
# google_pubsub_topic.events["shipping"]
# If you run terraform apply -var='topics=["payments","shipping"]'
# Terraform will safely destroy only the "orders" topic.
terraform destroy -var="project_id=my-gcp-project-123"Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
topic_ids = {
"orders" = "projects/my-gcp-project/topics/orders-events"
"payments" = "projects/my-gcp-project/topics/payments-events"
"shipping" = "projects/my-gcp-project/topics/shipping-events"
}
