Build map using 2 list(string)

Hi all, Hope all are well during this difficult context :wink:

I don’t find a good solution to build a map using 2 list of strings, sorry, perhaps I’m bad :wink:

I have :

  • variable A
    list(string) with : [“subnet-1”, Subnet-2"]

  • Variable B
    list(string) with : [“172.10.0.0/16”, “10.50.0.0/16”]

And I want to build a local object with :
Subnet-1 => 172.10.0.0/16
Subnet-1 => 10.50.0.0/16
Subnet-2 => 172.10.0.0/16
Subnet-2 => 10.50.0.0/16

To do a for_each in another resource :wink:

Do you have any clue ?

Thanks

If I try a setproduct function I have (for a for_each in another resource)

The given “for_each” argument value is unsuitable: the “for_each” argument
must be a map, or set of strings, and you have provided a value of type list
of tuple.

Hi @opsrom,

The example you gave of your intended result shows four map elements, but only two unique keys. A map is a data structure where each element is identified by a unique string, so a map exactly like you showed isn’t possible, but maybe you meant a map of lists instead, like this?

{
  Subnet-1 = ["172.10.0.0/16", "10.50.0.0/16"]
  Subnet-2 = ["172.10.0.0/16", "10.50.0.0/16"]
}

If so, I think something like this would get that result:

locals {
  subnet_ranges = {
    for pair in setproduct(var.A, var.B) : pair[0] => pair[1]...
  }
}

The above uses a less-common feature of for expressions: the ... after the value expression activates grouping mode, where each unique key maps to a list of all of the values corresponding to that key.

If I misunderstood what you’re trying to do, it would be helpful if you could share a little more about the underlying problem you are trying to solve – that is, your current goal rather than your current idea for how to meet that goal – and then I might be able to offer some broader suggestions on how to get that done with different Terraform language features.

I try to build an impossible map… I’m silly !

My goal is to call a aws_cludformation_stack with theses values (so 4 stacks):

  • subnet 1 with cidr 1
  • subnet 1 with cidr 2
  • subnet 2 with cidr 1
  • subnet 2 with cidr 2

template_body = <<STACK
Resources:
AuthRule:
Type: AWS::EC2::ClientVpnRoute
Properties:
ClientVpnEndpointId: ${aws_cloudformation_stack.vpnclient[0].id}
TargetVpcSubnetId: ${???}
DestinationCidrBlock: ${???}
STACK
}

Perhaps I need to build a map like this :

routes = {
    route1 = {
      subnet = "subnet1"
      cidr   = "cidr1"
    }
    route2 = {
      subnet = "subnet1"
      cidr   = "cidr2"
    }
    route3 = {
      subnet = "subnet2"
      cidr   = "cidr1"
    }
    route4 = {
      subnet = "subnet2"
      cidr   = "cidr2"
    }
  }

So I can do a clean for_each and use each.value.subnet to retreive value
But I don’t know how to build this kind of map using my 2 list(tring) variables ? :frowning:

Thank you

I think I have find the solution… happy saturday morning :slight_smile:

locals {
  subnet_ids = ["subnetID1", "subnetID2"]
  cidrs = ["xx.xx.xx.xx/16", "yy.yy.yy.yy/16"]

  results = setproduct(local.subnet_ids, local.cidrs)
  routes = [
    for item in local.results : merge({subnet = item[0]}, {cidr = item[1]})
  ]
}

resource "random_pet" "test" {
  count = length(local.routes)

  prefix = format("%s-%s", local.routes[count.index]["subnet"], local.routes[count.index]["cidr"])
  length = 5
}

I can’t do a for_each but seems to work with count (not the best but it probably works)
I have to test in my conrete project but seems good :wink:

Thanks all.

Hi @opsrom! I’m glad you found a working solution. Here’s an adaptation of your most recent example to use for_each instead, by constructing a map from your list:

resource "random_pet" "test" {
  for_each = {
    for r in local.routes : "${r.subnet}-${r.cidr}" => r
  }

  prefix = format("%s-%s", each.value.subnet, each.value.cidr)
  length = 5
}

The for expression in the for_each argument projects your list into a map with generated keys like subnetID1-xx.xx.xx.xx/16, which contain all of the values from the object and will thus be unique across all values in the local.routes list.

Terraform will then track these with addresses based on those keys:

  • random_pet.test["subnetID1-xx.xx.xx.xx/16"]
1 Like

I will try this thank you :wink: