bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Support evaluating resource IDs from symbolic names within the same resource

Open johndowns opened this issue 3 years ago • 36 comments

Some resources, such as load balancers, require referencing resource IDs as part of the properties of the resource definition, but those resource IDs are child resources of the resource itself. This means that Bicep detects this as a circular reference. For example, if I have a load balancer (simplified definition) like this:

resource loadBalancer 'Microsoft.Network/loadBalancers@2020-06-01' = {
  name: loadBalancerName
  location: location
  sku: {
    name: 'Standard'
  }
  properties: {
    frontendIPConfigurations: [
      {
        name: frontendIPConfigurationName
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          subnet: {
            id: subnetResourceId
          }
        }
      }
    ]
    loadBalancingRules: [
      {
        name: 'HttpRule'
        properties: {
          frontendIPConfiguration: {
            id: loadBalancer.properties.frontendIPConfigurations[0].id
        }
      }
    ]
  }
}

then this line causes a circular reference:

            id: loadBalancer.properties.frontendIPConfigurations[0].id

The workaround is to use the resourceId() function directly. However, it would be nicer if Bicep could allow this scenario using the symbolic reference instead.

johndowns avatar Mar 12 '21 23:03 johndowns

Related issues: #724, #1470 & #1787

anthony-c-martin avatar Mar 15 '21 14:03 anthony-c-martin

I've found a potential workaround - however it's not currently possible in bicep due to - I think - a bug:

resource vmsslb 'Microsoft.Network/loadBalancers@2021-05-01' = {
  name: '${vmssName}-lb'
  location: location
  properties: {
    frontendIPConfigurations: [
      {
        name: frontendIpConfig.name //ERROR: The expression is involved in a cycle ("frontendIpConfig" -> "fwlb"). bicep(BCP080)
        properties: {
          privateIPAddress: lbPrivateIp
        }
      }
    ]
  }

  resource frontendIpConfig 'frontendIPConfigurations' existing = {
    name: 'frontend'
  }
}

This in fact should be possible as frontendIpConfig is an existing resource and therefore should be (IMHO) excluded from the cycle detection.

miqm avatar Oct 29 '22 19:10 miqm

I found a working workaround but quite ugly:

resource _vmsslb 'Microsoft.Network/loadBalancers@2021-05-01' existing = {
  name: '${vmssName}-lb'

   resource frontendIpConfig 'frontendIPConfigurations' existing = {
    name: 'frontend'
  }
}

resource vmsslb 'Microsoft.Network/loadBalancers@2021-05-01' = {
  name: _vmsslb.name
  location: location
  properties: {
    frontendIPConfigurations: [
      {
        name: _vmss.frontendIpConfig.name //this works
        properties: {
          privateIPAddress: lbPrivateIp
        }
      }
    ]
  }
}

miqm avatar Oct 31 '22 18:10 miqm

If we proceed with excluding the existing resource dependency, we should also ensure that it works correctly with symbolic name codegen because we emit existing resources to JSON in that case.

majastrz avatar Oct 31 '22 21:10 majastrz

Assigning this to myself, however if someone is willing to pick this up - please write down a comment

miqm avatar Oct 31 '22 21:10 miqm

this is mental

4c74356b41 avatar Nov 08 '22 09:11 4c74356b41

this is mental

@4c74356b41 can you elaborate?

miqm avatar Nov 08 '22 21:11 miqm

this issue is open for over a year, whereas this problem exists since the inception of bicep and its not yet fixed*.

4c74356b41 avatar Nov 09 '22 04:11 4c74356b41

@4c74356b41 one of the reasons this hasn't really been prioritized is that we haven't seen any great need for this feature outside of the Microsoft.Network/loadBalancers resource; it is hard to justify building a new feature to address quirks in a single resource API.

Do you have any example use-cases outside of the loadbalancer resource? If you're able to share, that would help us triage & prioritize this.

anthony-c-martin avatar Nov 09 '22 14:11 anthony-c-martin

appgw - the only one that pops up right now

4c74356b41 avatar Nov 09 '22 14:11 4c74356b41

Working with appgw is a nightmare already in arm/bicep. This would help. Fixing the model to not work this way might help more though...

PolyphonyRequiem avatar Nov 11 '22 00:11 PolyphonyRequiem

honestly i dont understand why do they need to refer to each other by id? its not like you can reference a listener from another appgw, right?

4c74356b41 avatar Nov 11 '22 04:11 4c74356b41

I came here because of app gateway but I have circular id calls all over the place converting our existing infrastructure to code. This is indeed mental.

sc-atompower avatar Apr 05 '23 20:04 sc-atompower

Also stuck on app gateway config.. Mine is particularly tricky as I cannot use resourceId due to cycle detection issue because I'm creating the resource via the use of Azure/ResourceModules app gateway module.

So I'm not even sure if there's a work around for this...

Ro3A avatar Apr 20 '23 15:04 Ro3A

@Ro3A

I have some examples here:

  • https://github.com/Azure/bicep/issues/10268

brwilkinson avatar Apr 20 '23 22:04 brwilkinson

@brwilkinson Thanks for the reply.

If I'm understanding correctly, in that example, what you're demonstrating is the creation of the resource id ahead of time. If so, that's where I ended up yesterday as well.

That said, even if this was resolved from a resource perspective, I suspect further work would need to be done to support this same objective when using a module, such as the app gateway module in the CARML project. The App Gateway module in the Azure/ResourceModules (CARML) project provides inputs for many of the nested resources required for app gateway. Backend pools, ssl, userAssignIdentity, etc. The networking aspects, especially, require the resourceId of the parent app gateway.

However, using resourceId() at all, with a module, is a code smell due to cycle detection.

Ro3A avatar Apr 21 '23 13:04 Ro3A

Since https://github.com/Azure/bicep/issues/10268 was a duplicate, I will just bring that over here

Consider allowing a resource to reference itself for the values that are required at build time e.g. Name, Id, api, scope

Many network resources use the fully qualified resourceId for properties that reference itself.

  • Since there are some properties that are required at build, consider allowing self-reference for these properties.

What we do now (workaround)

param AppGWName string = 'mywaf123'

// Note I put this is as a variable - example 1
var AppGWID = resourceId('Microsoft.Network/applicationGateways', AppGWName )

//  it's more common for people to use resourceid()  - example 2
// in many places throughout the template, which does not represent a clean 'Bicep worthy' solution

resource applicationGateway 'Microsoft.Network/applicationGateways@2022-09-01' = {
  name: AppGWName 
  location: resourceGroup().location
  properties: {
    frontendIPConfigurations: [
      {
        name: 'public' // we are cross referencing this property ____________________________________________\/
        properties: {
          publicIPAddress: { 
            id: 'id'
          }
        }
      }
    ]
    httpListeners: [
      {
        name: 'name'
        properties: {
          frontendIPConfiguration: {
            // id requires the resourceid use AppGWID as substitute
            id: '${AppGWID}/frontendIPConfigurations/public' // cross reference to frontendIPConfiguration ___/\
          }
          frontendPort: {
            // a second example of cross reference, which is the more common pattern
            id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', AppGWName, 'fe-80')
          }
          protocol: 'Http'
          sslCertificate: null
        }
      }
    ]

The proposal - unblock the following self-reference, only allow access to: name, id, apiversion?

This expression is referencing its own declaration, which is not allowed. [{ "resource": "Untitled-2", "owner": "generated_diagnostic_collection_name#0", "code": "BCP079", "severity": 8, "message": "This expression is referencing its own declaration, which is not allowed.", "source": "bicep", "startLineNumber": 20, "startColumn": 21, "endLineNumber": 20, "endColumn": 39 }]

param AppGWName string = 'mywaf123'

resource applicationGateway 'Microsoft.Network/applicationGateways@2022-09-01' = {
  name: AppGWName 
  location: resourceGroup().location
  properties: {
    frontendIPConfigurations: [
      {
        name: 'public' // we are cross referencing this property
        properties: {
          publicIPAddress: {
            id: 'id'
          }
        }
      }
    ]
    httpListeners: [
      {
        name: 'name'
        properties: {
          frontendIPConfiguration: {
            id: '${applicationGateway.id}/frontendIPConfigurations/public' // use-self for id - example 1
          }
          frontendPort: {
            id: '${applicationGateway.id}/frontendPorts/fe-80' // use-self for id - example 2
          }
          protocol: 'Http'
          sslCertificate: null
        }
      }
    ]

There is a snippet for the Application Gateway to try this yourself res-app-gateway

  • There are other resources similar to this e.g. Virtual Network Gateway.

brwilkinson avatar Apr 21 '23 19:04 brwilkinson

Any additional movement on this story? The workarounds made by @brwilkinson and @miqm still the leading solutions? Anyone else find a creative way to resolve this while waiting for a feature change in bicep?

ckittel avatar May 26 '23 15:05 ckittel

@ckittel no update at this time.

Team has reviewed and did not schedule this work, so far.

So still sitting in the needs up vote stage... So the more comments and feedback will be useful to prioritize this for the future.

brwilkinson avatar May 26 '23 16:05 brwilkinson

I tried to follow the example here:

https://learn.microsoft.com/en-us/azure/application-gateway/quick-create-bicep?tabs=CLI

    requestRoutingRules: [
      {
        name: 'myRoutingRule'
        properties: {
          ruleType: 'Basic'
          httpListener: {
            id: resourceId('Microsoft.Network/applicationGateways/httpListeners', applicationGateWayName, 'myListener')
          }
          backendAddressPool: {
            id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', applicationGateWayName, 'myBackendPool')
          }
          backendHttpSettings: {
            id: resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', applicationGateWayName, 'myHTTPSetting')
          }
        }

And came across the same issue for the self referential bit, thanks for the workaround and a +1 on the suggested fix to allow self-referencing

acarter24 avatar Jun 15 '23 12:06 acarter24

Tangent: Maybe we can get the AppGW team to add a new property called name to each of those in a future API version, and we only need to provide myListener. Or id is built to support either the full ID or just the name part. :) It's not like we need to be referencing the listeners from a different app gateway. :)

ckittel avatar Jun 23 '23 13:06 ckittel

+1. Not sure why this isn't prioritized. A new property called name or nameOrId should suffice.

akhilthomas011 avatar Jun 30 '23 06:06 akhilthomas011

I am using the work around for the frontendIPConfiguration on my app gateway httpListeners however I am getting the error messaage:

Resource /subscriptions/a2bee89e-23fc-4c75-9a78-79d0aed1c958/resourceGroups/pxise-dmx-dbh-trial-rg/providers/Microsoft.Network/applicationGateways/pxise-dmx-dbh-trial-appgw/frontendIPConfigurations/appGwPublicFrontendIp referenced by resource /subscriptions/a2bee89e-23fc-4c75-9a78-79d0aed1c958/resourceGroups/pxise-dmx-dbh-trial-rg/providers/Microsoft.Network/applicationGateways/pxise-dmx-dbh-trial-appgw/httpListeners/pxise-dmx-dbh-trial-appgw-lstner-20305 was not found. Please make sure that the referenced resource exists, and that both resources are in the same region. (Code: InvalidResourceReference)

Is there another workaround that I can try? Guess I could just do a separate module to run after the app gateway gets created to address these child references.

RVAHaver01 avatar Jul 12 '23 20:07 RVAHaver01

I'm again back to setting up a nat gateway + nat gateway ip + vnet w/ subnets, and this would be useful functionality.

tasdflkjweio avatar Oct 11 '23 19:10 tasdflkjweio

I don't understand why this is in a state of needs: upvote. Without this functionality, we have to use half-functional workarounds to even do things such as deploy app gateway via bicep. I'd think core functionality would be scheduled regarless of number of users.

thefaftek-git avatar Oct 16 '23 18:10 thefaftek-git

I disagree that this is core functionality. The feature ask here would only be to accommodate a strange API design that only AppGW (and maybe other networking resources?) implemented. That's why we need a lot of customer demand to justify implementing a fix for a specific case like this.

Ideally, the AppGW team implements somethign like what @ckittel is describing here: https://github.com/Azure/bicep/issues/1852#issuecomment-1604307397

alex-frankel avatar Oct 18 '23 15:10 alex-frankel

I disagree that this is core functionality. The feature ask here would only be to accommodate a strange API design that only AppGW (and maybe other networking resources?) implemented. That's why we need a lot of customer demand to justify implementing a fix for a specific case like this.

Ideally, the AppGW team implements somethign like what @ckittel is describing here: #1852 (comment)

I challenge anyone to implement the well-architected framework via IaC and not run into this issue. It's not just AppGW and a few networking components few people use. It's almost all networking related components which are foundational to well architected framework.

Ro3A avatar Oct 18 '23 15:10 Ro3A

@ro3a - is there an inventory of the different networking resources that have this API design? I probably don't have a full understanding of the scope of the problem, but my point still stands if it is only the Networking RP that has APIs designed in this way.

It is not to say that there is not justification for implementing the fix on our side, but from a layering perspective it is not the right place to fix this problem.

alex-frankel avatar Oct 18 '23 16:10 alex-frankel

@alex-frankel I don't have a full inventory, but as we've begun the process to script our entire environment and adhere to Well architected framework, so far every networking related component has caused pain.

Top of mind: Virtual Networks, Subnets, API Management, Traffic Manager, App Gateway, regular Gateways.. I think it's generally anything that requires child resources, but child resources cannot be deployed independently. And it affects resources that depend on these resources as well, so again, scope is indeed fairly large.

Ideally, the fix is in those resources themselves. But I don't see that type of work getting done faster than the Bicep team might be able to implement a function to assist with this.

Ro3A avatar Oct 18 '23 16:10 Ro3A

I disagree that this is core functionality. The feature ask here would only be to accommodate a strange API design that only AppGW (and maybe other networking resources?) implemented. That's why we need a lot of customer demand to justify implementing a fix for a specific case like this.

Ideally, the AppGW team implements somethign like what @ckittel is describing here: #1852 (comment)

I'm willing to agree the API/impelmentation is strange, though I'd argue from a customer perspective we see functionality as usability in the end-result, not who owns layers in the middle.

Is there a way to flag/bring this to the NRP team? I'm more than happy to have them fix the backend instead, though that would only work for API versions going forward and not fix the issue that already exists.

Our company has a call with PMs for Bicep and a few other teams, I'll bring this up there as well, but any guidance on how to push for this would be really useful.

thefaftek-git avatar Oct 18 '23 23:10 thefaftek-git