Mastering Bicep Documentation: Annotate Your Scripts for Seamless Collaboration
Learn how to use decorators and metadata in Bicep to create clear, informative documentation for your infrastructure-as-code scripts. Enhance collaboration and knowledge sharing.

Bicep allows you to add decorators to describe and enforce the correct usage of modules. In addition, it will enable you to add metadata to enhance the documentation of the infrastructure as code delivery. You can use both sources to create automatically Markdown documentation to have a handy reference for future users and developers of your scripts.
How to Annotate a Bicep File
There are two ways to include annotations in Bicep
- Using Decorators
- Using Metadata
Decorators
Decorators are used during execution to describe parameters and enforce the correctness of passed values.
A simple example will showcase this. Let's use this resource definition for a storage module.
param storageAccountName string
param skuName string = 'Standard_LRS'
param location string = resourceGroup().location
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: skuName
}
kind: 'StorageV2'
}
Simple Storage Module without Annotations
Let's focus on the skuName
parameter. t is a string and has a default value. If you pass in a value like something
which is not valid at all, you will not spot the error until deployment. Even az deployment group validate
and a what-if
analysis will not spot the error.
❯ az deployment group what-if --template-file .\base\storage.bicep --resource-group rg-demo --parameters skuName=something --parameters storageAccountName=289jijdj3ei
Resource and property changes are indicated with this symbol:
+ Create
The deployment will update the following scope:
Scope: /subscriptions/___/resourceGroups/rg-demo
+ Microsoft.Storage/storageAccounts/289jijdj3ei [2023-01-01]
apiVersion: "2023-01-01"
id: "/subscriptions/___/resourceGroups/rg-demo/providers/Microsoft.Storage/storageAccounts/289jijdj3ei"
kind: "StorageV2"
location: "northeurope"
name: "289jijdj3ei"
sku.name: "something"
type: "Microsoft.Storage/storageAccounts"
Resource changes: 1 to create.
az deployment group what-if doesn't care about the wrong value
If we modify the corresponding bicep file with decorators, we can enforce an error using the analysis tools. Here is a modified version of the same storage module, now using decorators to describe expected parameter behavior in addition to descriptions.
@minLength(3)
@maxLength(24)
@description('Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only.')
param storageAccountName string
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_RAGRS'
'Standard_ZRS'
'Premium_LRS'
])
@description('Please talk with the subscription owner if a different SKU is needed')
param skuName string = 'Standard_LRS'
param location string = resourceGroup().location
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: skuName
}
kind: 'StorageV2'
}
Storage module with decorators
Now that the what-if analysis is being executed, the what-if analysis will correctly interrupt with the error message of the wrong value in the parameter skuName
.
❯ az deployment group what-if --template-file .\base\storage.bicep --resource-group rg-demo --parameters skuName=something --parameters storageAccountName=289jijdj3ei
InvalidTemplate - Deployment template validation failed: 'The provided value for the template parameter 'skuName' is not valid. The value 'something' is not part of the allowed value(s): 'Standard_LRS,Standard_GRS,Standard_RAGRS,Standard_ZRS,Premium_LRS'.'.
Now the what-if analysis is catching the wrong value
Metadata
In addition to decorators, metadata can enhance the bicep file you are working on. Metadata doesn't have any particular keywords. It can be anything. The syntax is simply metadata <metadata-name> = ANY
.
Metadata also doesn't enforce any validations on the bicep file. It is pure annotations of the content. You could add more context to the file to explain your decisions and give developers and users of the script a better understanding.
Taking the above example, let's annotate this file with additional metadata information.
metadata name = 'Simple Storage Module'
metadata summary = '''
This storage module is used for non critical business data.
'''
@minLength(3)
@maxLength(24)
@description('Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only.')
param storageAccountName string
@allowed([
'Standard_LRS'
'Standard_GRS'
'Standard_RAGRS'
'Standard_ZRS'
'Premium_LRS'
])
@description('Please talk with the subscription owner if a different SKU is needed')
param skuName string = 'Standard_LRS'
param location string = resourceGroup().location
@description('''
Please specify here an object that includes the environment and the owner
of the corresponding resource. See the example
''')
@metadata({
example: {
env: 'dev'
owner: 'bar'
}
})
param tags object = {
env: 'dev'
owner: 'foo'
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: skuName
}
kind: 'StorageV2'
tags: tags
}
@description('Name of the storage account created. Can be used to reference the resource')
output name string = storageAccount.name
A more comprehensive use of metadata
Beneath the metadata fields' name
and summary
, there is also descriptions and a metadata decorator on top of the parameter tags
.
Generate Markdown Documentation
We now have everything in the Bicep to generate the markdown documentation. We need an additional component to install on the system to do this. This component is PSDocs for Azure.
PSDocs for Azure
This tool takes an ARM template as input and uses it as a base for generating markdown documentation. It also leverages any metadata included in the ARM template.
The fun fact is that when working with Bicep, we don't have any ARM templates by default.
In order to use it we need a script that
- Converts the bicep modules into ARM templates
- Runs PSDocs.Azure to generate the documentation
- Combine multiple docs into one documentation file
As a prerequisite, we need PSDocs.Azure installed on our Powershell environment. The detailed instructions here.
Conversion Script
I created a Powershell script that will generate based on the folder where the biceps store the corresponding documentation and collect them into one final document stored in the desired output folder.

Powershell script to generate markdown documentation using PSDocs.Azure
To use the script on the current project root and store it in the folder docs
with the name readme.md
you call it
.\tools\generate-bicep-documentation.ps1 -BicepDirectory . -DestinationFile docs/readme.md
The Result
The storage module documentation in our sample is created using the generation script before and contains all metadata and decorator annotations, which look like this.
In addition to calling such scripts manually, you can create a pipeline step in your CI/CD system to generate and update that documentation on each push so you don't have outdated documents when people look things up.