0
votes

I'm trying out the new for_each function on a module, which itself outputs some values that I need to pass into another resource.

module "vnets" {
    source              = "../caf-virtual-network"
    for_each            = var.vnet_list

    ARM_ENVIRONMENT     = var.ARM_ENVIRONMENT
    ARM_LOCATION        = var.ARM_LOCATION
    ARM_SUBSCRIPTION_ID = var.ARM_SUBSCRIPTION_ID
    diagnostics_map     = local.diagnostics_map
    location            = var.ARM_LOCATION
    netwatcher          = local.netwatcher
    networking_object   = each.value
    tags                = var.global_settings.tags
    virtual_network_rg  = "${module.names.standard["resource-group"]}-${each.value.vnet.resource_group_name}"
    depends_on          = [
        module.resource_groups_networking
    ]
}

I can grab the output of the module for one or more of those objects by specifying something like this output "subnets" { value = module.vnets["vnet_shared_services_object"].vnet_subnets } , which in turn looks like this:

"vnet_shared_services_object" = {
    "sn-dev-uks-asdf-app-dynamic" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-app-dynamic"
    "sn-dev-uks-asdf-artifactory" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-artifactory"
  }

Here I'm specifying the output of ONE object, but I want to dynamically specify the output of both objects in one hit.

So I want this;

  "vnet_shared_services_object" = {
    "sn-dev-uks-asdf-app-dynamic" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-app-dynamic"
    "sn-dev-uks-asdf-artifactory" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-artifactory"
  }
  "vnet_transit_object" = {
    "AzureFirewallSubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/AzureFirewallSubnet"
    "GatewaySubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/GatewaySubnet"
    "sn-dev-uks-asdf-bind-dns" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/sn-dev-uks-asdf-bind-dns"
  }

...output to look like this:

subnets = {
    "sn-dev-uks-asdf-app-dynamic" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-app-dynamic"
    "sn-dev-uks-asdf-artifactory" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-artifactory"
    "AzureFirewallSubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/AzureFirewallSubnet"
    "GatewaySubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/GatewaySubnet"
    "sn-dev-uks-asdf-bind-dns" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/sn-dev-uks-asdf-bind-dns"
}

So i know doing the following will work, but the point i'm trying to make is that i don't know how many vnet modules i'm going to produce, and thus i need to make this dynamic:

output merge{
    value = merge({
        for key, value in module.vnets["vnet_shared_services_object"].vnet_subnets:
            key => value
    },
    {
        for key, value in module.vnets["vnet_transit_object"].vnet_subnets:
            key => value
    })
}

Using the guide on Terraform to flatten (https://www.terraform.io/docs/configuration/functions/flatten.html) the output object works, but it's not how i wish for it to be:

output stuff {
    value = flatten([
        for key, value in module.vnets: [
            for subnet, id in value.vnet_subnets: {
                "${subnet}" = id
            }
        ]
    ])
}

...which equats to:

stuff = [
  {
    "sn-dev-uks-asdf-app-dynamic" = "/subscriptions/asdf/resourceGroups/rg-dev-uks-asdf-vnet-shared-services/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-app-dynamic"
  },
  {
    "sn-dev-uks-asdf-artifactory" = "/subscriptions/asdf/resourceGroups/rg-dev-uks-asdf-vnet-shared-services/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-artifactory"
  },
...and so on
]

an FYI, this does not help me :(

output {
  value = merge(
      for key, value in module.vnets:
        key => value.vnets_subnets
)
}

Any help on this would be greatly appreciated!

2

2 Answers

0
votes

I'm not sure if I correctly understand the input maps, but I tried to replicate the issue creating some mock variables.

For that I created the following variables:


variable "vnets" {

  default = { 
  "vnet_shared_services_object" =  {
      "sn-dev-uks-asdf-app-dynamic" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-app-dynamic"
      "sn-dev-uks-asdf-artifactory" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-artifactory"
    }

  }

}


variable "vnet_subnets" {

  default = { 
  "vnet_transit_object" = {
      "AzureFirewallSubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/AzureFirewallSubnet"
      "GatewaySubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/GatewaySubnet"
      "sn-dev-uks-asdf-bind-dns" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/sn-dev-uks-asdf-bind-dns"
   }
  
  }

}

Then the output was defiend as:

output stuff {
    value = {for k,v in flatten([
        for key, value in merge(var.vnets, var.vnet_subnets):
        [for subkey1, subval1 in value: {"${subkey1}" = subval1}]
        
    ]): keys(v)[0] => values(v)[0]}
}

which resulted in:

stuff = {
  "AzureFirewallSubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/AzureFirewallSubnet"
  "GatewaySubnet" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/GatewaySubnet"
  "sn-dev-uks-asdf-app-dynamic" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-app-dynamic"
  "sn-dev-uks-asdf-artifactory" = "/subscriptions/asdf/resourceGroups/asdf/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-shared-services/subnets/sn-dev-uks-asdf-artifactory"
  "sn-dev-uks-asdf-bind-dns" = "/subscriptions/asdf/resourceGroups/qwer/providers/Microsoft.Network/virtualNetworks/vnet-dev-uks-asdf-transit/subnets/sn-dev-uks-asdf-bind-dns"
}
0
votes

A colleague was able to answer this question with the following code:

locals {
    subnet_list = {
        for key, value in module.vnets:
            key => value.vnet_subnets
    }
    subnet_map = merge(values(local.subnet_list)...)
}

it is the ... operator which is the key takeaway from this. you can look it up here; https://www.terraform.io/docs/configuration/expressions.html#expanding-function-arguments

... will expand a list of items to function parameters, hence you can call merge to merge a list of map