Mastering Bicep Documentation: Annotate Your Scripts for Seamless Collaboration

Dariusz
medialesson
Published in
4 min readMay 29, 2024

--

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

  1. Using Decorators
  2. 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'
}

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.

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'
}

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'.'.

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

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

  1. Converts the bicep modules into ARM templates
  2. Runs PSDocs.Azure to generate the documentation
  3. 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.

https://gist.github.com/dariuszparys/c8ed13e70295340b90c1c46ebab4eea2

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.

Originally published at https://www.dariuszparys.com on May 29, 2024.

--

--

Dariusz
medialesson

Principal Software Engineer at medialesson GmbH