Looping through nested list object over list object

I am attempting to create a module that will loop through a nested list object over a list object, this object will criate a vNet, subnet and NSG on Azure.

In this module I want to add capacity create multiples subnetworks resoures, based a list informed by user.

variables.tf

variable "network" {
  type = list(object({

    name = string
    address_space = list(string)
    location = string
    resource_group_name = string

    subnet = optional(list(object({
      name = optional(string)
      resource_group_name = optional(string)
      virtual_network_name = optional(string)
      address_prefixes = optional(list(string))
      delegation = optional(list(object({
        name = optional(string)
        service_delegation = optional(list(object({
          name = optional(string)
          actions = optional(list(string))
        })))
      })))
    })))
  }))
  default = []
  description = "List of object to configure `Network`, `Subnet` and `NSG`."
}

I’m don’t getting criate a logic to get the objects in subnet list nested on network list object.

All alternatives I tried returned me erro.

Module main.tf:

resource "azurerm_subnet" "default" {
  for_each = { for subnet in var.network : subnet.name => subnet }
  name                 = each.value["name"]
  resource_group_name  = each.value["resource_group_name"]
  virtual_network_name = (each.value["virtual_network_name"] == null) ? azurerm_virtual_network.default[each.key].name : each.value["virtual_network_name"]
  address_prefixes     = each.value["address_prefixes"]

  dynamic "delegation" {
    for_each = (each.value["delegation"] == null) ? [] : each.value["delegation"]
    content {
      name = delegation.value["name"]
      dynamic "service_delegation" {
        for_each = delegation.value["service_delegation"]
        content {
          name = service_delegation.value["name"]
          actions  = service_delegation.value["actions"]
        }
      }
    }
  }
}

main.tf

module "network" {
  source = "/mnt/c/Users/lmoreira/OneDrive/Documents/projetos/tf-modules/Azure/terraform-network"
  
  network = [
    {
      name = "my-network"
      address_space =  ["10.0.0.0/20"]
      location = var.location
      resource_group_name = module.rg.name

      subnet = [
        {
          name = "subnet-delegate"
          resource_group_name = module.rg.name
          address_prefixes = ["10.1.0.0/24"]
          delegation = [
            {
              name = "delegation"
              service_delegation = [
                {
                  name = "Microsoft.DBforPostgreSQL/flexibleServers"
                  actions = ["Microsoft.Network/networkinterfaces/*"]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

I am attempting to create a module that will loop through a nested list object over a list object, this object will criate a vNet, subnet and NSG on Azure.

In this module I want to add capacity create multiples subnetworks resoures, based a list informed by user.

I’m don’t getting criate a logic to get the objects in subnet list nested on network list object.

All alternatives I tried returned me erro.
When I perform my code I receive the following erros:

│ Error: Invalid index
│
│   on .terraform/modules/network/main.tf line 43, in resource "azurerm_subnet" "default":
│   43:   virtual_network_name = (each.value["virtual_network_name"] == null) ? azurerm_virtual_network.default[each.key].name : each.value["virtual_network_name"]
│     ├────────────────
│     │ each.value is object with 5 attributes
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│   on .terraform/modules/network/main.tf line 46, in resource "azurerm_subnet" "default":
│   46:   address_prefixes     = each.value["address_prefixes"]
│     ├────────────────
│     │ each.value is object with 5 attributes
│
│ The given key does not identify an element in this collection value.
╵
╷
│ Error: Invalid index
│
│   on .terraform/modules/network/main.tf line 50, in resource "azurerm_subnet" "default":
│   50:     for_each = (each.value["delegation"] == null) ? [] : each.value["delegation"]
│     ├────────────────
│     │ each.value is object with 5 attributes
│
│ The given key does not identify an element in this collection value.

I am approaching this the right way? I appreciate any help or tip that can help me solve this problem.

Hi everyone!

I solved the problem, I did create a local files where I get de especifi list object nested on network list object.

locals.tf

locals {
  network = { for network in var.network : network.name => network }

  subnet = flatten([
    for network in var.network : [
      for subnet in network.subnet : subnet
    ]
  ])

  nsg = flatten([
    for network in var.network : [
      for nsg in network.nsg : nsg
    ]
  ])
}

Then in the resource I configure the for_each with local output.

for_each = { for subnet in local.subnet : subnet.name => subnet }
resource "azurerm_subnet" "default" {
  for_each = { for subnet in local.subnet : subnet.name => subnet }
  name                 = each.value["name"]
  resource_group_name  = each.value["resource_group_name"]
  virtual_network_name = (each.value["virtual_network_name"] == null) ? element(values(azurerm_virtual_network.default)[*].name, length(var.network)) : each.value["virtual_network_name"]
  address_prefixes     = each.value["address_prefixes"]


  dynamic "delegation" {
    for_each = (each.value["delegation"] == null) ? [] : each.value["delegation"]
    content {
      name = delegation.value["name"]
      dynamic "service_delegation" {
        for_each = delegation.value["service_delegation"]
        content {
          name = service_delegation.value["name"]
          actions  = service_delegation.value["actions"]
        }
      }
    }
  }
}
  # module.network.azurerm_subnet.default["subnet-delegate"] will be created
  + resource "azurerm_subnet" "default" {
      + address_prefixes                               = [
          + "10.0.0.0/24",
        ]
      + enforce_private_link_endpoint_network_policies = (known after apply)
      + enforce_private_link_service_network_policies  = (known after apply)
      + id                                             = (known after apply)
      + name                                           = "subnet-delegate"
      + private_endpoint_network_policies_enabled      = (known after apply)
      + private_link_service_network_policies_enabled  = (known after apply)
      + resource_group_name                            = "rg-aks"
      + virtual_network_name                           = "my-network"

      + delegation {
          + name = "delegation"

          + service_delegation {
              + actions = [
                  + "Microsoft.Network/networkinterfaces/*",
                ]
              + name    = "Microsoft.DBforPostgreSQL/flexibleServers"
            }
        }
    }

  # module.network.azurerm_virtual_network.default["my-network"] will be created
  + resource "azurerm_virtual_network" "default" {
      + address_space       = [
          + "10.0.0.0/20",
        ]
      + dns_servers         = (known after apply)
      + guid                = (known after apply)
      + id                  = (known after apply)
      + location            = "eastus"
      + name                = "my-network"
      + resource_group_name = "rg-aks"
      + subnet              = (known after apply)
    }