Azure / bicep

Bicep is a declarative language for describing and deploying Azure resources
MIT License
3.2k stars 742 forks source link

Improve error message when the child resource (subnet) is unqualified #14595

Open harshpatel17 opened 1 month ago

harshpatel17 commented 1 month ago

Example:

param subnetName string

resource existingSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' existing = {
  name: subnetName
}

var subnetID = existingSubnet.id

If the subnetName is not qualified (does not contain "/"), the following error is shown:

{"code": "InvalidTemplate", "message": "Deployment template validation failed: 'The template variable 'subnetID' is not valid: The language expression property array index '1' is out of bounds.. Please see https://aka.ms/arm-functions for usage details.'.", "additionalInfo": [{"type": "TemplateViolation", "info": {"lineNumber": 17, "linePosition": 157, "path": "properties.template.variables.subnetID"}}]}

This is an error because we have no way of knowing what the parent resource is (in this case the vnet). But the error should be improved so this can be self-serviceable.

The user should be guided to represent the parent resource in the bicep file when declaring the existingSubnet. As seen below:

param vnetName string
param subnetName string

resource existingVnet 'Microsoft.Network/virtualNetworks@2021-02-01' existing = {
  name: vnetName
}

resource existingSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-02-01' existing = {
  parent: existingVnet
  name: subnetName
}

var subnetID = existingSubnet.id
anthony-c-martin commented 1 month ago

This error occurs because of the codegen we do in Bicep: image

Some possible paths forward:

  1. Introduce a new overload for resourceId that permits an array input
  2. Introduce the concept of a spread operator for functions
  3. Use the new fail function to explicitly add validation
  4. ??? open to other suggestions
jeskew commented 1 month ago

There is a linter that checks if literal names have the right number of / characters in them:

image

Since the name in the example is passed in as a parameter, validation would need to be done in the backend. Using a combination of tryGet, coalesce, and fail could work, though I don't know whether coalesce is "short-circuiting" in the way that and and or are. This would look something like:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.28.1.47646",
      "templateHash": "121690963890637"
    }
  },
  "parameters": {
    "subnetName": {
      "type": "string"
    }
  },
  "variables": {
    "subnetID": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(parameters('subnetName'), '/')[0], coalesce(tryGet(split(parameters('subnetName'), '/'), 1), fail('Error message')))]"
  },
  "resources": []
}

A drawback of this approach would be that the error message would not be localized since it would be supplied by Bicep.

As an alternative, the codegen could use tryGet, and the backend implementation of resourceId could raise a more descriptive message if any segments of the ID are null or empty.

alex-frankel commented 1 month ago

What about a linter rule that encourages either the parent property or the nested child resource syntax?