Register multiple instances to multiple target groups by using for_each

I am trying to register multiple instances to multiple target groups by using for_each.
I have two variables as list values and i am trying to convert to map.

target_groups = [“target1”,“target2”,“target3”]
instace_ids = [“id1”,“id2”]

locals {
clusteratgs_map = flatten([
for tg, id in var.instace_ids : [
for tg in var.target_groups : [
format("%s = %s", tg, id)
]
]
])
}

Output:
map_of_targets_ips = [
“target1 = id1”,
“target1 = id2”,
“target2 = id1”,
“target2 = id2”,
“target3 = id1”,
“target3 = id2”,
]

Expecting output:-

map_of_targets_ips = {
target1 = id1
target1 = id2
target2 = id1
target2 = id2
target3 = id1
target3 = id2
}

resource “aws_lb_target_group_attachment” “clustera” {
for_each = var.map_of_targets_ips
target_group_arn = each.key
target_id = each.value
}

You can’t have a map where the same key appears multiple times, that’s not how maps work.

But you don’t need to either as you can use the two lists directly in the resource declaration. I stumbled across this example in the docs which seems very much like what I think you want to achieve.

Maybe something like this:

resource “aws_lb_target_group_attachment” “clustera” {
  for_each = toset( setproduct(var.target_groups,var.instace_ids)  )
  target_group_arn = each.key[0]
  target_id = each.key[1]
}

Thanks a lot for your reply @bentterp .

I have tried your solution but no luck. below are the details.
Able to fetch map variable for for_each as below but at the resource level getting error.

map_of_targets_ips = [
[
“target1”,
“id1”,
],
[
“target1”,
“id2”,
],
[
“target2”,
“id1”,
],
[
“target2”,
“id2”,
],
]

resource “aws_lb_target_group_attachment” “clustera” {
for_each = toset( setproduct(var.target_groups,var.instace_ids) )
target_group_arn = each.key[0]
target_id = each.key[1]
}

Error: **
** in resource “aws_lb_target_group_attachment” “clustera”:

** 19: target_group_arn = each.key[0]**

This value does not have any indices.

Okay… this is a bit like driving while blindfolded :thinking:

Does it work better with each.value than with each.key?

Else, try and remove the indices and do a plan, maybe we can at least see the values so we can figure out what we’re doing wrong.

Using value and removing index resulted in below error.

The given “for_each” argument value is unsuitable: “for_each” supports maps
and sets of strings, but you have provided a set containing type tuple.

Me too looking for a way to print each.key value like “echo” which I couldn’t find any reference.

Okay, my mistake. I thought I could use a set of lists in the for_each construct itself. :slightly_frowning_face:

:thinking: lemme see…

This is not particularly elegant, but it should work.

locals {

  target_groups = ["target1","target2","target3"]
  instance_ids = ["id1","id2"]

  map = {
    for pair in setproduct(local.target_groups,local.instance_ids) :
        "${pair[0]}${pair[1]}" => { target_group = pair[0], instance_id = pair[1] }
  }
  list = keys(local.map)
}

output "tg" {
  value = local.map[local.list.3].target_group
}
output "id" {
  value = local.map[local.list.3].instance_id
}

This way, you have a list to loop over, using for_each = toset(local.list). And then you can pick values from the map using each.key as index.

But there must be better way…

I think this is on the right track… the missing part is to use a map to ensure that each of the instances has a unique string key, maybe like this:

resource “aws_lb_target_group_attachment” “clustera” {
  for_each = {
    for pair in setproduct(var.target_groups,var.instace_ids) : "${pair[0]} ${pair[1]}" => {
      target_group_arn = pair[0]
      target_id        = pair[1]
    }
  }

  target_group_arn = each.value.target_group_arn
  target_id        = each.value.target_id
}

The original example shows constant value ids like id1 and id2, which would produce instances with addresses like aws_lb_target_group_attachment.clustera["target1 id1"].

However, note that for_each requires that the keys of the map be known at planning time so that Terraform can see exactly which instances are needed. ARNs and ids tend to be determined only after apply, so if these values are coming from other resources in practice, rather than from constants as shown here, the above is unlikely to work exactly as written.

When dealing with values that are determined only after the create succeeds, I usually try to design the configuration so that there are some names chosen by me in the configuration that I use as the main identifier for for_each of related resources, and then use expressions to map between the locally-chosen names (which are therefore known at plan time) and the identifiers chosen by the remote system (which are known only during apply). This tends to work best if the source objects (the target groups and instances, in this case) are themselves being defined using for_each, since they will therefore presumably have their own instance keys to use as those local identifiers.

Thanks, @bentterp & @apparentlymart.

It’s working both ways. Going with @apparentlymart way.

Once again thanks a lot for quick responses. This community is awesome and you