Bicep linter emits BCP059 (The name "description" is not a function) for description decorator
Bicep version
run bicep --version via the Bicep CLI, az bicep version via the AZ CLI or via VS code by navigating to the extensions tab and searching for Bicep
Describe the bug
The Bicep linter is complaining about the description decorator, the documentation says the below is valid syntax
To Reproduce
Create a new file with the following contents:
targetScope = 'managementGroup'
param policyAssignmentDisplayName string
param policyAssignmentId string
param oldPolicyAssignmentId string
param policyDefinitionId string
@description('Target Management Group')
param targetManagementGroup string
@description('An array of the allowed locations.')
param allowedLocations array
param customParams object = {}
param roleDefinitionIds array = []
param timestamp string = utcNow()
param subscriptionId string = '<subs-id to host the deployment script>'
param resourceGroup string = '<resource group to host the deployment script>'
param enforcementMode string = 'Default'
param scriptContent string
param description string = 'Default Description'
var mgScope = tenantResourceId('Microsoft.Management/managementGroups', targetManagementGroup)
var policyParams = customParams
var checkOldAssignment_var = 'checkOldAssignment_${timestamp}'
var updateOldAssignment_var = 'updateOldAssignment_${timestamp}'
var regionSelector = {
name: 'selectedLocations'
selectors: [
{
kind: 'resourceLocation'
in: allowedLocations
}
]
}
var resourceSelectors = (contains(allowedLocations, 'global')
? []
: [
regionSelector
])
Additional context
The documentation for this error is not very informative, a more clear description would help:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-core-diagnostics#BCP059
If you instead paste the following contents, the problem goes away:
targetScope = 'managementGroup'
param policyAssignmentDisplayName string
param policyAssignmentId string
param oldPolicyAssignmentId string
param policyDefinitionId string
@description('Target Management Group')
param targetManagementGroup string
@description('An array of the allowed locations.')
param allowedLocations array
param customParams object = {}
var mgScope = tenantResourceId('Microsoft.Management/managementGroups', targetManagementGroup)
var policyParams = customParams
var regionSelector = {
name: 'selectedLocations'
selectors: [
{
kind: 'resourceLocation'
in: allowedLocations
}
]
}
var resourceSelectors = (contains(allowedLocations, 'global')
? []
: [
regionSelector
])
Content is generated from 'Paste JSON as Bicep' VSCode command on the following content:
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"policyAssignmentDisplayName": {
"type": "string"
},
"policyAssignmentId": {
"type": "string"
},
"oldPolicyAssignmentId": {
"type": "string"
},
"policyDefinitionId": {
"type": "string"
},
"targetManagementGroup": {
"type": "string",
"metadata": {
"description": "Target Management Group"
}
},
"allowedLocations": {
"type": "array",
"metadata": {
"description": "An array of the allowed locations."
}
},
"customParams": {
"type": "object",
"defaultValue": {}
},
"roleDefinitionIds": {
"type": "array",
"defaultValue": []
},
"timestamp": {
"type": "string",
"defaultValue": "[utcNow()]"
},
"subscriptionId": {
"type": "string",
"defaultValue": "<subs-id to host the deployment script>"
},
"resourceGroup": {
"type": "string",
"defaultValue": "<resource group to host the deployment script>"
},
"enforcementMode": {
"type": "string",
"defaultValue": "Default"
},
"scriptContent": {
"type": "string"
},
"description": {
"type": "string",
"defaultValue": "Default Description"
}
},
"variables": {
"mgScope": "[tenantResourceId('Microsoft.Management/managementGroups', parameters('targetManagementGroup'))]",
"policyParams": "[parameters('customParams')]",
"checkOldAssignment": "[concat('checkOldAssignment', '_', parameters('timestamp'))]",
"updateOldAssignment": "[concat('updateOldAssignment', '_', parameters('timestamp'))]",
"regionSelector": {
"name": "selectedLocations",
"selectors": [
{
"kind": "resourceLocation",
"in": "[parameters('allowedLocations')]"
}
]
},
"resourceSelectors": "[if(contains(parameters('allowedLocations'), 'global'), createArray(), createArray(variables('regionSelector')))]"
},
"resources": [
{
"condition": "[not(empty(parameters('oldPolicyAssignmentId')))]",
"name": "[variables('checkOldAssignment')]",
"type": "Microsoft.Resources/deployments",
"subscriptionId": "[parameters('subscriptionId')]",
"resourceGroup": "[parameters('resourceGroup')]",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "Inner"
},
"parameters": {
"oldPolicyAssignment": {
"value": "[if(empty(parameters('oldPolicyAssignmentId')), createObject(),reference(extensionResourceId(variables('mgScope'), 'Microsoft.Authorization/policyAssignments', parameters('oldPolicyAssignmentId')), '2022-06-01', 'Full'))]"
},
"scriptName": {
"value": "[concat('CheckOldAssignment', '_', parameters('timestamp'))]"
},
"newLocations": {
"value": "[parameters('allowedLocations')]"
},
"scriptContent": {
"value": "[parameters('scriptContent')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"oldPolicyAssignment": {
"type": "object"
},
"scriptName": {
"type": "string"
},
"newLocations": {
"type": "array"
},
"scriptContent": {
"type": "string"
}
},
"variables": {
"checkCondition": "[not(empty(parameters('oldPolicyAssignment')))]",
"oldResourceSelectors": "[if(contains(parameters('oldPolicyAssignment').properties,'resourceSelectors'), parameters('oldPolicyAssignment').properties.resourceSelectors, createArray())]"
},
"resources": [
{
"condition": "[variables('checkCondition')]",
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2020-10-01",
"kind": "AzurePowerShell",
"name": "[parameters('scriptName')]",
"location": "[resourceGroup().location]",
"properties": {
"azPowerShellVersion": "6.4",
"environmentVariables": [
{
"name": "oldResourceSelectors",
"value": "[string(if(empty(variables('oldResourceSelectors')), createArray(), variables('oldResourceSelectors')))]"
},
{
"name": "newLocations",
"value": "[string(parameters('newLocations'))]"
}
],
"scriptContent": "[parameters('scriptContent')]",
"cleanupPreference": "Always",
"retentionInterval": "PT1H"
}
}
],
"outputs": {
"oldAssignment": {
"value": "[parameters('oldPolicyAssignment')]",
"type": "object"
},
"updatedResourceSelectors": {
"value": "[json(reference(parameters('scriptName')).outputs.text)]",
"condition": "[variables('checkCondition')]",
"type": "array"
}
}
}
}
},
{
"condition": "[not(empty(parameters('oldPolicyAssignmentId')))]",
"name": "[variables('updateOldAssignment')]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"location": "[deployment().location]",
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "Inner"
},
"parameters": {
"oldPolicyAssignmentId": {
"value": "[parameters('oldPolicyAssignmentId')]"
},
"oldPolicyAssignment": {
"value": "[if(empty(parameters('oldPolicyAssignmentId')), createObject(), reference(variables('checkOldAssignment'), '2021-04-01').outputs.oldAssignment.value)]"
},
"updatedResourceSelectors": {
"value": "[if(empty(parameters('oldPolicyAssignmentId')), createObject(),reference(variables('checkOldAssignment'), '2021-04-01').outputs.updatedResourceSelectors.value)]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"oldPolicyAssignmentId": {
"type": "string"
},
"oldPolicyAssignment": {
"type": "object"
},
"updatedResourceSelectors": {
"type": "array"
}
},
"variables": {
"updateCondition": "[not(empty(parameters('oldPolicyAssignment')))]"
},
"resources": [
{
"condition": "[variables('updateCondition')]",
"type": "Microsoft.Authorization/policyAssignments",
"name": "[parameters('oldPolicyAssignmentId')]",
"apiVersion": "[parameters('oldPolicyAssignment').apiVersion]",
"location": "[parameters('oldPolicyAssignment').location]",
"identity": "[parameters('oldPolicyAssignment').identity]",
"properties": {
"displayName": "[parameters('oldPolicyAssignment').properties.displayName]",
"scope": "[parameters('oldPolicyAssignment').properties.scope]",
"enforcementMode": "[parameters('oldPolicyAssignment').properties.enforcementMode]",
"policyDefinitionId": "[parameters('oldPolicyAssignment').properties.policyDefinitionId]",
"parameters": "[parameters('oldPolicyAssignment').properties.parameters]",
"resourceSelectors": "[parameters('updatedResourceSelectors')]"
}
}
],
"outputs": {
"updatedOldAssignment": {
"value": "[reference(parameters('oldPolicyAssignmentId'))]",
"condition": "[variables('updateCondition')]",
"type": "object"
}
}
}
}
},
{
"type": "Microsoft.Authorization/policyAssignments",
"dependsOn": [
"[variables('updateOldAssignment')]"
],
"name": "[parameters('policyAssignmentId')]",
"apiVersion": "2022-06-01",
"location": "[deployment().location]",
"identity": {
"type": "SystemAssigned"
},
"properties": {
"displayName": "[parameters('policyAssignmentDisplayName')]",
"scope": "[variables('mgScope')]",
"enforcementMode": "[parameters('enforcementMode')]",
"description": "[parameters('description')]",
"policyDefinitionId": "[extensionResourceId(variables('mgScope'), 'Microsoft.Authorization/policyDefinitions', parameters('policyDefinitionId'))]",
"parameters": "[variables('policyParams')]",
"resourceSelectors": "[variables('resourceSelectors')]"
}
},
{
"condition": "[not(empty(parameters('roleDefinitionIds')))]",
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2021-04-01-preview",
"name": "[guid(parameters('policyAssignmentId'), parameters('targetManagementGroup'), parameters('roleDefinitionIds')[copyIndex()])]",
"dependsOn": [
"[parameters('policyAssignmentId')]"
],
"properties": {
"roleDefinitionId": "[parameters('roleDefinitionIds')[copyIndex()]]",
"principalType": "ServicePrincipal",
"principalId": "[toLower(reference(parameters('policyAssignmentId'), '2022-06-01', 'Full').identity.principalId)]"
},
"copy": {
"name": "roleAssignmentsCopy",
"count": "[length(parameters('roleDefinitionIds'))]",
"mode": "serial",
"batchSize": 1
}
}
]
}
Actually, I figured out the issue is that for param I need to use @sys.description but it wasn't very straightforward to understand.
Maybe the message can be made more informative for these previously working decorators
The error is being caused by this line in the template:
param description string = 'Default Description'
which adds a parameter that shadows the @description() decorator. It might be helpful to add a message like Did you mean 'sys.description'? to the BCP059 message (similar to how we suggest likely matches when an unrecognized property is referenced).
Yeah I think it would be helpful to add a hint like that would help, especially since the file was generated by the decompiler. Maybe adding a conditional in the decompiler to disambiguate 'description' to 'sys.description' may be useful to avoid confusion
The current error mesage looks like this:
Closing as fixed