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

copyIndex error "The provided copy name '' doesn't exist in the resource" when using triple FOR-loops for the same array #4453

Closed
doubleyouvdb opened this issue Sep 16, 2021 · 24 comments
Assignees
Labels
Milestone

Comments

@doubleyouvdb
Copy link

doubleyouvdb commented Sep 16, 2021

Bicep version
0.4.63

Describe the bug
When you are using in a variable with FOR-loop a copyIndex to refer to a certain child resource reference in another FOR-loop for the same array of objects which also uses an index to retrieve its parent that is also in another FOR-loop for the same set of objects, then in the autogenerated ARM template, all the translated copyIndexes for the parent in the copy variable get blanked out.

To Reproduce
Deploy AKS Cluster with the following code for agent pool profiles. For example purpose, only 'name' and the here relevant property 'vnetSubnetID' are listed. The properties of both the resource references and of the variable 'agentPoolProfiles' are all populated using the same array [for agentPool in aksCluster.agentPools].

// Get existing vnets
resource virtualNetworks 'Microsoft.Network/virtualNetworks@2021-02-01' existing = [for agentPool in aksCluster.agentPools: {
  name: union(defaultAgentPoolObject, agentPool).virtualNetwork.virtualNetworkName
  scope: resourceGroup(union(defaultAgentPoolObject, agentPool).virtualNetwork.subscriptionId, union(defaultAgentPoolObject, agentPool).virtualNetwork.resourceGroup)
}]

// Get existing subnets
resource subnets 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' existing = [for (agentPool, i) in aksCluster.agentPools: {
  name: union(defaultAgentPoolObject, agentPool).virtualNetwork.subnetName
  parent: virtualNetworks[i]
}]

var agentPoolProfiles = [for (agentPool, i) in aksCluster.agentPools: {
  name: agentPool.name
  vnetSubnetID: aksCluster.network.plugin =~ 'azure' ? subnets[i].id : null
}]

When deploying, this generates error:

An error occurred while deploying this offering. Error: 
InvalidTemplate - Long running operation failed with status
'Failed'. Additional Info:'Deployment template language
expression evaluation failed: 'The template language function
'copyIndex' has an invalid argument. The provided copy name ''
doesn't exist in the resource. Please see
https://aka.ms/arm-copy for usage details.'. Please see
https://aka.ms/arm-template-expressions for usage details.'

When examining the autogenerated ARM template, the cause is clear: in the variable, the copyIndex() where a property of the parent resource is called, is always empty. It should be copyIndex('agentPoolProfiles').

"copy": [
      {
        "name": "agentPoolProfiles",
        "count": "[length(parameters('aksCluster').agentPools)]",
        "input": {
          "name": "[parameters('aksCluster').agentPools[copyIndex('agentPoolProfiles')].name]",
          "vnetSubnetID": "[if(equals(toLower(parameters('aksCluster').network.plugin), toLower('azure')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', union(variables('defaultAgentPoolObject'), parameters('aksCluster').agentPools[copyIndex()]).virtualNetwork.subscriptionId, union(variables('defaultAgentPoolObject'), parameters('aksCluster').agentPools[copyIndex()]).virtualNetwork.resourceGroup), 'Microsoft.Network/virtualNetworks/subnets', union(variables('defaultAgentPoolObject'), parameters('aksCluster').agentPools[copyIndex()]).virtualNetwork.virtualNetworkName, union(variables('defaultAgentPoolObject'), parameters('aksCluster').agentPools[copyIndex('agentPoolProfiles')]).virtualNetwork.subnetName), null())]"
        }

Additional context
N/A

@ghost ghost added the Needs: Triage 🔍 label Sep 16, 2021
@doubleyouvdb
Copy link
Author

Btw, found the following workaround to be valid:

// Get existing subnets
resource subnets 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' existing = [for agentPool in aksCluster.agentPools: if (aksCluster.network.plugin =~ 'azure') {
  name: '${agentPool.virtualNetwork.virtualNetworkName}/${agentPool.virtualNetwork.subnetName}'
}]

@slavizh
Copy link
Contributor

slavizh commented Sep 16, 2021

+1

@brwilkinson
Copy link
Collaborator

brwilkinson commented Sep 25, 2021

here is the simple (standalone) repro for this... removing any complexity of the agentPools or AKS.

  • Looping through VNETS and then looping through Subnets while referencing the VNET (loop) for the parent.
var agentPools = [
  {
    name: 'NodePool1'
    virtualNetwork: {
      virtualNetworkName: 'vnet1'
      subnetName: 'subnet1'
    }
  }
]

resource virtualNetworks 'Microsoft.Network/virtualNetworks@2021-02-01' existing = [for agentPool in agentPools: {
  name: agentPool.virtualNetwork.virtualNetworkName
}]

resource subnets 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' existing = [for (agentPool, index) in agentPools: {
  name: agentPool.virtualNetwork.subnetName
  parent: virtualNetworks[index]
}]

var agentPoolProfiles = [for (agentPool, index) in agentPools: {
  name: agentPool.name
  vnetSubnetID: subnets[index].id
}]

output agentPoolProfiles array = agentPoolProfiles

Expected compiled:

"vnetSubnetID": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('agentPools')[copyIndex('agentPoolProfiles')].virtualNetwork.virtualNetworkName, variables('agentPools')[copyIndex('agentPoolProfiles')].virtualNetwork.subnetName)]"

Actually compiled:

"vnetSubnetID": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('agentPools')[copyIndex()].virtualNetwork.virtualNetworkName, variables('agentPools')[copyIndex('agentPoolProfiles')].virtualNetwork.subnetName)]"

@majastrz
Copy link
Member

majastrz commented Mar 31, 2022

Thanks for the self-contained repro @brwilkinson. It looks like the root cause is that we're not doing index replacement in a way that preserves the local loop name.

@stephaniezyen stephaniezyen modified the milestones: v0.6, v0.7 Apr 22, 2022
@santo2
Copy link

santo2 commented May 19, 2022

what is the status of this? I'm facing this issue as well.

@slavizh
Copy link
Contributor

slavizh commented May 19, 2022

very annoying bug that for me shouldn't be hard to fix. It quite common among many resources as in many cases you have to reference subnet IDs

@majastrz
Copy link
Member

Yes, we're not picking up the index expression from the parent property which fails an assertion in loop code generation. I'm working on the fix.

@alex-frankel alex-frankel modified the milestones: v0.10, v0.11 Sep 15, 2022
@stephaniezyen stephaniezyen moved this from In Progress to Blocked in Bicep Sep 27, 2022
@stephaniezyen stephaniezyen modified the milestones: v0.11, v0.13 Sep 30, 2022
@stephaniezyen stephaniezyen modified the milestones: v0.13, v0.14 Nov 4, 2022
@stephaniezyen stephaniezyen modified the milestones: v0.14, v0.15 Dec 6, 2022
@Kaloszer
Copy link

+1

@stephaniezyen stephaniezyen modified the milestones: v0.15, v0.16 Feb 28, 2023
@stephaniezyen stephaniezyen moved this from Blocked to Todo in Bicep Mar 16, 2023
@alex-frankel alex-frankel modified the milestones: v0.16, v0.17 Apr 5, 2023
@alex-frankel alex-frankel modified the milestones: v0.17, v0.18 May 8, 2023
@stephaniezyen stephaniezyen modified the milestones: v0.18, v0.19 May 24, 2023
@slavizh
Copy link
Contributor

slavizh commented Jun 30, 2023

@majastrz was anything done in recent versions of Bicep to fix this? When I use the latest sample for reproduction and generate the ARM template it seems that copyIndex() has parameter now so it seems fixed. I am yet to test it with actual deployment but if the ARM code is correct there should not be a problem.

@majastrz
Copy link
Member

I think @jeskew made a change that fixed loop codegen with indexed scopes. I will double check.

@slavizh
Copy link
Contributor

slavizh commented Jul 11, 2023

I have done some tests and they show that it is fixed now.

@majastrz
Copy link
Member

Thanks for confirming. I will also add your repro as a test case, so it doesn't regress in the future.

@alex-frankel
Copy link
Collaborator

Closing since it appears this is resolved.

@github-project-automation github-project-automation bot moved this from Todo to Done in Bicep Jul 11, 2023
@jupacaza
Copy link

jupacaza commented Jul 2, 2024

Hello, I am reproducing a very issue today with Bicep CLI version 0.28.1
Bicep CLI version 0.28.1 (ba1e9f8c1e)

I define variables with the objects for the private endpoints that the AVM modules require. The "subnetResourceId" property adequately takes the copyIndex('storageAccountPrivateEndpoints') value but the privateDnsZoneResourceIds only says copyIndex() so I get "The provided copy name '' doesn't exist in the resource" error when deploying.

Attaching reproduction files.
simple.bicep.txt
simple.json.txt

Bicep:

var storageAccountPrivateEndpoints = [
  for (config, i) in stampConfigs: {
    subnetResourceId: privateEndpointSubnets[i].id
    location: config.location
    service: 'blob'
    privateDnsZoneGroupName: 'privatelink.blob.${environment().suffixes.storage}'
    privateDnsZoneResourceIds: [
      storageBlobPrivateDnsZones[i].id
    ]
  }
]

Expected:

"variables": {
    "copy": [
      {
        "name": "storageAccountPrivateEndpoints",
        "count": "[length(parameters('stampConfigs'))]",
        "input": {
          "subnetResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, format('rg-{0}-{1}-{2}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].location, parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].stamp)), 'Microsoft.Network/virtualNetworks/subnets', split(format('vnet-apps-{0}-{1}-{2}/{3}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].shortLocation, parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].stamp, variables('privateEndpointsSnetName')), '/')[0], split(format('vnet-apps-{0}-{1}-{2}/{3}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].shortLocation, parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].stamp, variables('privateEndpointsSnetName')), '/')[1])]",
          "location": "[parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].location]",
          "service": "blob",
          "privateDnsZoneGroupName": "[format('privatelink.blob.{0}', environment().suffixes.storage)]",
          "privateDnsZoneResourceIds": [
            "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, format('rg-{0}-{1}-{2}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].location, parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].stamp)), 'Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]"
          ]
        }
      }
    ],
...

Actual:

"variables": {
    "copy": [
      {
        "name": "storageAccountPrivateEndpoints",
        "count": "[length(parameters('stampConfigs'))]",
        "input": {
          "subnetResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, format('rg-{0}-{1}-{2}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].location, parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].stamp)), 'Microsoft.Network/virtualNetworks/subnets', split(format('vnet-apps-{0}-{1}-{2}/{3}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].shortLocation, parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].stamp, variables('privateEndpointsSnetName')), '/')[0], split(format('vnet-apps-{0}-{1}-{2}/{3}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].shortLocation, parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].stamp, variables('privateEndpointsSnetName')), '/')[1])]",
          "location": "[parameters('stampConfigs')[copyIndex('storageAccountPrivateEndpoints')].location]",
          "service": "blob",
          "privateDnsZoneGroupName": "[format('privatelink.blob.{0}', environment().suffixes.storage)]",
          "privateDnsZoneResourceIds": [
            "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, format('rg-{0}-{1}-{2}', parameters('networkWorkloadName'), parameters('stampConfigs')[copyIndex()].location, parameters('stampConfigs')[copyIndex()].stamp)), 'Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]"
          ]
        }
      }
    ],

@jupacaza
Copy link

jupacaza commented Jul 3, 2024

Update:

it seems my issue stemps from using fixed values for the private DNS zone names. The copyIndex('...') value is not populate when I do not reference the objects of the iterated array in the name.

This does not work:

resource storageBlobPrivateDnsZones 'Microsoft.Network/privateDnsZones@2020-06-01' existing = [
  for (config, i) in stampConfigs: {
    name: 'privatelink.blob.${environment().suffixes.storage}' // NOTE THE FIXED VALUE FOR ALL ITEMS IN ARRAY
    scope: resourceGroup('rg-${networkWorkloadName}-${config.location}-${config.stamp}')
  }
]

This works:

var networkResources = [for (config, i) in stampConfigs: {
  rg: 'rg-${networkWorkloadName}-${config.location}-${config.stamp}'
  storageBlobPrivateDnsZoneName: 'privatelink.blob.${environment().suffixes.storage}'
}]

resource storageBlobPrivateDnsZones 'Microsoft.Network/privateDnsZones@2020-06-01' existing = [
  for r in networkResources: {
    name: r.storageBlobPrivateDnsZoneName // NOTE NAME REFERENCES SOMETHING IN THE ITERATED ARRAY
    scope: resourceGroup(r.rg)
  }
]

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

No branches or pull requests

9 participants