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))
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
...