bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Investigate supporting nested resource loops

Open majastrz opened this issue 4 years ago • 15 comments

majastrz avatar Mar 25 '21 20:03 majastrz

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

sandorfr avatar Apr 03 '21 03:04 sandorfr

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]
    }]
}

stan-sz avatar Oct 18 '21 11:10 stan-sz

I currently deploy nested resources in a Module, up to 4 layers of Modules.

  1. an array of x top level resource
  2. a single x top level resource
  3. first child resource
  4. second child resource

samples:

Service Bus https://github.com/brwilkinson/AzureDeploymentFramework/blob/a1a8678f1a54dd08bce782407a49165bb4d013e5/ADF/tenants/AOA/ACU1.T5.parameters.json#L527

image

image

  1. https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/SB.bicep
  2. https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/SB-ServiceBus.bicep
  3. 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

image

Cosmos

image

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

image

brwilkinson avatar Oct 18 '21 16:10 brwilkinson

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'
        }
    }
}

brwilkinson avatar Oct 18 '21 17:10 brwilkinson

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

AlexanderSehr avatar Dec 02 '21 18:12 AlexanderSehr

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 avatar Jan 19 '22 09:01 ramondegoede

@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}"
                  }
                ],
  1. main template loops over multiple frontdoors https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/FD.bicep
  2. child template does single frontdoor https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/FD-frontDoor.bicep
  3. nested module for processing the backend object https://github.com/brwilkinson/AzureDeploymentFramework/blob/main/ADF/bicep/FD-frontDoor-BE.bicep
  4. 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)
        }
      }
    }]

brwilkinson avatar Jan 19 '22 18:01 brwilkinson

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 avatar Jan 31 '22 05:01 brwilkinson

@brwilkinson the example is from me - @slavizh

slavizh avatar Jan 31 '22 08:01 slavizh

Thanks @slavizh

  • I copied and pasted directly from a similar thread not sure how that happened 😞

brwilkinson avatar Jan 31 '22 16:01 brwilkinson

Is there any update?

aslan-im avatar Oct 26 '23 14:10 aslan-im

Is there any update? Struggling to deploy a dynamic AGW.

srinadhbh avatar Oct 27 '23 02:10 srinadhbh

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 avatar Oct 31 '23 19:10 alex-frankel

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

slavizh avatar Nov 01 '23 07:11 slavizh

Nested loops are still needed. Also struggeling with Application Gateway URL paths.

janinternet avatar May 18 '24 19:05 janinternet

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!

dozer75 avatar Dec 06 '24 14:12 dozer75

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

simonkurtz-MSFT avatar Dec 06 '24 15:12 simonkurtz-MSFT

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

supernoodles avatar Jan 17 '25 10:01 supernoodles

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
  }
]

ohadschn avatar Jan 19 '25 15:01 ohadschn

Hi @alex-frankel, any news on this one? How many votes does it need? Thanks

guimatheus92 avatar Mar 23 '25 01:03 guimatheus92

My vote can be canceled. We migrated to Terraform.

aslan-im avatar Mar 23 '25 05:03 aslan-im