Certopolis

HashiCorp Certified: Terraform Associate

count vs for_each and dynamic blocks for Terraform Associate

count takes a whole number and creates that many instances, addressed by index from 0. for_each takes a map or a set of strings and creates one instance per element, addressed by key. Use count for nearly identical instances and for_each when instances need distinct values. A block cannot have both.

count

count = 4 creates four instances: aws_instance.server[0] to aws_instance.server[3]. Inside the block, count.index gives each instance its number. A conditional such as count = var.create ? 3 : 0 creates the resource only when needed.

The count value must be known before Terraform performs any remote operations, so it cannot depend on attributes known only after apply, such as an ID the API generates.

for_each

for_each accepts a map or a set of strings. Inside the block, each.key is the map key or set member and each.value is the map value. For a set, both are the same. Instances are addressed by key, for example azurerm_resource_group.rg["a_group"].

for_each does not convert a list to a set for you. for_each = var.names fails if var.names is a list. Use for_each = toset(var.names), or declare the variable as set(string). toset drops duplicates and ordering.

The keys must be known before apply, cannot come from impure functions such as uuid or timestamp, and cannot be sensitive values, because Terraform shows each key in the UI.

count and for_each work on resource, data, module and ephemeral blocks.

Why the choice matters for a list

Take var.names = ["logs", "media", "assets"]. With count = length(var.names), the buckets are tracked as [0], [1] and [2]. Remove "media" and "assets" moves from index 2 to index 1. The object at [1] must now match "assets", and the object at [2] has no list item left, so the plan changes or replaces one bucket and destroys another.

With for_each = toset(var.names), the buckets are tracked as ["logs"], ["media"] and ["assets"]. Removing "media" removes only that instance, and the other two keep their addresses.

Sets have no order, so Terraform does not let you index them. var.my_set[0] is an error. Convert first with tolist(var.my_set) if you need an element by position.

dynamic blocks

Some resources have repeatable nested blocks, such as ingress in a security group. A dynamic "ingress" block with for_each and a content block generates one nested block per element. Inside content, the iterator is named after the block label (here ingress.key and ingress.value) unless you set iterator.

dynamic blocks work inside resource, data, provider and provisioner blocks. They cannot generate meta-argument blocks such as lifecycle. HashiCorp recommends writing nested blocks out literally where possible and using dynamic blocks mainly to build a clean interface for a reusable module.

Where the exam catches people

  • Believing for_each = var.names works on a list: for_each does not convert lists. Use toset(var.names).
  • Believing you can combine count and for_each on one block: you cannot.
  • Believing a set can be indexed with [0]: sets are unordered. Convert with tolist() first.
  • Believing a dynamic block can generate lifecycle: dynamic blocks cannot generate meta-argument blocks.
  • Believing count or for_each can use an ID created during apply: the value must be known before remote operations start.

Written from HashiCorp's documentation

Try 3 questions on this

Pick an answer to see why each option is right or wrong.

  1. One bucket is needed per name in a variable holding a list of strings. A name removed from the middle later must not disturb the other buckets. Which of the following meets both?

    Right. In the game that’s Renown for your Terraform configuration building.

    Not that one: B is right. In the game your Terraform configuration building would lose a little Renown, and this question would come back later.

  2. A security group needs one ingress block per entry in a list of port numbers, written once rather than repeated by hand. Which of the following produces them?

    Right. In the game that’s Renown for your Terraform configuration building.

    Not that one: B is right. In the game your Terraform configuration building would lose a little Renown, and this question would come back later.

  3. A variable is declared as set(string) and an expression indexes it with [0]. What happens when you plan that configuration?

    Right. In the game that’s Renown for your Terraform configuration building.

    Not that one: D is right. In the game your Terraform configuration building would lose a little Renown, and this question would come back later.