For_each objects to list

I need to get the results of the following objects from the for_each loop into an array to use as an attribute within another resource.

client_vpce_ids= [
    {
        account_id= "123456789101",
        vpc_id= "vpce-xxxxxxxxxxxxxxx"
    },
    {
        account_id= "123456789102",
        vpc_id= "vpce-yyyyyyyyyyyyyyy"
    }
]

resource "first_resource" "first_example" {

  for_each = { for vpce in var.CLIENT_VPCE_IDS : vpce.account_id => vpce }

  name   = "${each.value.account_id}-access-vpce"

 rule {

    source = "${each.value.vpc_id}"

  }

}

I then need to add all the first_resource.first_example object ids to an array to use in another resource e.g.

resource "second_resource" "second_example" {

    array = [first_resource.first_example[key1].id,
                  first_resource.first_example[key2].id
                   ]
}

But I’m struggling to know either how to work out what key1,key2,key3 etc… should be and how to dynamically add\remove them. I’ve tried with locals e.g.

locals {

    for_each = first_resource.first_example
    array = each.value.id

}

But this isn’t supported.

Any help very much appreciated.

Hi @jenny-law,

The for_each argument is for declaring multiple instances of a resource or module using a single resource, data, or module block.

However, the situation you’re describing here is manipulating a data structure rather than declaring resources/modules, and any time we’re manipulating values we use expressions.

For projecting one data structure in another the most relevant kind of expression is for expressions, which take one collection as input and produce another collection as the result.

So with all of that said, the problem you’ve presented here is taking a map of objects as produced by a resource with a for_each block, and projecting that into a list of values taken from the id attributes of those objects. You can write that as follows:

  array = [
    for o in first_resource.first_example : o.id
  ]

I called this argument “array” to match what you shared but please note that there’s no such data type as “array” in Terraform. Instead, this argument probably requires either a list or a set. The difference between these is that a list preserves the order of the objects and allows duplicates, whereas a set is just like a bag of values that are in no particular order and have no duplicates.

Most of the time you don’t need to worry too much about this distinction, but I mention it here because map elements are not ordered either and so projecting a map into a list as shown above requires Terraform to choose an ordering for the map. Terraform will visit the map elements in lexical order by map key, so if this resource argument is expecting a list then the ordering of the IDs will be the same as the sorted order of the map keys. If the second resource type actually expects a set then this won’t matter, because Terraform will automatically convert the list into a set and thus discard the ordering.

Thank you that’s really helpful. I’m having other issues. If I wanted to take a list of maps,

e.g.

variable "TAGS"=[
{"Key":"billing_team","Value":"santa"},
"Key":"cost_centre","Value":"200142"}
]

and make
tags={"billing_team"="santa", "cost_centre"="200142" }

I'm trying various version of 
locals {
  tags = [  

  for key, value in var.TAGS: map({value[0] = value[1]})

 ]
}

but I’m missing something with the logic still…

Finally got there
locals {

keys = [

for map in var.TAGS: [

for key, value in map: value ]

]

tags = [

for str in local.keys:

{

tag = tomap({"${str[0]}" = "${str[1]}"})

}

]

mandatory_tags = merge(local.tags[*].tag…)

}

Not sure if this is the best\ most elegant method but… it works