-
Notifications
You must be signed in to change notification settings - Fork 566
Contributing
Looking to contribute to this project, whether that be Bicep code, examples, documentation or GitHub automation, you are in the right place. Please review the rest of this wiki page for important information to help you to start contributing to the project effectively.
Before you start contributing to the ALZ Bicep code, it is highly recommended that you complete the following Microsoft Learn paths, modules & courses:
- Deploy and manage resources in Azure by using Bicep
- Structure your Bicep code for collaboration
- Manage changes to your Bicep code by using Git
To contribute to this project the following tooling is required:

The following tooling/extensions are recommended to assist you developing for the project:
- CodeTour extension for Visual Studio Code
- ARM Tools extension for Visual Studio Code
- ARM Template Viewer extension for Visual Studio Code
- PSRule extension for Visual Studio Code
- EditorConfig for VS Code
- For visibility of Bracket Pairs:
- Inside Visual Studio Code, add
editor.bracketPairColorization.enabled: true to yoursettings.json, to enable bracket pair colorization.
- Inside Visual Studio Code, add
The below guidelines should be adhered to whilst contributing to this projects Bicep code.
Throughout the development of Bicep code you should follow the Bicep Best Practices.
It is suggested to keep this page open whilst developing for easy reference
- Strict
camelCasingmust be used for all elements:- Symbolic names for:
- Types
- Parameters
- Variables
- Resource
- Modules
- Outputs
- Symbolic names for:
- All
parandoutvalues in Bicep templates should include full product name instead ofcamelCasedabbreviation, for example:parExpressRouteGwNameinstead ofparErGwName - Services with "Azure" in the name are abbreviated "Az", for example:
parAzBastionNameinstead ofparAzureBastionName - Use parameter decorators to ensure integrity of user inputs are complete and therefore enable successful deployment
- Only use the
@secure()parameter decorator for inputs. Never for outputs as this is not stored securely and will be stored/shown as plain-text!
- Only use the
- A description is required on parameters to provide an explanation into their function. As metadata is used in Bicep modules,
sys.description('description here')is the chosen formatting. More information can be found in the Azure Bicep documentation - Comments should be provided where additional information/description of what is happening is required, except when a decorator like
@sys.description('Example description')is providing adequate coverage- Single-line
// <comment here>and multi-line/* <comment here> */comments are both welcomed - Provide contextual public Microsoft documentation recommendation references/URLs in comments to help user understanding of code implementation
- Single-line
- All expressions, used in conditionals and loops, should be stored in a variable to simplify code readability
- Specify default values for all parameters where possible - this improves deployment success
- The default value should be called out in the description of the parameter for ease of visibility
- Default values should also be documented in the appropriate location
- Tab indents should be set to
2for all Bicep files - Double line-breaks should exist between each element type section
- When intended for scopes above resource group deployment, targetScope should be indicated at the beginning of the file
| Element Type | Naming Prefix | Example |
|---|---|---|
| Types | typ |
typCustomRole, typSubnetOptions
|
| Parameters | par |
parLocation, parManagementGroupsNamePrefix
|
| Variables | var |
varConditionExpression, varIntermediateRootManagementGroupName
|
| Resources | res |
resIntermediateRootManagementGroup, resResourceGroupLogAnalytics
|
| Modules | mod |
modManagementGroups, modLogAnalytics
|
| Outputs | out |
outIntermediateRootManagementGroupID, outLogAnalyticsWorkspaceID
|
The below guidelines should be adhered to whilst contributing to this projects Bicep code.
-
parLocation- Shall be used for all module parameters specifying the Azure region to which a resource or module will be deployed.
- The only exception to this is when two inter-related services do not have region parity and need to be deployed to different regions. (i.e. Log Analytics and Automation Accounts in China. See logging module for an example)
For all Bicep files created as part of this project they will follow the structure pattern of being grouped by element type, this is shown in the image below:

Types, Parameters, Variables, Resources, Modules & Outputs are all types of elements.
Below is an example of Bicep file complying with the structure and styling guidelines specified above:
// SCOPE
targetScope = 'subscription' //Deploying at Subscription scope to allow resource groups to be created and resources in one deployment
// METADATA
metadata name = 'ALZ Bicep - Example Module'
metadata description = 'ALZ Bicep Module used as an example'
// TYPES
@minValue(0)
type typExampleNonNegativeInteger = int
// PARAMETERS
@sys.description('Example description for parameter.') // Avoid describing default values
param parExampleResourceGroupNamePrefix string = 'TEST'
// VARIABLES
var varExampleResourceGroupName = 'rsg-${parExampleResourceGroupNamePrefix}' // Create name for the example resource group
// RESOURCE DEPLOYMENTS
resource resExampleResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: varExampleResourceGroupName
location: 'uksouth' // Hardcoded as an example of commenting inside a resource
}
/*
No modules being deployed in this example
*/
// OUTPUTS
output outResourceGroupExampleID string = resExampleResourceGroup.id
To author Bicep modules that are in-line with the requirements for this project, the following must be true:
- Follows the Bicep Formatting Guidelines as detailed above
- A new folder per module in the following directory:
infra-as-code/bicep/modules/...- Folder Name will be created with camel case:
infra-as-code/bicep/modules/moduleName
- Folder Name will be created with camel case:
- Each new module folder must contain:
- A
mediafolder that will contain images used in theREADME.md - A
README.mdfor each module in the root of its own folder, as above, detailing the module, what it deploys and any other useful information for consumers.- The
README.mdmust also contain a Bicep visualizer image of the complete module
- The
- Parameter detail files are automatically generated in the
generateddocsfolder of each module after contributing to the repository. A link in theREADME.mdfile must be created. - When contributing a module to the repository, anticipate creation of the markdown for the parameters. To link to the parameters after contribution, use the following structure in the parameters section of the readme:
- A
- [Link to Azure Commercial Cloud/Azure China Cloud](generateddocs/<NAME-OF-BICEP-MODULE-FILE.bicep.md)
On your Pull Request (PR), the following steps will happen:
- PR is created.
- GitHub Action Workflow initialises.
- The PR Branch is checked out.
- A
Bicep Buildis run on each of the modules. - PSDocs For Azure generates a markdown file against each of the built JSON files in the module folders.
- A
generateddocsfolder is created in each of the modules. - The markdown files are stored in this folder for each of the modules.
- Removal of the JSON files from the Bicep Build task is done.
- A git status and push is then done to the same PR for the created/updated generated documentation to complete the workflow.
You MUST manually generate the parameter markdown files for your Bicep modules before submitting a PR to the repository. This is to ensure that the documentation is up-to-date and reflects the current state of the Bicep modules. This will be checked as part of a GitHub Action workflow that runs on your PR and fail if the markdown files are not present or not up-to-date.
To do this copy and run the below PowerShell Script in your PR Branch and then stage and commit the files that are generated/updated once it has ran and completed. You need to be in the root of the ALZ-Bicep repo directory you have cloned to your machine and on the correct branch that your are working on for the PR before running the script!
Please note the script will try to install the
PSDocs.AzurePowerShell Module as this is a requirement. You can install it by running the commandInstall-Module -Name 'PSDocs.Azure' -Repository PSGallery -Forceor by following the instructions here
Write-Host "==> Starting Bicep build (parallel capable)"
$bicepFiles = Get-ChildItem -Recurse -Path infra-as-code/bicep/ -Filter '*.bicep' -Exclude 'callModuleFromACR.example.bicep','orchHubSpoke.bicep'
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "PowerShell version does not support -Parallel. Falling back to sequential builds."
foreach ($f in $bicepFiles) {
Write-Information "==> Attempting Bicep Build For File: $f" -InformationAction Continue
$null = bicep build $f.FullName 2>&1 | Tee-Object -Variable buildOut
if ($LASTEXITCODE -ne 0) { throw "Bicep build failed for $($f.FullName): `n$buildOut" }
}
} else {
$throttle = if ($env:BICEP_BUILD_PARALLEL_LIMIT) { [int]$env:BICEP_BUILD_PARALLEL_LIMIT } else { 8 }
Write-Host "Using parallel builds with ThrottleLimit=$throttle"
$errors = [System.Collections.Concurrent.ConcurrentBag[string]]::new()
$bicepFiles | ForEach-Object -Parallel {
try {
Write-Information "==> [Parallel] Building: $($_.FullName)" -InformationAction Continue
$out = bicep build $_.FullName 2>&1
if ($LASTEXITCODE -ne 0) { throw "Bicep build failed (exit $LASTEXITCODE) for $($_.FullName): `n$out" }
else { $out | ForEach-Object { Write-Host $_ } }
}
catch {
[System.Console]::Error.WriteLine($_)
$errBag = $using:errors
$msg = "{0}: {1}" -f $_.FullName, $_
[void]$errBag.Add($msg)
}
} -ThrottleLimit $throttle
if ($errors.Count -gt 0) {
Write-Host '--- Bicep build errors detected ---'
$errors | ForEach-Object { Write-Host $_ }
throw "One or more Bicep builds failed."
}
}
Install-Module -Name 'PSDocs.Azure' -Repository PSGallery -force; Import-Module PSDocs.Azure -Force
# Scan for Azure template file recursively in the infra-as-code/bicep/ directory and generate markdown in parallel
Write-Host "==> Starting markdown generation (parallel capable)"
$templates = Get-AzDocTemplateFile -Path infra-as-code/bicep/
$mdThrottle = if ($env:MD_GEN_PARALLEL_LIMIT) { [int]$env:MD_GEN_PARALLEL_LIMIT } else { 8 }
$mdErrors = [System.Collections.Concurrent.ConcurrentBag[string]]::new()
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "PowerShell version does not support -Parallel for markdown generation. Falling back to sequential."
foreach ($t in $templates) {
try {
$template = Get-Item -Path $t.TemplateFile
$templateraw = Get-Content -Raw -Path $t.Templatefile
$version = $template.Directory.Name
$docNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($template.Name)
$jobj = ConvertFrom-Json -InputObject $templateraw
$outputpathformds = $template.DirectoryName + '/generateddocs'
New-Item -Path $outputpathformds -ItemType Directory -Force | Out-Null
$convertedfullpath = $template.DirectoryName + "\\" + $template.Name
$jobj | ConvertTo-Json -Depth 100 | Set-Content -Path $convertedfullpath
$mdname = $docNameWithoutExtension + '.bicep'
Invoke-PSDocument -Module PSDocs.Azure -OutputPath $outputpathformds -InputObject $template.FullName -InstanceName $mdname -Culture en-US
}
catch {
Write-Host "[Markdown-Error] $($template.FullName): $_"
$mdErrors.Add("$($template.FullName): $_")
}
}
} else {
Write-Host "Using parallel markdown generation with ThrottleLimit=$mdThrottle"
$templates | ForEach-Object -Parallel {
try {
$template = Get-Item -Path $_.TemplateFile
$templateraw = Get-Content -Raw -Path $_.Templatefile
$version = $template.Directory.Name
$docNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($template.Name)
$jobj = ConvertFrom-Json -InputObject $templateraw
$outputpathformds = $template.DirectoryName + '/generateddocs'
New-Item -Path $outputpathformds -ItemType Directory -Force | Out-Null
$convertedfullpath = $template.DirectoryName + "\\" + $template.Name
$jobj | ConvertTo-Json -Depth 100 | Set-Content -Path $convertedfullpath
$mdname = $docNameWithoutExtension + '.bicep'
Invoke-PSDocument -Module PSDocs.Azure -OutputPath $outputpathformds -InputObject $template.FullName -InstanceName $mdname -Culture en-US
}
catch {
[System.Console]::Error.WriteLine($_)
$mdErrBag = $using:mdErrors
$msg = "{0}: {1}" -f $_.TemplateFile, $_
[void]$mdErrBag.Add($msg)
}
} -ThrottleLimit $mdThrottle
}
if ($mdErrors.Count -gt 0) {
Write-Host '--- Markdown generation errors detected ---'
$mdErrors | ForEach-Object { Write-Host $_ }
throw "One or more markdown generations failed."
}
Get-ChildItem -Recurse -Path infra-as-code/bicep/ -Filter '*.json' -Exclude 'bicepconfig.json','*.parameters.json','*.parameters.*.json','policy_*' | ForEach-Object {
Write-Information "==> Removing generated JSON file $_ from Bicep Build" -InformationAction Continue
Remove-Item -Path $_.FullName
}IMPORTANT: Once the workflow has finished all it should of generated/updated is
.mdfiles and nothing else. Please carefully check the git changes it has made before staging and committing anything.
- A
bicepconfig.jsonfor each module in the root of its own folder is no longer required, as we have a central one in./infra-as-code/bicep/bicepconfig.jsonto help keep maintenance simplified.- There are still separate
bicepconfig.jsonfiles for networking related modules, due to specific overrides required to some linter rules - If you are facing linter errors that justify a override then please create a
bicepconfig.jsonin the root of the module's directory to configure the override of the rules from the central one in./infra-as-code/bicep/bicepconfig.jsonby following the Bicep Linting Documentation
- There are still separate
- The Bicep module file itself
- A
parametersfolder that will contain the parameters files for the module - Parameters
...all.jsonand...min.jsonfiles based on file naming convention below - Parameter files should be named according to the convention:
<module>.<parameterSet>.parameters.<min|all>.json-
<module>denotes the current module (and scope when necessary), for example:roleAssignmentManagementGroup -
<parameterSet>denotes a set of parameters with similar characteristics, for example:securityGroup -
parametersconstant to denote the file as a parameters file -
<min|all>.jsondenotes whether a parameter file contains all possible parameters or only minimum necessary for deployment
-
Each resource must use the latest available, working, API version. If the latest API version cannot be used for any reason, a comment must be placed above the resource in the module file stating why and also called out as part of the PR.
The Bicep linter rule
use-recent-api-versionswill now also check for this 👍
When adding new parameters to a module, changing existing parameter names, or creating a new module, there are some additional steps that must be considered to ensure the Accelerator works with the associated ALZ-PowerShell-Module. The accelerator publishes the ALZ-Powershell.config.json file which contains the configuration required for accelerator deployment. The following diagram outlines the steps for release process and provides additional context for changes that may be required:

If you discover any documentation bugs or would like to request new content, please raise them as an issue on the repo.
Contributions to this wiki are done through the main repo under docs/wiki.
- Wiki Home
- Deployment Flow
- Consumer Guide
- How Does ALZ-Bicep Implement Azure Policies?
- How Does ALZ-Bicep Implement resilient deployments across availability zones?
- Contributing
- Telemetry Tracking Using Customer Usage Attribution (PID)
- Azure Container Registry Deployment - Private Bicep Registry
- Sample Pipelines
- Code tours