Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve access to nested properties of a resource defined in the template #3166

Open
sebader opened this issue Jun 10, 2021 · 7 comments
Open

Comments

@sebader
Copy link
Member

sebader commented Jun 10, 2021

Is your feature request related to a problem? Please describe.
Let me start by saying that I believe the problem - and its workaround - is a limitation of ARM engine but nevertheless Bicep should (and I think would already) be able to help the user here.

What I'm trying to do is basically to get to a nested property of a resource which I define in my bicep template:

resource privateEndpoint 'Microsoft.Network/privateEndpoints@2020-11-01' =  {
  name: 'myPrivateEndpoint'
  location: location
  properties: {
    subnet: {
      id: '${vnet.id}/subnets/mysubnet'
    }
    privateLinkServiceConnections: [
      {
        name: 'privateconnection'
        properties: {
          privateLinkServiceId: 'someResourceId
        }
      }
    ]
  }
}

// THIS IS WHAT I NEED TO GET TO IN THE END:
output ip string = privateEndpoint.properties.networkInterfaces[0].properties.ipConfigurations[0].properties.privateIPAddress

However, this does not work: It compiles fine but at deployment time you'll get

The language expression property 'properties' doesn't exist, available properties are 'id'

Interestingly, though, bicep code completion helped me to create that entire statement in the first place :) So from the user experience, you actually expect this to work: Bicep Intellisense helps you write that code and compiles it, why should it not work? ;)

Next thing I tried was

resource nic 'Microsoft.Network/networkInterfaces@2020-11-01' existing = {
 name: last(split(privateEndpoint.properties.networkInterfaces[0].id, '/'))
}

This, however doesn't even let you build:

 The property "name" must be evaluable at the start of the deployment, and cannot depend on any values that have not yet been calculated. Accessible properties of pgbouncerPrivateEndpoint are "apiVersion", "id", "name", "type".bicep(BCP120)

image

So the way I found to work around this is using a module (i.e. a nested template). As you can see below, the module really doesn't do much so it would be great if we could somehow offer a more direct way through bicep:

module nicModule 'privateEndpoint-ip.bicep' =  {
  name: 'nic-reference'
  params: {
    networkInterfaceResourceName: last(split(privateEndpoint.properties.networkInterfaces[0].id, '/'))
  }
}

output ip string = nicModule.outputs.ipAddress

The module code is:

param networkInterfaceResourceName string

resource nic 'Microsoft.Network/networkInterfaces@2020-11-01' existing = {
 name: networkInterfaceResourceName 
}

output ipAddress string = nic.properties.ipConfigurations[0].properties.privateIPAddress

Describe the solution you'd like
Since overall the workaround with the module works, it would be great if bicep would provide a way to remove the need for all this

@sebader sebader added the enhancement New feature or request label Jun 10, 2021
@ghost ghost added the Needs: Triage 🔍 label Jun 10, 2021
@alex-frankel
Copy link
Collaborator

(At least) one problem is the swagger specifies properties that will be returned on GET that, in practice, are not actually returned. One debugging step I would try next is emitting the entire nic or nic.properties as an output to understand what this resource actually looks like. I will try to debug this as well, but you may be able to get to it faster than me :)

@anthony-c-martin
Copy link
Member

This, however doesn't even let you build:

 The property "name" must be evaluable at the start of the deployment, and cannot depend on any values that have not yet been calculated. Accessible properties of pgbouncerPrivateEndpoint are "apiVersion", "id", "name", "type".bicep(BCP120)

I've raised #3169 for this.

@sebader
Copy link
Member Author

sebader commented Jun 10, 2021

Side question: is there any easier / better way to do this? (To use with "existing")

name: last(split(privateEndpoint.properties.networkInterfaces[0].id, '/'))

If I have a resource ID already, it feels weird that I need to manually pull out the name again

@sebader
Copy link
Member Author

sebader commented Jun 11, 2021

@alex-frankel
not sure if this is the same, but this is what the privateEndpoint resource looks like in resource explorer

{
  "name": "c19dashdukwfe01pgbouncerpe",
  "id": "/subscriptions/****/resourceGroups/rg-dukw-frontend-su01/providers/Microsoft.Network/privateEndpoints/c19dashdukwfe01pgbouncerpe",
  "etag": "W/\"2eda1786-48e2-47ca-a98e-3a090d43e927\"",
  "type": "Microsoft.Network/privateEndpoints",
  "location": "ukwest",
  "properties": {
    "provisioningState": "Succeeded",
    "resourceGuid": "7effd5a2-ca80-4153-a550-88016dd7e6f9",
    "privateLinkServiceConnections": [
      {
        "name": "pgbouncerprivateconnection",
        "id": "/subscriptions/*****/resourceGroups/rg-dukw-frontend-su01/providers/Microsoft.Network/privateEndpoints/c19dashdukwfe01pgbouncerpe/privateLinkServiceConnections/pgbouncerprivateconnection",
        "etag": "W/\"2eda1786-48e2-47ca-a98e-3a090d43e927\"",
        "properties": {
          "provisioningState": "Succeeded",
          "privateLinkServiceId": "/subscriptions/****/resourceGroups/rg-duks-frontend-su01/providers/Microsoft.Network/privateLinkServices/c19dashduksfe01pgbouncerprivatelink",
          "privateLinkServiceConnectionState": {
            "status": "Approved",
            "description": "",
            "actionsRequired": "None"
          }
        },
        "type": "Microsoft.Network/privateEndpoints/privateLinkServiceConnections"
      }
    ],
    "manualPrivateLinkServiceConnections": [],
    "subnet": {
      "id": "/subscriptions/***/resourceGroups/rg-dukw-frontend-su01/providers/Microsoft.Network/virtualNetworks/c19dashdukwfe01vnet/subnets/pgbouncer-pe"
    },
    "networkInterfaces": [
      {
        "id": "/subscriptions/***/resourceGroups/rg-dukw-frontend-su01/providers/Microsoft.Network/networkInterfaces/c19dashdukwfe01pgbouncerpe.nic.f99d9150-4a5c-4b13-a70c-1647ea19ba94"
      }
    ],
    "customDnsConfigs": []
  }
}

@alex-frankel
Copy link
Collaborator

Right, so the networkInterfaces[*] object only has one property, which is id. The swagger is wrong and says it returns things (like networkInteraces[*].properties) that aren't really returned. Bicep only understands the swagger definition, so that's why these non-existent properties show up in completions and validate. The fix for bicep to understand this properly is for the swagger spec to be corrected.

If I have a resource ID already, it feels weird that I need to manually pull out the name again

We don't have a good way to turn an arbitrary resource ID into an existing resource unfortunately. This is something we are looking to make easier with #2245. Either way, I do agree that getting the existing reference is probably the best way to handle this. Using the string manipulation functions to grab the name is the only way to do it atm.

@sebader
Copy link
Member Author

sebader commented Jun 18, 2021

The fix for bicep to understand this properly is for the swagger spec to be corrected.

how does this happen? :) do you have somebody to reach out to?

@alex-frankel
Copy link
Collaborator

Networking team is aware of the issue, I'll update this if/when I hear back from them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants