Search code examples
azureterraformhcl

Terraform - nested loop for resource creation


I'm trying to create all defined virtual_networks in all defined resource_groups.

So the variables looks like this:

inputs = {
  virtual_networks = {
    "test-net1": {
      address_space = ["10.0.0.0/24"]
    }

    "test-net2": {
      address_space = ["10.0.1.0/24"]
    }
  }
  resource_groups = {
    "group1": {
      location  = "West Europe"
    }
    "group2": {
      location  = "East Europe"
    }
  }
}

The resource block looks something like this:

resource "azurerm_virtual_network" "virtual_network" {
  for_each = var.resource_groups

  name                = <FIRST KEY OF var.virtual_networks>
  resource_group_name = each.key
  location            = each.value["location"]
  address_space       = <ADDRESS_SPACE VALUE OF FIRST NETWORK FROM var.virtual_networks>

}

I tried many different ways transforming/restructuring/merging the maps from the variables so i can iterate over them in a better way for this use-case. However i just could not find the right structure and how to build it.

I built the following local variable to help solving this. However i was not able to structure the map in a way so i can loop over it in a single resource block.

locals = {
  resource_groups = {
    for key, resource_groups in var.resource_groups : "${key}" => 
    merge(
      var.virtual_networks,
      resource_groups
    )
  }
}

Outputs:

           group1 = {
               location             = "West Europe"
               test-net1            = {
                   address_space = [
                       "10.0.0.0/24",
                    ]
                }
               test-net2 = {
                   address_space = [
                       "10.0.1.0/24",
                    ]
                }
            }
           group2  = {
               location             = "West Europe"
               test-net1            = {
                   address_space = [
                       "10.0.0.0/24",
                    ]
                }
               test-net2 = {
                   address_space = [
                       "10.0.1.0/24",
                    ]
                }
            }       

Solution

  • As far as i understand, you want to created a neseted loop over two dictionaries. You cloud structure your local like this :

    locals {
      resource_groups = distinct(flatten([
        for k1, rg in var.resource_groups : [
          for k2, vn in var.virtual_networks : {
            group = merge({name = "${k1}"}, rg )
            network = merge({name = "${k2}"}, vn )
          }
        ]
      ]))
    }
    

    The output would result in:

    Changes to Outputs:
      + test = [
          + {
              + group   = {
                  + location = "West Europe"
                  + name     = "group1"
                }
              + network = {
                  + address_space = [
                      + "10.0.0.0/24",
                    ]
                  + name          = "test-net1"
                }
            },
          + {
              + group   = {
                  + location = "West Europe"
                  + name     = "group1"
                }
              + network = {
                  + address_space = [
                      + "10.0.1.0/24",
                    ]
                  + name          = "test-net2"
                }
            },
          + {
              + group   = {
                  + location = "East Europe"
                  + name     = "group2"
                }
              + network = {
                  + address_space = [
                      + "10.0.0.0/24",
                    ]
                  + name          = "test-net1"
                }
            },
          + {
              + group   = {
                  + location = "East Europe"
                  + name     = "group2"
                }
              + network = {
                  + address_space = [
                      + "10.0.1.0/24",
                    ]
                  + name          = "test-net2"
                }
            },
        ]
    

    And your resource would look like this:

    resource "azurerm_virtual_network" "virtual_network" {
      for_each = {for obj in local.resource_groups : "${obj.group.name}_${obj.network.name}" => obj}
    
      name                = each.value.network.name
      resource_group_name = each.value.group.name
      location            = each.value.group.location
      address_space       = each.value.network.address_space
    
    }