-
Notifications
You must be signed in to change notification settings - Fork 753
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
User Define types - Deployment template validation failed: 'The resource '' at line '' and column '' is defined multiple times in a template. #13555
Comments
The only workaround if you still want to use user defined types is to enter the policy definition ID directly instead of using existing resource but obviously that should be temporary workaround as this is a bug. |
Tagging this one for team discussion. Everything here is working as intended, although a divergence in behavior between symbolic name and non-symbolic name templates was not foreseen. @slavizh I'm guessing the example you shared has been simplified, but one workaround would be to use the resource policyAssignmentsRes 'Microsoft.Authorization/policyAssignments@2023-04-01' = [for (policyAssignment, i) in policyAssignments: {
name: policyAssignment.id
location: deployment().location
properties: {
displayName: policyAssignment.name
policyDefinitionId: tenantResourceId('Microsoft.Authorization/policyDefinitions', policyAssignment.policyDefinitionId)
metadata: {}
parameters: policyAssignment.parameters
}
}] If you do need to work with other properties of the policy definition resource, you can use an expression to get the distinct policy definition IDs (although this could cause the index of a policy assignment in resource tenantPolicyDefinitions 'Microsoft.Authorization/policyDefinitions@2023-04-01' existing = [for policyDefinitionId in union(map(policyAssignments, a => a.policyDefinitionId), []): {
name: policyDefinitionId
scope: tenant()
}] |
I prefer using concat as it feels better for me. Thankfully I do not need the other properties in this case. I am hoping that it could be fixed though as it is really that is something allowed by ARM and it should not be limit with bicep when using user define types. |
The error you're seeing is coming from ARM and reflects behavior of the deployment engine. The reason this only comes up when using user-defined types is because that is one of the triggers that will cause Bicep compilation to target "languageVersion 2.0" in the ARM JSON it outputs, and one of the primary differences between that template language version and the one that preceded it is how existing resources are handled. In a standard ARM template, param storageAccountName string
resource sa 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
name: storageAccountName
}
output storageAccountAccessTier string = sa.properties.accessTier Bicep will compile it to either: {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.25.53.49325",
"templateHash": "7769578801167663262"
}
},
"parameters": {
"storageAccountName": {
"type": "string"
}
},
"resources": [],
"outputs": {
"storageAccountAccessTier": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2023-01-01').accessTier]"
}
}
} or {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"languageVersion": "2.0",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.25.53.49325",
"templateHash": "12793356154020495492"
}
},
"parameters": {
"storageAccountName": {
"type": "string"
}
},
"resources": {
"sa": {
"existing": true,
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageAccountName')]"
}
},
"outputs": {
"storageAccountAccessTier": {
"type": "string",
"value": "[reference('sa').accessTier]"
}
}
} The main difference is that in the first example, {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"languageVersion": "2.0",
"contentVersion": "1.0.0.0",
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.25.53.49325",
"templateHash": "11667410164823205578"
}
},
"parameters": {
"storageAccountName": {
"type": "string"
}
},
"resources": {
"sa": {
"existing": true,
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageAccountName')]",
"dependsOn": [
"mod"
]
},
"mod": {
"type": "Microsoft.Resources/deployments",
"apiVersion": "2022-09-01",
"name": "mod",
"properties": {...}
}
},
"outputs": {
"storageAccountAccessTier": {
"type": "string",
"value": "[reference('sa').accessTier]"
}
}
} There's no good way for ARM to support multiple resource declarations targeting the same resource ID while allowing the order of calls to be controlled via |
ok. so it cannot be fixed due to being ARM issue? If it cannot be fixed than the warning of using resourceId() instead of existing becomes useless and we have to suppress it. Additionally every time we use existing somewhere in loop we have to think if the reference resource will repeat or not and put different code based on that. Also when we add schema to existing templates we need to figure out if we have case like that as well. Another scenario that comes to my mind if you have the same type of existing resource defined twice with different names (no loops). If the parameters input results in the same resource name provided would that result also in error? Typical case is where you have resource with password and encryption. For both you can define different existing Key Vault statement but the end user providing the parameters can use the same key vault. |
There are a few things we can do here to improve the experience. Please let me know if you would have any preference between them and the team can prioritize accordingly:
|
@jeskew should add "support deduplication of existing symbolic name resources to deployment engine" (I think what you were suggesting earlier) to that list? With the caveat that it's probably going to be tricky to implement and would likely take a long time. |
@anthony-c-martin Per the team discussion, that's not something we can do. The fundamental affordance in ARM of We could expose some mechanism to control whether an @inlineOnly()
resource r '<type>@<version>' existing = {
name: 'name'
} Where the @inlineOnly()
resource r '<type>@<version>' existing = {
name: 'name'
dependsOn: [ // <-- error-level diagnostic because a `reference()` function can't depend on anything
other
]
}
resource other '<type>@<version>' = {
dependsOn: [
r // <-- error-level diagnostic because `r` isn't part of the deployment graph
]
} |
Is this desirable though? Mostly we want users thinking declaratively, not imperatively. I'd have thought collapsing the references and then scheduling it as late as possible (based on dependencies) in the job graph would allow us to be at parity with the Alternatively, if we want the opposite behavior (independent GETs for each resource), I'd have thought we could fix this by adding entropy (e.g. the symbolic name of the resource declaration) to the set of identifiers used to deduplicate references in the job graph. |
I would push back gently on the idea that specifying dependencies on existing resources is imperative rather than declarative. The only control lever exposed by ARM is
This may be possible, although I suspect it would break whatever makes #7402 avoidable in symbolic name templates. |
Looking at the proposed methods I think they are all some workarounds with the exception of this being fixed somehow in ARM so the developer can just do not have to care and use only existing as syntax. With that said I think it is better for you to decide what workaround to implement that fits mostly into the spirit of Bicep language. This workaround must come up with some good documentation when existing should be used and when the other thing and if in doubt which one should be chosen as default. I have noticed this problem when when it was reported by end user who tried to assign the same policy definition more than once. Initially I thought it was some kind of bug in code or end user issue even. Also I am still not sure if I define two different existing resources but because the end user put the same resource would end up in error or not. |
Running into this issue with a template that has several secrets passed to it from a parent template. In this case, using Snippet in question resource runAsPasswordKeyVaults 'Microsoft.KeyVault/vaults@2023-07-01' existing = [
for runCommand in virtualMachine.?runCommands ?? []: if (!empty(runCommand.?runAsPassword)) {
name: runCommand.?runAsPassword.name ?? 'dummy'
scope: resourceGroup(
runCommand.?runAsPassword.?subscriptionId ?? subscription().subscriptionId,
runCommand.?runAsPassword.?resourceGroup ?? resourceGroup().name
)
}
]
resource secureParamKeyVaults 'Microsoft.KeyVault/vaults@2023-07-01' existing = [
for (runCommand, i) in virtualMachine.?runCommands ?? []: if (!empty(runCommand.?secureParameters)) {
name: runCommand.?secureParameters.name ?? 'dummy'
scope: resourceGroup(
runCommand.?secureParameters.?subscriptionId ?? subscription().subscriptionId,
runCommand.?secureParameters.?resourceGroup ?? resourceGroup().name
)
}
]
resource sasTokenKeyVaults 'Microsoft.KeyVault/vaults@2023-07-01' existing = [
for (runCommand, i) in virtualMachine.?runCommands ?? []: if (!empty(runCommand.source.?sasToken)) {
name: runCommand.source.?sasToken.?name ?? 'dummy'
scope: resourceGroup(
runCommand.source.?sasToken.?subscriptionId ?? subscription().subscriptionId,
runCommand.source.?sasToken.?resourceGroup ?? resourceGroup().name
)
}
]
@batchSize(1)
module runCommandsDeploy 'virtual-machine-run-command.bicep' = [
for (runCommand, i) in virtualMachine.?runCommands ?? []: {
name: 'runCommand-${uniqueString(virtualMachineRes.name)}-${i}'
params: {
location: location
tags: union(tags, runCommand.?tags ?? {})
runCommand: runCommand
virtualMachineName: virtualMachineRes.name
runAsUserPassword: !empty(runCommand.?runAsPassword)
? runAsPasswordKeyVaults[i].getSecret(runCommand.?runAsPassword.secretName)
: ''
secureParams: !empty(runCommand.?secureParameters)
? secureParamKeyVaults[i].getSecret(runCommand.?secureParameters.secretName)
: ''
sasToken: !empty(runCommand.source.?sasToken)
? sasTokenKeyVaults[i].getSecret(runCommand.source.?sasToken.?secretName)
: ''
}
}
] |
agree. The key vault part is big problem as many times you need to define the same resource to get different secrets. I think we need resolution sooner on this. |
The cases discussed so far in this issue would be addressed by #13674. This is an imperfect solution, as template authors would still need to prune duplicate |
glad that there will be fix for get secret scenario as well. |
Bicep version
Bicep CLI version 0.25.53 (c0ad57d)
Describe the bug
I have stumbled upon the following deployment error:
I am trying to do two different policy assignments for the same policy definition. Interestingly the error appears only if there is schema (user defined types) defined and runs without issue if I remove the user defined types from code. The issue is also not present even with schema when the referenced policy definitions do not repeat.
To Reproduce
Steps to reproduce the behavior:
input-types.bicep
main.bicep
Additional context
Add any other context about the problem here.
The text was updated successfully, but these errors were encountered: