# Convert to map(objects)

**URL:** <https://discuss.hashicorp.com/t/convert-to-map-objects/27227>\
**Category:** Terraform\
**Created:** [July 23, 2021, 2:53pm UTC](https://discuss.hashicorp.com/t/convert-to-map-objects/27227 "2021-07-23T14:53:21Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![jprouten](https://avatars.discourse-cdn.com/v4/letter/j/d26b3c/32.png) [@jprouten](https://discuss.hashicorp.com/u/jprouten)\
**Post date:** [July 23, 2021, 2:53pm UTC](https://discuss.hashicorp.com/t/convert-to-map-objects/27227/1 "2021-07-23T14:53:21Z")

</div>

Is there a way of converting the value of the below local (local.nlb\_config) to use in a for\_each?

```auto

nlb_config= [
  {
    "ec2_id" = "i-yyyyyyyyyyyyyy"
    "nlb_target_name" = "example-nlb1"
    "nlb_target_port" = "3689"
    "nlb_target_protocol" = "TCP"
    "nlb_target_type" = "instance"
  },
  {
    "ec2_id" = "i-xxxxxxxxxx"
    "nlb_target_name" = "example-nlb1"
    "nlb_target_port" = "3689"
    "nlb_target_protocol" = "TCP"
    "nlb_target_type" = "instance"
  },
  {
    "ec2_id" = "i-yyyyyyyyyyyyyy"
    "nlb_target_name" = "example-nlb2"
    "nlb_target_port" = "53"
    "nlb_target_protocol" = "TCP"
    "nlb_target_type" = "instance"
  },
  {
    "ec2_id" = "i-xxxxxxxxxx"
    "nlb_target_name" = "example-nlb2"
    "nlb_target_port" = "53"
    "nlb_target_protocol" = "TCP"
    "nlb_target_type" = "instance"
  },
]

```

I have constructed that local but now need it in the correct format to use in a for\_each. The local was created like below:

```auto

variable "target_id" {
type = map(object({
    id = string
    availability_zone = string
    tags = map(string)
  }))
}
variable "nlb_target_group" {
  type = map(object({
    nlb_target_name = string
    nlb_target_port = string
    nlb_target_protocol = string
    nlb_target_type = string
    #health_check = map(string)
  }))
}

nlb_target_group = {
  group1 = {
    nlb_target_name = "example-nlb1"
    nlb_target_port = "3689"
    nlb_target_protocol = "TCP"
    nlb_target_type = "instance"
  },
  group2 = {
    nlb_target_name = "example-nlb2"
    nlb_target_port = "53"
    nlb_target_protocol = "TCP"
    nlb_target_type = "instance"
  }
}

locals {
  ec2sflat = [
    for key, ec2 in var.target_id : {
      ec2_id = ec2.id
    }
  ]

  nlbsflat = [
    for key, nlb in var.nlb_target_group : {
      key = key
      nlb_target_name = nlb.nlb_target_name
      nlb_target_port = nlb.nlb_target_port
      nlb_target_protocol = nlb.nlb_target_protocol
      nlb_target_type = nlb.nlb_target_type
    }
  ]

  nlb_config = [
    for pair in setproduct(local.nlbsflat, local.ec2sflat) : merge(pair[0], pair[1])
  ]
}

```

I then want to use local.nlb\_config in a for\_each like below

```auto

resource "aws_lb" "this" {
  name = var.nlb_name
  internal = var.internal
  load_balancer_type = "network"
  subnets = var.nlb_subnets

  enable_deletion_protection = var.enable_deletion_protection
  enable_cross_zone_load_balancing = var.enable_cross_zone_load_balancing
}

resource "aws_lb_target_group" "this" {
  for_each = local.nlb_config
  name = each.value.nlb_target_name
  port = each.value.nlb_target_port
  protocol = each.value.nlb_target_protocol
  target_type = each.value.nlb_target_type
  vpc_id = var.vpc_id

}

resource "aws_lb_target_group_attachment" "this" {
  for_each = local.nlb_config
  target_group_arn = aws_lb_target_group.this[each.key].arn
  target_id = each.value.ec2_id
  port = var.target_group_attachment_port
}

resource "aws_lb_listener" "this" {
  for_each = local.nlb_config
  load_balancer_arn = aws_lb.this.arn
  port = var.nlb_listner_port
  protocol = var.nlb_listner_protocol
  default_action {
    type = var.nlb_listner_default_action_type
    target_group_arn = aws_lb_target_group.this[each.key].arn
  }
}

```

I seem so close so hopefully an easy solution. Thanks

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [July 26, 2021, 5:02pm UTC](https://discuss.hashicorp.com/t/convert-to-map-objects/27227/2 "2021-07-26T17:02:39Z")

</div>

Hi @jprouten,

You do seem close indeed!

The two important rules for `for_each` are:

1. You must have a collection that has one element for each instance you want to declare.
2. It must be possible to select or generate a unique string for each element _only from values defined directly in the configuration_ to serve as its instance key.

It looks like you’ve already dealt with rule 1 in your `local.nlb_config` value, where you have one element for each combination of “nlbs” and “ec2s”, which seems like the right cardinality for an `aws_lb_target_group_attachment`.

I think the challenge for rule 2 is that the only unique identifier you have for your EC2 instances is the ID chosen by the remote system. That _isn’t_ defined directly in your configuration (it’ll only be known after apply) and so it isn’t suitable to use as part of a unique key for `for_each`. Instead, you’ll need to select some other unique key that’s based on something _your configuration_ decided, rather than something the remote system decided.

However, I noticed your `var.target_id` is itself a map of objects and so the keys from _that_ map could potentially serve as the unique keys for your EC2 instances, if you adjust `ec2sflat` like this:

```auto
locals {
  ec2sflat = [
    for key, ec2 in var.target_id : {
      ec2_id = ec2.id
      ec2_key = key
    }
  ]
}

```

To keep things unambiguous, let’s also rename the `key` attribute from `local.nlbsflat` elements to `nlb_key`, and thus the final merged data structure will have both `ec2_key` and `nlb_key` in each of its attributes, and then we can meet rule 2 by projecting the set into a map with another `for` expression:

```auto
resource "aws_lb_target_group_attachment" "this" {
  for_each = {
    for cfg in local.nlb_config : "${cfg.nlb_key}:${cfg.ec2_key}" => cfg
  }

  target_group_arn = aws_lb_target_group.this[each.value.nlb_key].arn
  target_id = each.value.ec2_id
  port = var.target_group_attachment_port
}

```

I think that `local.nlb_config` isn’t the right data structure for some of your other resources though, if I’m understanding the relationships correctly. It seems like you only need one `aws_lb_target_group` and `aws_lb_listener` per entry in `var.nlb_target_group`, because neither of those refer to any of the EC2 properties and so I don’t think you want to create a separate target group and listener for each EC2 instance.

---

<div class="post-metadata">

**Author:** ![jprouten](https://avatars.discourse-cdn.com/v4/letter/j/d26b3c/32.png) [@jprouten](https://discuss.hashicorp.com/u/jprouten)\
**Post date:** [August 3, 2021, 2:30pm UTC](https://discuss.hashicorp.com/t/convert-to-map-objects/27227/3 "2021-08-03T14:30:45Z")

</div>

Thanks. The solution works… but then you are also right that it isn’t right for other resources in the module.
