Azure Verified Modules
GlossaryGitHubGitHub IssuesToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Last updated: 24 Jan 2024

Bicep Specific Specification

Make sure to checkout the Shared Specification first before reading further so you understand the specifications items that are shared and agnostic to the IaC language/tool.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

This page contains the Bicep specific requirements for AVM modules (Resource and Pattern modules) that ALL Bicep AVM modules MUST meet. These requirements are in addition to the Shared Specification requirements that ALL AVM modules MUST meet.

The following table summarizes the category identification codes used in this specification:

ScopeFunctional requirementsNon-functional requirements
Shared requirements (resource & pattern modules)BCPFRBCPNFR
Resource module level requirementsN/AN/A
Pattern module level requirementsN/AN/A

Shared Requirements (Resource & Pattern Modules)

Listed below are both functional and non-functional requirements for Bicep AVM modules (Resource and Pattern).

Functional Requirements (BCPFR)

This section includes Bicep specific, functional requirements (BCPFR) for AVM modules (Resource and Pattern).

ID: BCPFR1 - Category: Composition - Cross-Referencing Modules

Module owners MAY cross-references other modules to build either Resource or Pattern modules.

However, they MUST be referenced only by a public registry reference to a pinned version e.g. br/public:avm/xxx/yyy:1.2.3. They MUST NOT use local parent path references to a module e.g. ../../xxx/yyy.bicep.

Although, child modules, that are children of the primary resources being deployed by the AVM Resource Module, MAY be specified via local child path e.g. child/resource.bicep.

Modules MUST NOT contain references to non-AVM modules.

ID: BCPFR2 - Category: Composition - Role Assignments Role Definition Mapping

Module owners MAY define common RBAC Role Definition names and IDs within a variable to allow consumers to define a RBAC Role Definition by their name rather than their ID, this should be self contained within the module themselves.

However, the MUST use only the official RBAC Role Definition name within the variable and nothing else.

To meet the requirements of BCPFR2, BCPNFR5 and BCPNFR6 you MUST use the below code sample in your AVM Modules to achieve this.

@description('''Required. You can provide either the display name (note not all roles are supported, check module documentation) of the role definition, or its fully qualified ID in the following format: `/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11`.''')
param roleDefinitionIdOrName string

var builtInRbacRoleNames = {
  Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
  Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
  Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
  'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168'
  'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
  //Other RBAC Role Definitions Names & IDs can be added here as needed for your module

var roleDefinitionIdMappedResult = (contains(builtInRbacRoleNames, roleDefinitionIdOrName) ? builtInRbacRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName)

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  //Other properties removed for ease of reading
  properties: {
    roleDefinitionId: roleDefinitionIdMappedResult
    //Other properties removed for ease of reading

ID: BCPFR4 - Category: Composition - Telemetry Enablement

To meet the requirements of SFR3 & SFR4 you MUST use the below code sample in your AVM Modules to achieve this.

@description('Optional. Location for all resources.')
param location string = resourceGroup().location

@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true

resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
  name: take('46d3xbcp.res.compute-virtualmachine.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64)
  properties: {
    mode: 'Incremental'
    template: {
      '$schema': ''
      contentVersion: ''
      resources: []
      outputs: {
        telemetry: {
          type: 'String'
          value: 'For more information, see'

Non-Functional Requirements (BCPNFR)

This section includes Bicep specific, non-functional requirements (BCPNFR) for AVM modules (Resource and Pattern).

ID: BCPNFR1 - Category: Inputs - Data Types

To simplify the consumption experience for module consumers when interacting with complex data types input parameters, mainly objects and arrays, the Bicep feature of User-Defined Types MUST be used and declared.

User-Defined Types are GA in Bicep as of version v0.21.1, please ensure you have this version installed as a minimum.

User-Defined Types allow intellisense support in supported IDEs (e.g. Visual Studio Code) for complex input parameters using arrays and objects.

A early goal for AVM is to complete the evolution/migration of CARML modules into AVM modules so they are available on the Bicep Public Registry. However, retrofitting User-Defined Types for all CARML modules as they come into AVM will take a considerable amount of time.

Therefore it has been decided by the AVM core team that CARML modules initial migrations to AVM will NOT mandate and enforce the addition of using and defining User-Defined Types. However, all CARML migrated modules to AVM MUST add User-Defined Types prior to their next release of a version of the module.

ID: BCPNFR7 - Category: Inputs - Parameter Requirement Types

Modules will have lots of parameters that will differ in their requirement type (required, optional, etc.). To help consumers understand what each parameter’s requirement type is, module owners MUST add the requirement type to the beginning of each parameter’s description. Below are the requirement types with a definition and example for the description decorator:

Parameter Requirement TypeDefinitionExample Description Decorator
RequiredThe parameter value must be provided. The parameter does not have a default value and hence the module expects and requires an input.@description('Required. <PARAMETER DESCRIPTION HERE...>')
ConditionalThe parameter value can be optional or required based on a condition, mostly based on the value provided to other parameters. Should contain a sentence starting with ‘Required if (…).’ to explain the condition.@description('Conditional. <PARAMETER DESCRIPTION HERE...>')
OptionalThe parameter value is not mandatory. The module provides a default value for the parameter.@description('Optional. <PARAMETER DESCRIPTION HERE...>')
GeneratedThe parameter value is generated within the module and should not be specified as input in most cases. A common example of this is the utcNow() function that is only supported as the input for a parameter value, and not inside a variable.@description('Generated. <PARAMETER DESCRIPTION HERE...>')

ID: BCPNFR2 - Category: Documentation - Module Documentation Generation

This script/tool is currently being developed by the AVM team and will be made available very soon.

Bicep modules documentation MUST be automatically generated via the provided script/tooling from the AVM team, providing the following headings:

  • Title
  • Description
  • Navigation
  • Resource Types
  • Usage Examples
  • Parameters
  • Outputs
  • Cross-referenced modules

ID: BCPNFR3 - Category: Documentation - Usage Example formats

Usage examples for Bicep modules MUST be provided in the following formats:

  • Bicep file (orchestration module style) - .bicep
    module <resourceName> 'br/public:avm/res/<publishedModuleName>:1.0.0' = {
      name: '${uniqueString(deployment().name, location)}-test-<uniqueIdentifier>'
      params: { (...) }
  • JSON / ARM Template Parameter Files - .json
    "$schema": "",
      "contentVersion": "",
      "parameters": { (...) }
The above formats are currently automatically taken & generated from the tests/e2e tests. It is enough to run the Set-ModuleReadMe or Set-AVMModule functions (from the utilities folder) to update the usage examples in the readme(s).
Bicep Parameter Files (.bicepparam) are being reviewed and considered by the AVM team for the usability and features at this time and will likely be added in the future.

ID: BCPNFR4 - Category: Documentation - Parameter Input Examples

Bicep modules MAY provide parameter input examples for parameters using the metadata.example property via the @metadata() decorator.


  example: 'uksouth'
@description('Optional. Location for all resources.')
param location string = resourceGroup().location

  example: '''
    keyName: 'myKey'
    keyVaultResourceId: '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.KeyVault/vaults/myvault'
    keyVersion: '6d143c1a0a6a453daffec4001e357de0'
    userAssignedIdentityResourceId '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity'
@description('Optional. The customer managed key definition.')
param customerManagedKey customerManagedKeyType

It is planned that these examples are automatically added to the module readme’s parameter descriptions when running either the Set-ModuleReadMe or Set-AVMModule scripts (available in the utilities folder).

ID: BCPNFR5 - Category: Composition - Role Assignments Role Definition Mapping Limits

As per BCPFR2, module owners MAY define common RBAC Role Definition names and IDs within a variable to allow consumers to define a RBAC Role Definition by their name rather than their ID.

Module owners SHOULD NOT map every RBAC Role Definition within this variable as it can cause the module to bloat in size and cause consumption issues later when stitched together with other modules due to the 4MB ARM Template size limit.

Therefore module owners SHOULD only map the most applicable and common RBAC Role Definition names for their module and SHOULD NOT exceed 15 RBAC Role Definitions in the variable.

Remember if the RBAC Role Definition name is not included in the variable this does not mean it cannot be declared, used and assigned to an identity via an RBAC Role Assignment as part of a module, as any RBAC Role Definition can be specified via its ID without being in the variable.
Review the Bicep Contribution Guide’s ‘RBAC Role Definition Name Mapping’ section for a code sample to achieve this requirement.

ID: BCPNFR6 - Category: Composition - Role Assignments Role Definition Mapping Compulsory Roles

Module owners MUST include the following roles in the variable for RBAC Role Definition names:

  • Owner - ID: 8e3af657-a8ff-443c-a75c-2fe8c4bcb635
  • Contributor - ID: b24988ac-6180-42a0-ab88-20f7382dd24c
  • Reader - ID: acdd72a7-3385-48ef-bd42-f606fba81ae7
  • User Access Administrator - ID: 18d7d88d-d35e-4fb5-a5c3-7773c20a72d9
  • Role Based Access Control Administrator (Preview) - ID: f58310d9-a9f6-439a-9e8d-f62e7b41a168
Review the Bicep Contribution Guide’s ‘RBAC Role Definition Name Mapping’ section for a code sample to achieve this requirement.

ID: BCPNFR8 - Category: Composition - Code Styling - lower camelCasing

Module owners SHOULD use lower camelCasing for naming the following:

  • Parameters
  • Variables
  • Outputs
  • User Defined Types
  • Resources (symbolic names)
  • Modules (symbolic names)

For example: camelCasingExample (lowercase first word (entirely), with capital of first letter of all other words and rest of word in lowercase)

ID: BCPNFR14 - Category: Composition - Versioning

To meet SNFR17 and depending on the changes you make, you may need to bump the version in the version.json file.

  "$schema": "",
  "version": "0.1",
  "pathFilters": [

The version value is in the form of MAJOR.MINOR. The PATCH version will be incremented by the CI automatically when publishing the module to the Public Bicep Registry once the corresponding pull request is merged. Therefore, contributions that would only require an update of the patch version, can keep the version.json file intact.

For example, the version value should be:

  • 0.1 for new modules, so that they can be released as v0.1.0.
  • 1.0 once the module owner signs off the module is stable enough for it’s first Major release of v1.0.0.
  • 0.x for all feature updates between the first release v0.1.0 and the first Major release of v1.0.0.

ID: BCPNFR9 - Category: Testing - Expected Test Directories

Module owners MUST create the defaults, waf-aligned folders within their /tests/e2e/ directory in their module source code and SHOULD create a max folder also. Each folder will be used as described for various test cases.

  • MUST - defaults == minimum/required parameters/variables only, heavy reliance on the default values for other parameters/variables
  • MUST - waf-aligned == showing all parameters/variables for the module to be as WAF compliant as possible
  • SHOULD - <other folders for examples as required> == all the possible parameters/variables set, some will be mutually exclusive for example. If a module can deploy varying styles of the same resource, e.g. VMs can be Linux or Windows, the names above should be used as suffixes in the directory name to denote the style, e.g. for a VM we would expect to see:
    • /tests/e2e/defaults.linux/main.test.bicep
    • /tests/e2e/waf-aligned.linux/main.test.bicep
    • /tests/e2e/
    • /tests/e2e/

ID: BCPNFR10 - Category: Testing - Test Bicep File Naming

Module owners MUST name their test .bicep files in the /tests/e2e/<defaults/waf-aligned/max/etc.> directories: main.test.bicep as the test framework (CI) relies upon this name.

ID: BCPNFR11 - Category: Testing - Test Tooling

Module owners MUST use the below tooling for unit/linting/static/security analysis tests. These are also used in the AVM Compliance Tests.

  • PSRule for Azure
  • Pester
    • Some tests are provided as part of the AVM Compliance Tests, but you are free to also use Pester for your own tests.

ID: BCPNFR12 - Category: Testing - Deployment Test Naming

Module owners MUST invoke the module in their test using the syntax:

module testDeployment '../../../main.bicep' =

Example 1: Working example with a single deployment

module testDeployment '../../../main.bicep' = {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}'
  params: {

Example 2: Working example using a deployment loop

module testDeployment '../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {
  scope: resourceGroup
  name: '${uniqueString(deployment().name, location)}-test-${serviceShort}-${iteration}'
  params: {

The syntax is used by the ReadMe-generating utility to identify, pull & format usage examples.

ID: BCPNFR13 - Category: Testing - Test file metadata

By default, the ReadMe-generating utility will create usage examples headers based on each e2e folder’s name. Module owners MAY provide a custom name & description by specfying the metadata blocks name & description in their main.test.bicep test files.

For example:

metadata name = 'Using Customer-Managed-Keys with System-Assigned identity'
metadata description = 'This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.'

would lead to a header in the module’s file along the lines of

### Example 1: _Using Customer-Managed-Keys with System-Assigned identity_

This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret.

ID: BCPNFR15 - Category: Contribution/Support - AVM Module Issue template file

As part of the “initial Pull Request” (that publishes the first version of the module), module owners MUST add an entry to the AVM Module Issue template file in the BRM repository (here).

Through this approach, the AVM core team will allow raising a bug or feature request for a module, only after the module gets merged to the BRM repository.

The module name entry MUST be added to the dropdown list with id module-name-dropdown as an option, in alphabetical order.

Module owners MUST ensure that the module name is added in alphabetical order, to simplify selecting the right module name when raising an AVM module issue.

Example - AVM Module Issue template module name entry for the Bicep resource module of Azure Virtual Network (avm/res/network/virtual-network):

- type: dropdown
  id: module-name-dropdown
    label: Module Name
    description: Which existing AVM module is this issue related to?
      - "avm/res/network/virtual-network"