LAB23_GCP_Terraform State: Creating Managed Resources with For_Each

Terraform State: Creating Managed Resources with For_Each

☁ GCP 🟢 Beginner Terraform Core HCL for_each Pub/Sub

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.

Your Objectives
  • 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"
✓ Expected Output After Apply
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"
}