bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Template validation error despite if condition and ternary operator

Open sebader opened this issue 4 years ago • 14 comments

Bicep version Bicep CLI version 0.2.390 (008a03590a)

Describe the bug I define module dns_names with an if condition. In the next module I have this line dns: customDomainName != '' ? dns_names.outputs.appSubdomainFqdn : ''

If customDomainName is an empty string, I get the below error message:

Error message:

{'additionalProperties': {}, 'code': 'InvalidTemplate', 'message': "Deployment template validation failed: 'The resource 'subscriptions/0fc3b137-40f9-4c78-a1ae-02bf8065bcd7/resourceGroups/providers/Microsoft.Resources/deployments/dns_names-20210208T123910Z' is not defined in the template. Please see https://aka.ms/arm-template for usage details.'.", 'target': None, 'details': None, 'additionalInfo': [{'additionalProperties': {}, 'type': 'TemplateViolation', 'info': {'lineNumber': 0, 'linePosition': 0, 'path': ''}}]}

I would expect bicep/ARM to realize that the module dns_names is rightfully not defined and thus this should work.

To Reproduce Steps to reproduce the behavior:

param customDomainName string {
  default: ''
  metadata: {
    description: '(Optional) Custom domain name to be used with this app.'
  }
}

module dns_names './modules/dns-names.bicep' = if (customDomainName != '') {
  name: 'dns_names-${deploymentId}'
  scope: resourceGroup(dnsZoneResourcegroupName)
  params: {
    prefix: prefix
    customDomainName: customDomainName
  }
}

// Global resources as a module

module global_resources './modules/global-resources.bicep' = {
  name: 'global_resources-${deploymentId}'
  scope: resourceGroup(rg_global.name)
  params: {
    location: rg_global.location
    prefix: prefix
    dns: customDomainName != '' ? dns_names.outputs.appSubdomainFqdn : ''
  }
}

Additional context Add any other context about the problem here.

sebader avatar Feb 08 '21 12:02 sebader

This is a known issue with the ARM Template Runtime, not Bicep in particular. We are working on a fix on the service side. We will update this issue accordingly when that is done.

alex-frankel avatar Feb 17 '21 22:02 alex-frankel

I'm not sure how practical it is to do it automatically in bicep but it is possible to get around these issues. When its just a property you can do something like

workspaceId: ((!empty(LogAnalyticsID)) ? reference(LogAnalyticsID, '2015-03-20').customerId : 'NOTUSED')

As the resource is never deployed NOTUSED is never truly evaluated and therefore passes the initial ARM inspection.

This can also be done for entire objects, when I want to conditionally deploy an VM availability set I have to use

availabilitySet: ((!empty(AvailabilitySetName)) ? json('{"id":"${resourceId('Microsoft.Compute/availabilitySets', AvailabilitySetName)}"}') : null)

while nasty it correctly creates the arm template for either the correct id object or null if the availability set hasn't previously been created

@alex-frankel I don't suppose there is any opportunity of some of this being automated as part of the compilation process is there? In theory there is enough information in the code to identify that a variable may not be created (availabilitySet) or the resource may not be deployed (workspaceId). The compiler could then insert the if statement with 'NOTUSED' automatically in the ARM template without the user ever having to worry about the workaround.

ld0614 avatar May 17 '21 15:05 ld0614

The goal is to fix the runtime so you won't need this workaround in either bicep or ARM. We have the work scheduled to fix this in the next 2-3 months. I'll be very happy to close this issue :)

alex-frankel avatar May 17 '21 15:05 alex-frankel

Thanks for the update (and predicted timeline), I will also be extremely happy when this is fixed 😄

ld0614 avatar May 17 '21 15:05 ld0614

Any updates @alex-frankel? The issue is a year old now

itpropro avatar Feb 23 '22 11:02 itpropro

No update unfortunately. I know @majastrz is very close to picking this up, but I don't believe the work has started yet.

alex-frankel avatar Feb 23 '22 21:02 alex-frankel

I still have problems with resource evaluation for simple conditional deployments:

param loadBalancerConfig object = {}

var loadBalancer = !empty(loadBalancerConfig)

module vmLoadBalancer 'modules/loadBalancer.bicep' = if (loadBalancer) {
  name: loadBalancerConfig.name
  scope: resourceGroup(loadBalancerConfig.resourceGroupName)
  params: {
    loadBalancerPort: loadBalancerConfig.port
    ...
    loadBalancerName: loadBalancerConfig.name
  }
}

The deployment of the above throws the following error, if loadBalancerConfig is not provided: Deployment template validation failed: 'The template resource '[parameters('loadBalancerConfig').name]' at line '234' and column '5' is not valid: The language expression property 'name' doesn't exist, available properties are ''.. Please see https://aka.ms/arm-template-expressions for usage details.'.

Any recommendation how to handle these problems until this issue is resolved @alex-frankel ?

itpropro avatar May 10 '22 18:05 itpropro

You would need to use a ternary expression on each property to only access the loadBalancerConfig properties if loadBalancer is true. Something like (I haven't tested this e2e):

param loadBalancerConfig object = {}

var loadBalancer = !empty(loadBalancerConfig)

module vmLoadBalancer './02-noise.bicep' = if (loadBalancer) {
  name: loadBalancer ? loadBalancerConfig.name : ''
  scope: loadBalancer ? resourceGroup(loadBalancerConfig.resourceGroupName) : resourceGroup('')
  params: {
    loadBalancerPort: loadBalancer ? loadBalancerConfig.port : ''
    loadBalancerName: loadBalancer ? loadBalancerConfig.name : ''
  }
}

alex-frankel avatar May 11 '22 22:05 alex-frankel

I already tried this, but it does unfortunately not work. The error is the following if I use an empty string name: loadBalancer ? loadBalancerConfig.name : '':

Deployment template validation failed: 'The template resource '' of type 'Microsoft.Resources/deployments' at line '234' and column '5' is not valid. The name property cannot be null or empty. Please see https://aka.ms/arm-template/#resources for usage details.'.

If I use a specific name like this name: loadBalancer ? loadBalancerConfig.name : 'loadBalancer', the error is

Deployment template validation failed: 'The resource 'subscriptions/SUB_ID/resourceGroups/providers/Microsoft.Resources/deployments/loadBalancer' is not defined in the template. Please see https://aka.ms/arm-template for usage details.'.

EDIT: I am now certain that the error relates to another piece of code that references the loadBalancer's output later on: loadBalancer ? vmLoadBalancer.outputs.backendPoolId : '' As soon as that is removed, the error The resource 'subscriptions/SUB-ID/resourceGroups/providers/Microsoft.Resources/deployments/loadBalancer' is not defined in the template is gone. So the question is, how do you handle references to conditionally deployed resources @alex-frankel ?

itpropro avatar May 12 '22 12:05 itpropro

Any news on this issue? I'm having exact the same problem

olandese avatar Jun 18 '22 14:06 olandese

I am still commenting in and out code depending on if I have a loadBalancer config in my parameters file. I hope this gets fixed soon, so I can finally take that code into production 🙏 Did you eventually have time to look into "how do you handle references to conditionally deployed resources" @alex-frankel ?

itpropro avatar Jun 18 '22 22:06 itpropro

no reaction anymore from @alex-frankel or @majastrz ?

olandese avatar Jun 20 '22 15:06 olandese

I've added a triage label so that we discuss this in our next issue triage

anthony-c-martin avatar Jun 20 '22 15:06 anthony-c-martin

The thread is quite old, so let me tag a few people @sebader @itpropro @olandese

It's a known issue that the Scope and the Deployment name or Resource Name, should have a value in pre-flight validation.

if you are using null or empty strings for your values, these do not meet the pre-reqs to complete validation.

The workaround is to use the ternary operator for these values.

param loadBalancerConfig object = {}

var loadBalancer = !empty(loadBalancerConfig)

module messengerLower 'foo3.bicep' = if (loadBalancer) {
  name: 'loadbalancer-${loadBalancer ? loadBalancerConfig.name : 'na'}'
  scope: loadBalancer ? resourceGroup(loadBalancerConfig.resourceGroupName) : resourceGroup('na')
  params: {
    message: loadBalancerConfig.name
  }
}

You could use an empty string or a placeholder value.

  name: 'loadbalancer-${loadBalancer ? loadBalancerConfig.name : ''}'
  scope: loadBalancer ? resourceGroup(loadBalancerConfig.resourceGroupName) : resourceGroup('')
  name: loadBalancer ? loadBalancerConfig.name : 'na'
  scope: loadBalancer ? resourceGroup(loadBalancerConfig.resourceGroupName) : resourceGroup()

The deployment or resource name needs to be non-null or empty string.

brwilkinson avatar Aug 09 '22 21:08 brwilkinson

Any update? Facing the same issue.

Kaloszer avatar Jan 25 '23 11:01 Kaloszer

Can you share a recent repro of this issue? Did @brwilkinson's response in August clarify anything? While this is a frustrating runtime bug, the workarounds should cover everything. If they don't, I think we are missing something.

alex-frankel avatar Jan 27 '23 21:01 alex-frankel

EDIT: All of this is as @brwilkinson described.

When we encountered this issue, we saw this message in the DevOps pipeline logs:

##[error]Deployment template validation failed: 'The template resource '/my-resource-name' for type 'Microsoft.Sql/servers/databases' at line '1' and column '7394' has incorrect segment lengths. A nested resource type must have identical number of segments as its resource name. A root resource type must have segment length one greater than its resource name. Please see https://aka.ms/arm-template/#resources for usage details.'.

For us, it's our nested SQL Database resource inside a conditional existing SQL Server resource. In our Bicep file, we had this definition to conditionally grab an existing SQL Server if a Resource Name is provided:

@description('The name of an existing SQL Server (when empty, a new Server will be created).')
param ExistingSqlServerName string = ''

resource exsitingSqlServer 'Microsoft.Sql/servers@2022-05-01-preview' existing = if (ExistingSqlServerName != '') {
  name: ExistingSqlServerName

  resource sqlDatabase 'databases' = {
    name: databaseName
    // . . . 
    // other properties
    // . . .
  }
}

In our pipeline, we're using the AzureResourceManagerTemplateDeployment@3 with the deploymentMode set to Validation. When this executes, despite the conditional to skip the existingSqlServer Resource, the engine creates a database name in the format '${ExistingSqlServerName}/${databaseName}'. This results in the invalid name above that starts with the / character.

To solve this and pass validation, we changed to a ternary expression and a fake value when the parameter is empty:

resource exsitingSqlServer 'Microsoft.Sql/servers@2022-05-01-preview' existing = if (!empty(ExistingSqlServerName)) {
  name: !empty(ExistingSqlServerName) ? ExistingSqlServerName : 'fake-name-to-validate'

  // . . .
  // child resources
  // . . .
  }
}

Since the existing Resource is conditional based on the name, it won't ever try to get a reference to a SQL Server named fake-name-to-validate, and the Bicep file passes validation and deploys the Resources properly.

In another part of the Bicep file, we have a new SQL Server definition with its own nested SQL Database and a conditional for when the parameter is empty. That has no problems validating because we always create the new name into a variable and it's never empty.

tstooke avatar Jan 27 '23 22:01 tstooke

Can you share a recent repro of this issue? Did @brwilkinson's response in August clarify anything? While this is a frustrating runtime bug, the workarounds should cover everything. If they don't, I think we are missing something.

I'm sorry Alex but I've gotten past that issue sometime during the week, possibly by rewriting part of the code. I couldn't pinpoint the exact time it was happening in my commits, I will be sure to note down future issues with examples.

Kaloszer avatar Jan 30 '23 07:01 Kaloszer