Setproduct - Create multiple Azure VNETs, and multiple Subnets assigned to each VNET

I am trying to use the terraform ‘setproduct’ function example scenario to replicate the exact scenario being illustrated

[https://www.terraform.io/docs/configuration/functions/setproduct.html](https://setproduct function documenation)

I need to create multiple Azure VNETs, and create multiple Azure Subnets for each of those VNETs.

To achieve this, I am trying to use setproduct, along with the type = map(object({ })) variable types, and locals.

When I pass the var.networks, and var.subnets inputs at the module, it gives the following errors:

**I realize I may not understand how the map(object) wants the passed in ‘string’

Code to help explain my scenario:

tf variables:

variable "networks" {

  type = map(object({

    base_cidr_block = string

  }))

}

variable "subnets" {

  type = map(object({

    bits = string

  }))

}

network.tf

locals {

  vnet_name_prefix   = "${var.project_ident}-${var.env_ident}-vnet-${var.region_suffix}"

  subnet_name_prefix = "${var.project_ident}-${var.env_ident}-subnet-${var.region_suffix}"

  networks = [

    for key, network in var.networks : {

      key        = key

      cidr_block = network.base_cidr_block

    }

  ]

  subnets = [

    for key, subnet in var.subnets : {

      key    = key

      number = subnet.bits

    }

  ]

  network_subnets = [

    for pair in setproduct(local.networks, local.subnets) : {

      network_key  = pair[0].key

      subnet_key   = pair[1].key

      network_name = azurerm_virtual_network.vnet[pair[0].key].name

      cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number)

    }

  ]

}

resource "azurerm_virtual_network" "vnet" {

  for_each = var.networks

    resource_group_name = var.resource_group_name

    location            = var.location

    address_space       = [each.value.base_cidr_block]

    name                = join("- ", [local.vnet_name_prefix, substr(each.value.base_cidr_block, 0, length(each.value.base_cidr_block)-3)])

}

resource "azurerm_subnet" "bastion-sub" {

  for_each = var.networks

    name                        = var.subnet1_name # Name must match 'AzureBastionSubnet'

    resource_group_name         = var.resource_group_name

    vitual_network_name         = join("- ", [local.vnet_name_prefix, substr(each.value.base_cidr_block, 0, length(each.value.base_cidr_block)-3)])

    address_prefixes            = [cidrsubnet(each.value.base_cidr_block, 11, 0)]

    depends_on                  = [azurerm_virtual_network.vnet]

}

resource "azurerm_subnet" "subnets" {

    for_each = {

        for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet

    }

    name                        = join("- ", [local.subnet_name_prefix, substr(each.each.value_cidr_block, 0, length(each.value)-3)])

    resource_group_name         = var.resource_group_name

    virtual_network_name        = each.value.network_name

    address_prefixes            = [each.value.cidr_block]

}

Calling the network module, and passing in the vars used through out the locals:

module "network" {

        source                        = "../../modules/network"

        project_ident                 = var.project_ident

        env_ident                     = var.env_ident

        region_suffix                 = var.region_suffix

        resource_group_name           = data.azurerm_resource_group.core-rg.name

        location                      = data.azurerm_resource_group.core-rg.location

        networks                      = ["10.1.0.0/16","10.2.0.0/16"]

        subnets                       = [1,2,3]

 }

Ive tried to turn this list into a map, but no matter how i try it, i get the following errors

networks                      = tomap(["10.1.0.0/16","10.2.0.0/16"])
subnets                        = tomap([1,2,3])

setproduct_variableInputErrors_copnverttomap

Thank you for any help on this

I am new to using these complex terraform for_each and for expressions, but this would really help make a very dynamic network module, to allow multiple vnets with custom cidrs, and multiple subnet with custom cidrs

Hi @Gvazzana,

Your first set of errors seem to be about command line syntax in the -var command line argument. Passing complex-typed values by those command line arguments is very inconvenient due to the need to handle shell escaping, so I’d recommend writing a .tfvars file instead and then using the -var-file option to pass it to Terraform. That way you can write the values using normal Terraform syntax, without any special shell escaping.

However, in your second case you are calling the module as a child of another module, so the previous observation doesn’t apply there: child module variables are always set inside the module block, as you’ve done here.

In the second case then, I think the problem is that the values you’ve set don’t conform to the type constraints you wrote. One way to write values conforming to those type constraints would be like this:

  networks = {
    a = {
      base_cidr_block = "10.1.0.0/16"
    }
    b = {
      base_cidr_block = "10.2.0.0/16"
    }
  }
  subnets = {
    a = {
      bits = 1
    }
    b = {
      bits = 2
    }
    c = {
      bits = 3
    }
  }

The a, b and a, b, c keys I used here are just arbitrary examples because I don’t know what would be meaningful keys to use in your infrastructure. Those will be the identifiers Terraform will use to track the networks and subnets; if you were using the aws_subnet configuration shown in the docs then for example you’d end up with three subnet resource instances with the following addresses:

  • aws_vpc.example["a"]
  • aws_vpc.example["b"]
  • aws_vpc.example["c"]

I suggest choosing suitable keys that are meaningful for how your system is built, so the final addresses will be meaningful to you and anyone else who will be working with this infrastructure.