since a Bicep update not so long ago i get errors for the variable vnetArray that returns a list of all the vnets and their respective subnets and subnetIDs. I think the solution is quite easy, but i can't seem to find a solution.

The error that i'm getting for virtualNetworkModule[i].outputs.vnetName and virtualNetworkModule[i].outputs.subnetIDArray in mainSpoke.bicep variable vnetArray: "This expression is being used in the for-body of the variable "vnetArray", which requires values that can be calculated at the start of the deployment. The property "outputs" of virtualNetworkModule cannot be calculated at the start. Properties of virtualNetworkModule which can be calculated at the start include "name".bicep(BCP182)"

mainSpoke.bicep

@description('REQUIRED: Array containing virtualNetwork parameters')
param virtualNetwork array

// This variable returns a list of all the vnets and their respective subnets and subnetIDs.
var vnetArray = [for i in range(0, length(virtualNetwork)): { name: virtualNetworkModule[i].outputs.vnetName, value: virtualNetworkModule[i].outputs.subnetIDArray }]

module virtualMachineDeploymentModule '../../Modules/Compute/virtualMachineDeployment/virtualMachineDeploy.bicep' = [for vmConfiguration in virtualMachines: {
  name: 'virtualmachinedeployment-${vmConfiguration.name}'
  dependsOn: [ availabilitySetModule ]
  params: {
    vmConfiguration: vmConfiguration
    vnets: vnetArray
    keyvaultName: keyvaultName
    availabilitySetId: (availabilitySet.availabilitySetName == '' ? '' : availabilitySetModule.outputs.id)
  }
}]

// Deploy Network resources
module virtualNetworkModule '../../Modules/Network/virtualNetwork/virtualNetwork.bicep' = [for vnet in virtualNetwork: {
  name: 'virtualnetworkdeploy-${vnet.name}'
  params: {
    virtualNetworkName: '${customerName}-${vnet.name}'
    location: vnet.location
    subnets: vnet.subnets
    networkSecurityGroupId: networkSecurityGroupModule.outputs.networkSecurityGroupId
    addressPrefixes: vnet.addressPrefixes
    diagnosticStorageAccountId: storageAccountModule[0].outputs.staccId
    logAnalyticsWorkspaceId: logAnalyticsModule[0].outputs.logAnalyticsId
  }
}]

mainSpoke.parameters.jsonc

"{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "customerName": {
            "value": ""
        },
        "virtualNetwork": {
            "value": [
                {
                    "name": "VNET1",
                    "location": "westeurope",
                    "addressPrefixes": [
                        "10.0.0.0/16"
                    ],
                    "subnets": [
                        {
                            "name": "subnet1",
                            "addressPrefix": "10.0.1.0/24",
                            "delegationsName": "",
                            "delegationsServiceName": ""
                        },
                        {
                            "name": "subnet2",
                            "addressPrefix": "10.0.2.0/24",
                            "delegationsName": "",
                            "delegationsServiceName": ""
                        }
                    ]
                },
                {
                    "name": "VNET2",
                    "location": "westeurope",
                    "addressPrefixes": [
                        "10.1.0.0/16"
                    ],
                    "subnets": [
                        {
                            "name": "subnet1",
                            "addressPrefix": "10.1.1.0/24",
                            "delegationsName": "",
                            "delegationsServiceName": ""
                        },
                        {
                            "name": "subnet2",
                            "addressPrefix": "10.1.2.0/24",
                            "delegationsName": "",
                            "delegationsServiceName": ""
                        }
                    ]
                }
            ]
        }}

virtualNetwork.bicep

@description('REQUIRED: The name of the virtual network resource.')
param virtualNetworkName string

@description('REQUIRED: A list of subnets in a Virtual Network.')
param subnets array

@description('REQUIRED: The reference to the NetworkSecurityGroup resource.')
param networkSecurityGroupId string

@description('REQUIRED: A list of address blocks reserved for this virtual network in CIDR notation.')
param addressPrefixes array

@description('Array that consists of objects with 2 values being routeTableName & routeTableId')
param routeTablesNameAndId array = []


// If routeTableNameAndId is empty, then a random object is created. This object will never be used, but it couldn't be empty because Bicep Linter gives an error
var routeTablesObject = empty(routeTablesNameAndId) ? { q: 'randomValueBecauseItNeedsToBeNotEmpty' } : toObject(routeTablesNameAndId, i => i.name, i => i.id)

resource VNET 'Microsoft.Network/virtualNetworks@2021-02-01' = {
  name: toUpper(virtualNetworkName)
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: addressPrefixes

    }
    subnets: [for subnet in subnets: {
      name: subnet.name
      properties: {
        addressPrefix: subnet.addressPrefix
        routeTable: empty(routeTablesNameAndId) ? null : (subnet.routeTableName == '' ? null : {
          id: routeTablesObject[subnet.routeTableName]
        })
        networkSecurityGroup: networkSecurityGroupId == '' ? null : {
          id: networkSecurityGroupId
        }
        delegations: subnet.delegationsName != '' ? [
          {
            name: subnet.delegationsName
            properties: {
              serviceName: subnet.delegationsServiceName
            }
          }
        ] : null
        serviceEndpoints: [
          { service: 'Microsoft.KeyVault' }
        ]
      }
    }]
  }

}


output vnetId string = VNET.id
output vnetName string = VNET.name
output subnetIDArray array = [for (subnet, i) in subnets: {
  name: VNET.properties.subnets[i].name
  resourceId: VNET.properties.subnets[i].id
}]

I can confirm that the 2 virtual networks are created successfully and have the corresponding subnets, i just can't seem to map variable that returns a list of all the vnets and their respective subnets and subnetIDs. Var vnetArray will be used in virtualMachineDeploymentModule for the following:

// This variable is used to create an object of the vnets and subnets lists.
var vnetObject = toObject(vnets, i => i.name, i => toObject(i.value, j => j.name, j => j.resourceId))
1

There are 1 best solutions below

0
On BEST ANSWER

A bit ugly but you could wrap the vnet creation into its own module:

// file virtualNetworkWrapper.bicep

param customerName string
param virtualNetwork array

// Deploy Network resources
module virtualNetworkModule 'virtualNetwork.bicep' = [for vnet in virtualNetwork: {
  name: 'virtualnetworkdeploy-${vnet.name}'
  params: {
    virtualNetworkName: '${customerName}-${vnet.name}'
    location: vnet.location
    subnets: vnet.subnets
    networkSecurityGroupId: networkSecurityGroupModule.outputs.networkSecurityGroupId
    addressPrefixes: vnet.addressPrefixes
  }
}]

output vnetArray array = [for i in range(0, length(virtualNetwork)): { name: virtualNetworkModule[i].outputs.vnetName, value: virtualNetworkModule[i].outputs.subnetIDArray }]

Then from your main that would look like that:

// file mainSpoke.bicep

param customerName string
param virtualNetwork array

module virtualNetworkModule '../../Modules/Network/virtualNetwork/virtualNetworkWrapper.bicep' = {
  name: 'allvirtualnetworkdeploy-${customerName}'
  params: {
    customerName: customerName
    virtualNetwork: virtualNetwork
  }
}

var vnetArray = virtualNetworkModule.outputs.vnetArray
...