Terraform: converting ordered lists to sets to avoid errors with for_each

If you are using a Terraform “for_each” and get the error message below, it is most likely because you are sending an ordered list instead of an unordered set (which is not supported at the resource level).

The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type tuple.

If one of the fields in your list is uniquely identifiable, then you can convert the ordered list to a set.  Here is an example:

locals {
  my_list = [
    { "id":"a", "name":"first" },
    { "id":"b", "name":"second" },
    { "id":"c", "name":"third" }
  ]
}

resource "null_resource" "show_list" {
  # would FAIL !!!
  # for_each =  local.my_list

  # OK! convert ordered list to set
  for_each = { for entry in local.my_list: entry.id=>entry }

  provisioner "local-exec" {
  command = "echo my_list for_each: the name for ${each.value.id} is ${each.value.name}"
  }
}

If you did not have a unique field for each row, this would lead to key collision and Terraform would error out during the conversion.  In that situation you should use “count” instead of for_each.


In the example below, we use “count” instead of for_each to work on an ordered list.  Note that “id” is not unique anymore, but that does not matter.

locals {
  my_list = [
    { "id":"a", "name":"first" },
    { "id":"a", "name":"second" },
    { "id":"a", "name":"third" }
  ]
}

resource "null_resource" "show_list_with_count" {
  count = length(local.my_list)
  provisioner "local-exec" {
    command = "echo my_list count: the name for ${local.my_list[count.index].id} is ${local.my_list[count.index].name}"
  }
}

REFFERENCES

terraform, count

terraform, for_each

NOTES

for_each does NOT support lists at the resource level, but does at the dynamic block level