Open sebader opened 3 years ago
Looking further into the generated ARM template, adding the API version here actually fixes the validation error - but then I get a deployment error...
From:
"value": "[format('{0}{1}', reference(resourceId('Microsoft.Storage/storageAccounts', take(format('stg{0}{1}', parameters('location'), replace(guid(resourceGroup().name, parameters('location'), 'stg'), '-', '')), 22))).primaryEndpoints.table, variables('tableName'))]"
to
"value": "[format('{0}{1}', reference(resourceId('Microsoft.Storage/storageAccounts', take(format('stg{0}{1}', parameters('location'), replace(guid(resourceGroup().name, parameters('location'), 'stg'), '-', '')), 22)), '2019-06-01').primaryEndpoints.table, variables('tableName'))]"
Deployment failed. Correlation ID: 3524fadb-a2c9-4eaf-a468-ba33a707c056. {
"status": "Failed",
"error": {
"code": "ResourceDeploymentFailure",
"message": "The resource operation completed with terminal provisioning state 'Failed'.",
"details": [
{
"code": "DeploymentFailed",
"message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.",
"details": [
{
"code": "NotFound",
"message": "{\r\n \"error\": {\r\n \"code\": \"ParentResourceNotFound\",\r\n \"message\": \"Can not perform requested operation on nested resource. Parent resource 'stgfrancecentralac3cef' not found.\"\r\n }\r\n}"
},
{
"code": "NotFound",
"message": "{\r\n \"error\": {\r\n \"code\": \"ResourceNotFound\",\r\n \"message\": \"The Resource 'Microsoft.Storage/storageAccounts/stgfrancecentralac3cef' under resource group 'teamsloops' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix\"\r\n }\r\n}"
}
]
}
]
}
}
just tagging @anthony-c-martin and @majastrz to see if you could comment on this. thanks! :)
The first example looks like a problem with the ARM deployment engine - it is attempting to evaluate the reference()
function inside a resource body which is behind a condition and will not be deployed. I modified your repro slightly to create a shorter example:
param useTableStorage bool = false
var location = resourceGroup().location
var tableName = 'testTable'
resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = if(useTableStorage) {
name: take('stg${location}${replace(guid(resourceGroup().name, location, 'stg'), '-', '')}', 22) // build unique storage account name from 'stg', location and guid()
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_ZRS'
tier: 'Standard'
}
properties: {
supportsHttpsTrafficOnly: true
}
}
resource table 'Microsoft.Storage/storageAccounts/tableServices/tables@2019-06-01' = if(useTableStorage) {
name: '${storageAccount.name}/default/${tableName}'
tags: {
test: storageAccount.properties.primaryEndpoints.table
}
}
This generates the following JSON:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"useTableStorage": {
"type": "bool",
"defaultValue": false
}
},
"functions": [],
"variables": {
"location": "[resourceGroup().location]",
"tableName": "testTable"
},
"resources": [
{
"condition": "[parameters('useTableStorage')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[take(format('stg{0}{1}', variables('location'), replace(guid(resourceGroup().name, variables('location'), 'stg'), '-', '')), 22)]",
"location": "[variables('location')]",
"kind": "StorageV2",
"sku": {
"name": "Standard_ZRS",
"tier": "Standard"
},
"properties": {
"supportsHttpsTrafficOnly": true
}
},
{
"condition": "[parameters('useTableStorage')]",
"type": "Microsoft.Storage/storageAccounts/tableServices/tables",
"apiVersion": "2019-06-01",
"name": "[format('{0}/default/{1}', take(format('stg{0}{1}', variables('location'), replace(guid(resourceGroup().name, variables('location'), 'stg'), '-', '')), 22), variables('tableName'))]",
"tags": {
"test": "[reference(resourceId('Microsoft.Storage/storageAccounts', take(format('stg{0}{1}', variables('location'), replace(guid(resourceGroup().name, variables('location'), 'stg'), '-', '')), 22))).primaryEndpoints.table]"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', take(format('stg{0}{1}', variables('location'), replace(guid(resourceGroup().name, variables('location'), 'stg'), '-', '')), 22))]"
]
}
],
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.3.1.62928",
"templateHash": "17601285248459050236"
}
}
}
I know we're tracking some issues with the ways expressions are evaluated inside conditional resources; I'll discuss this with the team and see if it's a known issue. @bmoore-msft FYI.
Any progress on the issue? This still happens in version 0.4.1272 I'll be happy to provide example, should it be necessary
@shenglol is this a dup of #6008?
@shenglol is this a dup of #6008?
It seems like a duplicate of #6008, and it's fixed in Build #11498 (not official release)
It's related but not a duplicate. #6008 is a regression for module output references only (we removed API versions from module output references in the latest release. This one is for resource references. I think the same fix that fixes #6008 can be applied here to add API versions for conditional resource references, but it won't fix the deployment error.
I have this error in multiple customer deployments, what is the expected timeline to fix this compilation error? @alex-frankel
This seems to have made its way onto Azure DevOps build agents resulting in broken production pipelines.
Can someone provide some recent repros? Looking through this issue, it's become unclear what the root issue that this thread is describing.
The conditional evaluation bug is loosely tracked with #3990 and #2371, but is more accurately tracked internally here: https://msazure.visualstudio.com/One/_sprints/taskboard/Azure-ARM-Deployments/One/Custom/Azure-ARM/Nickel?workitem=4830897
However, that is not a new issue or regression.
@alex-frankel you can refer to this repro below
Release new version so that we don't get the " requires an API version" error
@sudivate - the failure in your case is a bit different - the apiVersion would make the template pass validation but it will [likely] fail deployment or result in something unexpected in the case where synapse001 is not deployed.
This deployment has a condition on it... https://github.com/Azure/data-product-batch/blob/f639ace2ca46e3b85e3fd381b3cdde4b5ce1b5ea/infra/main.json#L391
This reference to the deployment does not: https://github.com/Azure/data-product-batch/blob/f639ace2ca46e3b85e3fd381b3cdde4b5ce1b5ea/infra/main.json#L2300
So in the case when that synapse001 is not deployed, one of two things will happen - either deployment will fail (because the deployment does not exist) or a reference would be made to an old deployment, not part of this set of deployments. Not sure if that's intended.
@alex-frankel & @shenglol - I'm not sure #6009 is the right fix as I understand it... adding the apiVersion will fix the preflight error but I think it may cause a false sense of security in thinking that your code is correct when it's likely not. Generally in this scenario you should have an if() around the reference, and if that's used preflight will short circuit the "false" side of the statement.
"Deployment template validation failed: 'The template output reference to 'Microsoft.Storage/storageAccounts/remehatestdataprocsa' requires an API version.
I have a very simple module
param storageAccountType string param storageAccountName string param location string param resourceTags object param deployStorageAccount bool
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = if (deployStorageAccount) { name: storageAccountName location: location sku: { name: storageAccountType } kind:'StorageV2' properties: { azureFilesIdentityBasedAuthentication: { directoryServiceOptions: 'None' } encryption: { services: { file: { keyType: 'Account' enabled: true } blob: { keyType: 'Account' enabled: true } table: { enabled: true keyType: 'Account' } queue: { enabled: true keyType: 'Account' } } keySource: 'Microsoft.Storage' } networkAcls: { bypass: 'AzureServices' virtualNetworkRules: [ ] ipRules: [ ] defaultAction: 'Deny' } accessTier: 'Hot' minimumTlsVersion: 'TLS1_2' allowBlobPublicAccess: false isHnsEnabled: true supportsHttpsTrafficOnly: true allowSharedKeyAccess: true } tags: resourceTags }
resource sabackupcontainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = if (deployStorageAccount) { name: '${storageAccount.name}/default/backup' }
resource rawcontainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = if (deployStorageAccount) { name: '${storageAccount.name}/default/raw' }
resource conformedcontainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = if (deployStorageAccount) { name: '${storageAccount.name}/default/conformed' }
resource modeledcontainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = if (deployStorageAccount) { name: '${storageAccount.name}/default/modeled' }
resource privateEndPoint_1StorageAccount 'Microsoft.Storage/storageAccounts/privateEndpointConnections@2021-08-01' = if (deployStorageAccount) { name: 'privateEndPoint${storageAccount.name}.utcNow()' parent: storageAccount properties: {} }
output accountURL string = storageAccount.properties.primaryEndpoints.dfs //output storageEndpoint object = storageAccount.properties.primaryEndpoints
when it is enabled it works just fine .. problem is with false. I read this https://github.com/Azure/bicep/commit/fbe7a45ed8c90274902e63551f844a9a47ec94dd but not understand
below is the module
// Storage Account name var fullStorageAccountName = '${toLower(organisationName)}${toLower(environmentType)}${substring(accountName,0,8)}sa'
// module to deploy - storage account module sa 'azurestorageaccount.bicep' = { name: 'storageAccountDeploy' params: { storageAccountName: fullStorageAccountName storageAccountType: storageAccountType location: location resourceTags: resourceTags deployStorageAccount: deployStorageAccount } }
@bmoore-msft -- can you look at #3750 and #3990? Do either of those more completely resolve the issue?
I think #3990 would fix this - it's what we currently do for as a workaround... the thing I'm not sure about is if there are unwanted consequences to it (can't think of any off the top). Technically, one would be that "I want to reference the deployment and I know it exists even if not deployed in this template". But then the only proper fix for that scenario would be at run-time. Technically I don't think we have a way to author this in bicep if we do #3990 (where as we would do in JSON) but the scenario may be a bit contrived.
3990 should also catch #2371
@alex-frankel thanks https://github.com/Azure/bicep/issues/3750 helped!
Ok, thinking about this more - this may cover all the scenarios...
Conditional Resource – when the resource that makes the run-time call is conditional, the run-time call can be wrapped in the same condition. It doesn’t matter if this run-time call is for a deployment output or any other resource reference, this scenario can be address by #3990.
The other scenario is a “New vs. Existing” or “Optional Feature” scenario. New/Existing means I want to sometimes reference a new resource and sometimes use an existing one. Also, the resource that makes this reference is unconditional (if it were conditional the fix in #3990 supersedes this). In this case the code that follow the developer’s intent is that the reference() call must have the apiVersion – primarily for the case when the resource already exists and is not being deployed in the current template. This reference() can be unconditional because the reference() call params is the same in either case. Adding the apiVersion in this case is required, but it’s not sufficient to correctly author the scenario. Note also that this scenario is true for any run-time reference/list on any resourceType, not just deployments/modules.
The “optional feature” scenario is when the developer may or may not want to enable a feature (e.g. publicIp, boot diags, replication). In this case the resource with the optional feature (i.e. the run-time function) does not have a condition, it’s always deployed. The optional feature (or property) has the condition. In bicep we’re seeing this implemented via a module in the current template, though this does not have to be the case… it may already exist or be in a template other than the current one. In this “optional feature” case the developer would need to wrap the optional feature property value in the appropriate condition, bicep cannot assert what this is in all cases. If the module (or resource) is in the current template and has a condition, this same condition could be used by the compiler. Adding the apiVersion in this case could be correct or incorrect depending on intent. So I think we have:
Note the “optional feature” scenario may also be combined with New/Existing, for example I want a new PublicIP address an existing one or no publicIp address. In this case the developer would just combine the two techniques.
Any updates regarding this? I have problems deploying an azure function slot conditionally in Bicep.
Facing this myself... any updates?
Bicep version Bicep CLI version 0.2.472 (73d4e93e9c)
Describe the bug I have a couple of resources defined with an if-condition. This template is used as a module (not sure if that is important). If the condition solves to
false
, i.e. the resource would not be deployed, I get the following error on deployment:To Reproduce
Additional context Is this somewhat related to https://github.com/Azure/bicep/issues/1492 ? While I could use a work around for that issue, I don't see one for this new issue.
Let me know if you need more information. Also happy to jump on a call if needed.
The entire template including the ARM template is actually open: https://github.com/sebader/teams-distributor/tree/main/deployment