Home > Back-end >  Terraform list all items from a key for each iteration of an object map
Terraform list all items from a key for each iteration of an object map

Time:08-06

I have a map of objects in Terraform for a list of networks as follows:

networks = {
  "network1" = {
    subnet_names = ["subnet-1", "subnet-2", "subnet-3"]
  }
  "network2" = {
    subnet_names = ["subnet-4", "subnet-5", "subnet-6"]
  }

I want to create 6 network security groups (1 per subnet) as follows:

resource "azurerm_network_security_group" "example" {
  for_each = toset(var.networks[*].subnet_names)
[...]
}

However when I plan with this code I get an error that var_networks doesn't have an element called subnet_names. I have tried a couple of for loops as well but I think I may need to do something with a nested for loop somewhere. what I want to achieve is a list like this: nsgs_to_create=["subnet-1", "subnet-2", "subnet-3", "subnet-4", "subnet-5", "subnet-6"]] Which I can then use to create the NSGs per subnet. Any suggestions? Thanks. Andrew.

CodePudding user response:

This should not be too complicated to achieve. You should use the built-in flatten function which will flatten all the lists and return only one list:

locals {
  networks = {
    "network1" = {
      subnet_names = ["subnet-1", "subnet-2", "subnet-3"]
    }
    "network2" = {
      subnet_names = ["subnet-4", "subnet-5", "subnet-6"]
    }
  }
  
  flattened_networks = flatten([for k, v in local.networks: values(v) ])
}

This returns (using terraform console):

> local.flattened_networks
[
  "subnet-1",
  "subnet-2",
  "subnet-3",
  "subnet-4",
  "subnet-5",
  "subnet-6",
]

You can then use toset to make it work with for_each.


[1] https://www.terraform.io/language/functions/flatten

CodePudding user response:

If you want to solve it with a splat expression:

resource "azurerm_network_security_group" "example" {
  for_each = toset(flatten(values(local.networks)[*].subnet_names))
[...]
}

Explanation:

  • You need the values function to create an array, which look like this:
subnets = [
  {
    "subnet_names" = [
      "subnet-1",
      "subnet-2",
      "subnet-3",
    ]
  },
  {
    "subnet_names" = [
      "subnet-4",
      "subnet-5",
      "subnet-6",
    ]
  },
]
  • You can use the splat expression to get only the subnet names, but this will result in an array of arrays. You have to use flatten for to build a simple array.

Additionally, this solution works if you will have other attributes for a subnet, for example:

locals {
  networks = {
    "network1" = {
      subnet_names = ["subnet-1", "subnet-2", "subnet-3"]
      cidr = "10.0.0.0/24"
      # ...
    }
    "network2" = {
      subnet_names = ["subnet-4", "subnet-5", "subnet-6"]
      cidr = "10.0.1.0/24"
      # ...
    }
  }
}

Output:

output "subnets" {
  value = toset(flatten(values(local.networks)[*].subnet_names))
}
subnets = toset([
  "subnet-1",
  "subnet-2",
  "subnet-3",
  "subnet-4",
  "subnet-5",
  "subnet-6",
])
  • Related