Consuming Bicep modules – What are my options?

Photo of author

Dan Rios

5 min read

Introduction

There’s a few different options when it comes to consuming Bicep modules, do you need to write your own modules? Use the public Bicep registry? or are there other options to consider?

I’ll detail my experiences & thoughts on this with pros & cons that each of the routes have.

To check these different approaches out you can head over to the Bicepify GitHub project to see more:

https://github.com/riosengineer/Bicepify/tree/main/bicep-examples/consuming-modules

Public Bicep Registry (Now Azure Verified Modules!)

Azure/bicep-registry-modules: Bicep registry modules (github.com)

Microsoft have recently started publishing Bicep modules on the public registry, this means you can consume the modules directly from VSCode like so:

Make sure the Bicep VSCode extension is installed

Pros

  • Less Bicep engineering and management overhead as you won’t need to create, manage or update the modules (except the API versions in the module block)
  • Centrally accessible to everyone
  • Easy to view available Bicep modules from the Intelisense
  • All parameters included (with descriptions when hovering over the param!). Easy to ‘pick up and go’ and accelerate your Bicep journey
  • Azure best practice is baked in to the code by default (although this won’t necessarily stop you deploying with sub-optimal config or misconfigurations)

Cons

  • Can lag behind latest releases whilst Microsoft update API versions and available Azure resources for the modules
  • Bicep Module list is lacking. Many modules are still to be added. But being increased week by week!
  • Less flexibility. Can’t be customised as these are centrally controlled by Microsoft

Private Bicep Registry

Like the Bicep public registry, you can create an internal (private) Bicep registry with an Azure Container Registry, to have an internal bicep registry for use. Some great info in the docs on how to get started: Create private registry for Bicep module – Azure Resource Manager | Microsoft Learn

Example private Bicep ACR, note the br: address is pointing to the ACR manifest URI for that particular module.

You can create an alias for your br: ACR URI address by editing your bicepconfig.json. e.g. ‘br:bicepify/modules/logging:2023-09-29’ would be an example alias of the below.

Pros

  • Flexibility of what modules are available for consumption (including custom modules)
  • Updating to new API versions, new Bicep modules and any updates are likely to be faster than the public registry alternative
  • Security. You control the ACR access and therefore who can use the modules.
  • Brings your modules under central control and helps reduce code drift from static module files in repositories differing from organisation standards

Cons

  • Overhead of managing the registry
  • Version control is very important. Breaking changes could have a large knock on impact if not communicated to teams, and could break deployment code across the board
  • More suited to larger organisations due to management overhead, testing and custom requirements
  • Bicep VSCode Intelisense does not load available modules like the public registry can. This means you have to manually go to the Azure Container Registry and copy the manifest URI to use (Bicep team working on this one, coming before end of the year hopefully)
If you’re after an Azure DevOps pipeline example of how you would publish the modules to an ACR, I have some examples below.

Azure Pipelines YAML example:

trigger:
branches:
include:
– main
paths:
include:
– Bicep/modules/*
exclude:
– Bicep/modules/*/.tests/*
name: $(Date:yyyyMMdd)$(Rev:.r)
variables:
serviceConnectionName: 'svc-connection-name'
stages:
– stage: Build
jobs:
– job:
steps:
– checkout: self
– task: PowerShell@2
displayName: 'Get changed or added files'
inputs:
targetType: filePath
filePath: .scripts/bicep-module-diff.ps1
pwsh: true
– task: PublishPipelineArtifact@1
displayName: 'Publish files as artifact'
condition: and(succeeded(), eq(variables.bicepFiles, 'true'))
inputs:
artifact: 'bicep'
targetPath: '$(Build.ArtifactStagingDirectory)'
publishLocation: pipeline
– task: AzureCLI@2
displayName: 'Validate bicep modules'
inputs:
azureSubscription: $(serviceConnectionName)
addSpnToEnvironment: true
scriptLocation: scriptPath
ScriptType: 'pscore'
scriptPath: .scripts/bicep-validate.ps1
# – stage: Test
# jobs:
# – job:
# steps:
# – checkout: self
# – task: AzureCLI@2
# displayName: 'Run What-If on test modules'
# condition: and(succeeded(), eq(variables.biceptstModuleFiles, 'true'))
# inputs:
# azureSubscription: $(serviceConnectionName)
# addSpnToEnvironment: true
# scriptLocation: scriptPath
# ScriptType: 'pscore'
# scriptPath: .scripts/bicep-whatif-module.ps1
– stage: Deploy
jobs:
– deployment: Deploy
environment: 'brmodules'
displayName: 'Deploy bicep files to registry'
strategy:
runOnce:
deploy:
steps:
– checkout: self
– task: DownloadBuildArtifacts@1
inputs:
artifactName: 'bicep'
allowPartiallySucceededBuilds: false
– task: AzureCLI@2
displayName: 'Deploy bicep files to ACR'
inputs:
azureSubscription: $(serviceConnectionName)
addSpnToEnvironment: true
scriptLocation: scriptPath
ScriptType: 'pscore'
scriptPath: .scripts/bicep-acr-publish.ps1

Bicep Modules – GIT Difference PowerShell script:

$version = get-date Format yyyyMMdd
$commit = "$ENV:BUILD_SOURCEVERSION"
# get the latest Bicep module files changed
$files = $(git diff HEAD HEAD~ nameonly bicep/modules ":(exclude)*/.tests/*" | Where-Object {$_.EndsWith('.bicep')})
if ($files.count -ge 1) {
# copy files to staging
copy-item $files Destination $ENV:BUILD_ARTIFACTSTAGINGDIRECTORY
# set a variable and build number
Write-Host ("##vso[task.setvariable variable=bicepFiles]true")
Write-host ("##vso[build.updatebuildnumber]$($version)")
Write-Host $files updates found
}
else {
Write-Host ("##vso[task.setvariable variable=bicepFiles]false")
}

And lastly, the publish PowerShell script:

# publish bicep modules to acr
$version = get-date Format yyyyMMdd
$modules = Get-ChildItem "$env:SYSTEM_ARTIFACTSDIRECTORY" Include *.bicep Recurse
foreach ($module in $modules) {
$name = $module.BaseName
bicep publish $module.FullName target "br:crriosuksbicep.azurecr.io/bicep/modules/${name}:${version}"
}

Inline Bicep modules

Next up is one of the last module consumption options to consider. Using modules in-line, with a structure very much like my blog post on testing Bicep modules.

In the inline Bicep module, we are calling from a local modules folder when writing in our main.bicep file (seen below) which could be a mixture of your own Bicep modules (custom), or from the below ResourceModules GitHub (see further below) forked. Example:

Example folder structure of this method. Modules are stored in the folder locally in the repository & called from the main file:


└── bicep/
    ├── modules/
    │   ├── storageAccount/
    │   │   ├── storageAccount.bicep
    │   │   ├── metadata.json
    │   │   └── README.md
    │   └── .tests/
    │       └── storageAccount.tests.bicep
    └── prod/
        ├── main.bicep

Pros

  • A lot of the pros are very much applicable from the private bicep registry above to this module method
  • All modules in one place, easy to understand the code flow and where the module code is, to read and understand it’s parameters, settings and properties
  • Great in combination with public registry options where less control and customisation is needed as a half way house option

Cons

  • Code drift. If you’re an MSP and spinning up lots of customer repositories for deployments, code can drift from updates to your ‘gold standard’ templates / modules. This can make it difficult to keep on top of keeping a 1:1 relationship with the module consistency across environments
  • Overhead. Keeping up with updates from the ResourceModule GitHub (below) and general API & resource updates becomes manual

ResourceModules Bicep GitHub (CARML)

Lastly, worthy of it’s own section this one. There is already a large repository for Bicep modules for consumption in the ResourceModules (aka CARML – Common Azure Resource Modules Library) repository, where many modules exist already:

ResourceModules/modules at main · Azure/ResourceModules (github.com)

Even if you just need a start point to then customise from. This is a solid bet as you know you’re starting from a good place (best practice baked in).

The Bicep team are looking to move more and more of these modules into the public registry for use which will mean you can pivot to the public registry over time.

Azure Verified Modules (AVM)

https://azure.github.io/Azure-Verified-Modules/faq/#what-is-happening-to-existing-initiatives-like-carml-and-tfvm

This is still in developement at the time of writing. However, there is a new initiative by the IaC teams at Microsoft to present what good Infrastructure-as-Code looks like. The idea here will be these modules will accelerate teams to deploy with Bicep, using best practice & aligned to the Well-Architected Framework.

Conclusion

I’m personally a big fan of the Bicep public registry wherever possible. It removes so much of the negatives and is so easy for writing code, as you can just directly define parameter values in the module block. I currently use a combination of the public registry and in-line modules forked from the ResourceModules repository. This works really well until the public registry gets up to speed.

3 thoughts on “Consuming Bicep modules – What are my options?”

  1. Hi Dan,

    Thanks for your insightful post on the different options for consuming Bicep modules. It really got me thinking about the best approach for organizations to manage their infrastructure as code.

    One point I’d like to raise is the management overhead that comes with setting up and maintaining a private Bicep registry. While it offers flexibility and control, it also introduces significant operational complexity—particularly around version control, access management, and ensuring consistency across teams.

    Given these challenges, I wonder why organizations should take on this overhead when they could achieve similar outcomes by simply using a shared Azure DevOps repository to manage their Bicep modules. A shared repository allows for centralized management without the need for additional infrastructure, while also leveraging GitHub’s robust versioning, access controls, and collaboration features. Teams can reference these modules directly in their pipelines, ensuring consistency and reducing the risk of code drift.

    By centralizing Bicep modules in a DevOps repo, organizations can avoid the complexity of managing a registry while still reaping the benefits of shared, standardized infrastructure code. It seems like a more streamlined approach, especially for organizations that might not have the scale or resources to justify the additional management burden of a private registry.

    I’d love to hear your thoughts on this. Do you see any potential drawbacks to using a shared DevOps repo instead of a private registry?

    Best regards
    PS your github link is broken btw

    Reply
    • Hey Maxim,

      Thanks for the comment, really insightful and it does raise a valid point. I’ve only seen organisations leverage a private ACR for Bicep modules when security dictates it. The major benefit I can see compared to a central ADO repo, is that you can reference the bicep module ACR in VSCode very easily and directly back to the repository URI like as I touch on briefly in the post.

      This way, the modules are called from a remote central location and can be maintained/versioned as such. With ADO you have to clone the repo locally to consume the modules for each user. Bicep doesn’t support public / private Git repos for module references unlike Terraform, so this is why a private ACR is only really viable for those organisations.

      If you don’t have such strict requirements for Bicep modules I would strongly recommend using the Azure Verified Modules from the public registry as these will have all the best practice baked in and you don’t need to bother wasting time maintaining modules. We use them and it is really a great option.

      Thanks for notifying me re: GitHub link. I have now resolved this, and embedded a GitHub Gist for the Private ACR automated versioning/publish via Pipeline directly into the article – what do you think?

      Dan

      Reply

Leave a comment


Skip to content