skip to Main Content

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.

2

Answers


  1. 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

    Login or Signup to reply.
  2. 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",
    ])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search