Search code examples
amazon-web-servicesterraformterraform-provider-awshcl

Combining usage of Terraform's zipmap, maps, and lists


I have two different scenarios, one with public subnets and one with private.

For both, I would like to apply some combination of for-loop and zipmask to have a single map object. You can assume I have checked the ordering of the two input maps/lists and that they are align.

Using the public-subnet as the first example, I have one cidr for each user:

pub_cidr_map = {
  "user2" = "10.0.8.0/21"
  "user4" = "10.0.24.0/21"
  "user1" = "10.0.0.0/21"
  "user3" = "10.0.16.0/21"
}

pub_id_list = [
  "subnet-666666662ee6f3442",
  "subnet-6666666696b92d895",
  "subnet-66666666cbaa4bfb3",
  "subnet-6666666655a09d064",
]

I would like this to look like this so I can access both values with a single key:

pub_lookup_map = {
  "user2" = ["10.0.8.0/21", "subnet-666666662ee6f3442"]
  "user4" = ["10.0.24.0/21", "subnet-6666666696b92d895"]
  "user1" = ["10.0.0.0/21", "subnet-66666666cbaa4bfb3"]
  "user3" = ["10.0.16.0/21", "subnet-6666666655a09d064"]
}

I'd also like to accomplish something similar with my private subets, which are apportioned two-per-user:

priv_cidr_map = {
  "user1" = [
    "10.0.96.0/20",
    "10.0.112.0/20",
  ]
  "user2" = [
    "10.0.160.0/20",
    "10.0.176.0/20",
  ]
  "user3" = [
    "10.0.64.0/20",
    "10.0.80.0/20",
  ]
  "user4" = [
    "10.0.128.0/20",
    "10.0.144.0/20",
  ]
}

priv_id_list = [
  "subnet-666666662f611f9a5",
  "subnet-6666666689f1eff5e",
  "subnet-66666666a3fe6efb9",
  "subnet-66666666faf4a62a8",
  "subnet-666666668f1442700",
  "subnet-66666666328a4b134",
  "subnet-666666661b147a933",
  "subnet-666666661ce02c330"
]

I would like this to look like

priv_lookup_map = {
  "user1" = [
    ["10.0.96.0/20","subnet-666666662f611f9a5"]
    ["10.0.112.0/20","subnet-6666666689f1eff5e"]
  ]
  "user2" = [
    ["10.0.160.0/20","subnet-66666666a3fe6efb9"]
    ["10.0.176.0/20","subnet-66666666faf4a62a8"]
  ]
  "user3" = [
    ["10.0.64.0/20","subnet-666666668f1442700"]
    ["10.0.80.0/20","subnet-66666666328a4b134"]
  ]
  "user4" = [
    ["10.0.128.0/20","subnet-666666661b147a933"]
    ["10.0.144.0/20","subnet-666666661ce02c330"]
  ]
}

I am open to any other structures someone might think are useful; the use case here is to provision subnets and EIPs as part of a separate, stateful deployment of a VPC prior to the deployment of the resources (like EC2, RDS) that will reside within these ranges.


Solution

  • I can not guarantee that this solution is correct, I will share my attempt anyway hoping it might be helpful.

    For the public subnets:

    locals {
      pub_lookup_map = {for key, value in zipmap(keys(var.pub_cidr_map), var.pub_id_list) : key => [var.pub_cidr_map[key], value] }
    }
    

    This will produce the following output:

    pub = {
      "user1" = [
        "10.0.0.0/21",
        "subnet-666666662ee6f3442",
      ]
      "user2" = [
        "10.0.8.0/21",
        "subnet-6666666696b92d895",
      ]
      "user3" = [
        "10.0.16.0/21",
        "subnet-66666666cbaa4bfb3",
      ]
      "user4" = [
        "10.0.24.0/21",
        "subnet-6666666655a09d064",
      ]
    }
    

    The problem with this output is that, as I noted in the comments, the iteration over a the keys of a map happens in a lexicographical order. This means that user1 will be mapped to the first entry from pub_id_list, user2 to the second entry, etc. Even if you are suggesting in the comments that "I've checked this ordering issue and not to worry about it", please double check this solution before using it.

    For the private subnets:

    locals {
      cidr_subnet_id = zipmap(flatten(values(var.priv_cidr_map)), var.priv_id_list)
    
      priv_lookup_map = {for key, value in var.priv_cidr_map: key => [ for cidr in value: [cidr, local.cidr_subnet_id[cidr]]]}
    }
    

    Please note, that I'm using an intermediary local variable to make my code readable. The value for cidr_subnet_id will be:

    cidr_subnet_id = {
      "10.0.112.0/20" = "subnet-6666666689f1eff5e"
      "10.0.128.0/20" = "subnet-666666661b147a933"
      "10.0.144.0/20" = "subnet-666666661ce02c330"
      "10.0.160.0/20" = "subnet-66666666a3fe6efb9"
      "10.0.176.0/20" = "subnet-66666666faf4a62a8"
      "10.0.64.0/20" = "subnet-666666668f1442700"
      "10.0.80.0/20" = "subnet-66666666328a4b134"
      "10.0.96.0/20" = "subnet-666666662f611f9a5"
    }
    

    This is essentially a map between the CIDR and the subnet id. Apparently, this works correctly, because the keys from the priv_lookup_map are in lexicographical order when provided. I think this somewhat answers your question in the comments for "if both maps are in lexigraphical order isn't this a non-issue?"

    The output for priv_lookup_map will be:

    priv = {
      "user1" = [
        [
          "10.0.96.0/20",
          "subnet-666666662f611f9a5",
        ],
        [
          "10.0.112.0/20",
          "subnet-6666666689f1eff5e",
        ],
      ]
      "user2" = [
        [
          "10.0.160.0/20",
          "subnet-66666666a3fe6efb9",
        ],
        [
          "10.0.176.0/20",
          "subnet-66666666faf4a62a8",
        ],
      ]
      "user3" = [
        [
          "10.0.64.0/20",
          "subnet-666666668f1442700",
        ],
        [
          "10.0.80.0/20",
          "subnet-66666666328a4b134",
        ],
      ]
      "user4" = [
        [
          "10.0.128.0/20",
          "subnet-666666661b147a933",
        ],
        [
          "10.0.144.0/20",
          "subnet-666666661ce02c330",
        ],
      ]
    }