bicep
bicep copied to clipboard
Investigate supporting nested resource loops
It's a pretty important one to do anything with loops (if you have a loop for azure webapp and some related app insights accounts).
What is the interim solution for nested resources like eventhubs/customgroups to be able to declare an list of child resources of an already-provisioned list of resources? Example below does not work:
var hubs = {
'foo': {
'name': 'foo'
'consumerGroups': {
'name': 'bar'
}
}
}
resource eventhub 'Microsoft.EventHub/namespaces@2017-04-01' = {
name: ''
location: ''
resource hub 'eventhubs' = [for hub in items(hubs): {
name: hub.value.name
}]
resource cg 'eventhubs/consumergroups' = [for (hub, i) in items(hubs): {
name: hub.value.consumerGroups.name
parent: hub[i]
}]
}
I currently deploy nested resources in a Module, up to 4 layers of Modules.
- an array of x top level resource
- a single x top level resource
- first child resource
- second child resource
samples:
Service Bus https://github.com/brwilkinson/AzureDeploymentFramework/blob/a1a8678f1a54dd08bce782407a49165bb4d013e5/ADF/tenants/AOA/ACU1.T5.parameters.json#L527


- https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/SB.bicep
- https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/SB-ServiceBus.bicep
- https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/SB-ServiceBus-TopicSubscription.bicep
This is the model that I use for all resources, that you can view here: https://github.com/brwilkinson/AzureDeploymentFramework/tree/main/ADF/bicep
e.g. FrontDoor

Cosmos

Even things like Multiple VM's in a loop/array, with each VM having 1 or more NICS.

actually looking at your sample I thought it was an array, however it's only an object, so perhaps above wasn't relevant for your sample?
var hubs = {
'foo': {
'name': 'foo'
'consumerGroups': {
'name': 'bar'
}
}
}
We use the same approach leveraged by @brwilkinson in our CARML modules for e.g. RBAC
(...)
"queueServices": {
"value": {
"queues": [
{
"name": "queue1",
"metadata": {},
"roleAssignments": [
{
"roleDefinitionIdOrName": "Reader",
"principalIds": [
"<<deploymentSpId>>"
]
}
]
},
{
"name": "queue2",
"metadata": {}
}
]
}
}
Where
- the storage account is a module that deploys
- a queue services module that deploy n-number of
- a queue module that iterates over an array of role assignments and deploys n-number of
- a role assignment module that iterats over n-number of principalIds
unfortunally this workaround only works when your creating new resources or child resources. Some advanced templates have nested arrays which wont work unfortunally.
e.g. https://docs.microsoft.com/en-us/azure/templates/microsoft.network/applicationgateways?tabs=bicep
backendAddressPools: [
{
id: 'string'
name: 'string'
properties: {
backendAddresses: [
{
fqdn: 'string'
ipAddress: 'string'
}
]
}
}
]
Where an Application gateway can have one or more backendpools with one or more backendaddresses
@ramondegoede This does actually work, however you have to process the nested array in it's own Module.
The module doesn't create any resources it simply iterates over some object and returns the objects that you need for the nested property.
Here is a sample to look at with FrontDoor.
https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/tenants/AOA/ACU1.T5.parameters.json#L982
"services": [
{
"Name": "APIM03", // acu1-BRW-AOA-s1-afd 01 - apim01 .psthing.com
"BEAddress": [
{
"address": "{Deployment}-apim03.azure-api.net",
"hostheader": "{Deployment}-afd03-apim03.{Domain}"
}
],
- main template loops over multiple frontdoors https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/FD.bicep
- child template does single frontdoor https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/FD-frontDoor.bicep
- nested module for processing the backend object https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/FD-frontDoor-BE.bicep
- The main front door template referencing the backend via outputs https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/FD-frontDoor.bicep#L151
backendPools: [for (service, index) in frontDoorInfo.services: {
name: service.Name
properties: {
backends: FDServiceBE[index].outputs.backends
loadBalancingSettings: {
id: resourceId('Microsoft.Network/frontdoors/loadBalancingSettings', FDName, service.LBSettings)
}
healthProbeSettings: {
id: resourceId('Microsoft.Network/frontdoors/healthProbeSettings', FDName, service.ProbeName)
}
}
}]
From: @stan-sz
related to #4555 . Gave example what should be allowed with double looping and probably even triple looping should be allowed for some rare RPs.
@brwilkinson the example is from me - @slavizh
Thanks @slavizh
- I copied and pasted directly from a similar thread not sure how that happened 😞
Is there any update?
Is there any update? Struggling to deploy a dynamic AGW.
No update on this that I am aware of. It's possible the introduction of lambda functions has exposed more workarounds, but not sure about that. @anthony-c-martin would be most likely to come up with something creative here :)
@alex-frankel you could nest multiple lambda functions. You only need to document it. Main problem is that lambda do not have index option and you cannot use them on existing resource. That forces you to use some other workarounds.
Nested loops are still needed. Also struggeling with Application Gateway URL paths.
For those of you that have this issue, I may have a workaround... I am using the map function instead of for loop, it will in the end give the same result.
I had the same problem as @janinternet, but I solved it by having it like this when I'm setting urlPathMaps:
urlPathMaps: [for urlPathMap in urlPathMaps: {
name: urlPathMap.name
properties: {
// Here is the map magic
pathRules: map(array(urlPathMap.pathRules), pathRule => {
name: pathRule.name
properties: {
paths: pathRule.paths
// other properties
}
})
// other properties
}
}]
I hope this helps some of you!
map is exactly what I use, too. I just used that approach yesterday for the Azure Container Apps Landing Zone Accelerator as a nested if was not possible. Note that map returns an array, which was desirable for me but may not always be what you want.
https://github.com/Azure/aca-landing-zone-accelerator/blob/852fbce2fd9734fc2f1bcf7589d8fcdbf94cb590/scenarios/aca-internal/bicep/modules/02-spoke/deploy.spoke.bicep#L306C1-L333C2
The map method also works for adding an array of firewall rules inside an Azure Firewall Policy rule collection. When building or updating a new Azure Firewall rule collection group, you need to pass an array of rule collections which each contain an array of rules. Nesting the for loops would be nice, but this map method appeals to the functional side of my brain :)
For reference, here is a sample usage of the "map method" that could be easily extended to N levels of nesting. In this case, we are assigning a list of given principals each of the given role definitions:
param principalIds string[]
param roleDefinitionIds string[]
var roleAssignments = flatten(
map(roleDefinitionIds, roleDefinitionId =>
map(principalIds, principalId => {
name: guid(roleDefinition, principalId)
properties: {
principalId: principalId
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinition)
}
})
)
)
resource roleAssignmentResources 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
for ra in roleAssignments: {
name: ra.name
scope: storageAccount
properties: ra.properties
}
]
Hi @alex-frankel, any news on this one? How many votes does it need? Thanks
My vote can be canceled. We migrated to Terraform.