Explanation
This error is often caused by passing a list to for_each
, but for_each
only works with unordered data-types, i.e. with sets and maps.
Solution
The resolution depends on the situation.
List of strings
If the list is just a list of strings, the easiest fix is to add a toset()
-call to transform the list to a set that can be handled by for_each, like this
resource "aws_ssm_parameter" "foo" {
for_each = toset(["a", "b"])
name = "foo-${each.value}"
type = "String"
value = "bar-${each.value}"
}
List that can be rearranged to a map
If the input is a list, but easily be rearranged to a map this is usually the best way.
Say we have a list like this
locals {
animals = [
{
name = "Bello"
age = 3
type = "dog"
},
{
name = "Minga"
age = 4
type = "cat"
},
]
}
Then an appropriate re-structuring might be this
locals {
animals = {
Bello : {
age = 3
type = "dog"
},
Minga : {
age = 4
type = "cat"
}
}
}
which then allows you to define
resource "aws_ssm_parameter" "foo" {
for_each = local.animals
name = each.key
type = string
value = "This is a ${each.value.type}, ${each.value.age} years old."
}
List that you do not want to rearrange
Sometimes it is natural to have a list, e.g. comming from an output of a module that one does not control or from a resource that is defined with count
. In such a situation, one can either work with count like this
resource "aws_ssm_parameter" "foo" {
count = length(local.my_list)
name = my_list[count.index].name
type = "String"
value = my_list[count.index].value
}
which works for a list of maps containing name and value as keys. Often times, though, it is more appropriate to transform the list to a map instead like this
resource "aws_ssm_parameter" "foo" {
for_each = { for x in local.my_list: x.id => x }
name = each.value.name
type = "String"
value = each.value.value
}
Here one should choose anything appropriate in place of x.id
. If my_list
is a list of objects, there is usually some common field like a name or key, that can be used. The advantage of this approach in favor of using count
as above, is that this behaves better when inserting or removing elements from the list. count
will not notice the insertion or deletion as such and will hence update all resources following the place where the insertion took place, while for_each
really only adds or removes the resource with the new or deleted id.