How to create resources from a list of objects?

I have a list of objects, and I want to use the object information to create resources. How is that possible?

let’s assume I have this “object_list”

[
  {
  "id" = "abc1",
  "target" = "xyz1"
  },
  {
  "id" = "abc2",
  "target" = "xyz2"
  }
]

when applying that list to resources:

resource "myresource" "example" {
  for_each = object_list
  resource_id = each.value.id
  resource_target = each.value.target
}

obviously doesn’t work - but I am running out of ideas how to reach these resources I want to create (pseudo-code):

example[0]
{
  resource_id = "abc1"
  resource_target = "xyz1"
}
example[1]
{
  resource_id = "abc2"
  resource_target = "xyz2"
}

Any ideas? Thanks!

Hi @selfscrum,

Typically when using for_each the goal is for the multiple instances of the resource to be identified by some meaningful unique key, so that adding and removing objects from the list later will add and remove the corresponding instances, rather than basing decisions on the positioning of items in the list.

If your id attribute is expected to be unique across all of the objects then I would recommend to convert your list into a map where the ids are the keys, like this:

resource "myresource" "example" {
  for_each = { for o in var.object_list : o.id => o }

  resource_id     = each.key
  resource_target = each.value.target
}

This uses a for expression to project the list of objects into a map of objects, using the id attribute as the key for each element. Terraform would then understand this as intent for the following two instances to exist:

  • myresource.example["abc1"]
  • myresource.example["abc2"]

If having multiple instances identified by their indices within the source list is important to you for some special reason then you can get that result by using count instead of for_each and then fixing the count to the length of the list:

resource "myresource" "example" {
  count = length(var.object_list)

  resource_id     = var.object_list[count.index].id
  resource_target = var.object_list[count.index].target
}

This one will instead declare instances with the following addresses, corresponding to what you showed in your example:

  • myresource.example[0]
  • myresource.example[1]

An important difference here is that if you were to insert a new element into your list between the two existing elements, the for_each technique would just declare a new instance with the given id, but the count technique would describe an update to myresource.example[1] to make it match the new element you added and the creation of myresource.example[2] to match what was previously myresource.example[1]. For some resource types that can lead to errors because Terraform might try to create the new myresource.example[2] before the update of myresource.example[1] has completed, causing there to be two instances with the same resource_id.

Thanks @apparentlymart, very helpful! I am still confused with the => part but now I can understand what it is about. Explanations like those are the missing (and missed) pieces in the official docs :wink: