How to Create and Test Bicep Modules for Azure Bicep Registry

In this blog post, we'll walk you through the process of creating, testing, and deploying Bicep modules in Azure DevOps, all while ensuring your modules are fully validated and ready to be published into the Azure Bicep Registry.

By the end of this guide, you’ll have a clear pipeline for Bicep module development, ensuring smooth deployment and future scalability.

1. What Are Azure Bicep Modules?

Azure Bicep is a domain-specific language (DSL) for deploying Azure resources declaratively. Bicep simplifies the process of defining your Azure infrastructure as code (IaC). A Bicep module is essentially a reusable piece of Bicep code that can be shared, versioned, and deployed independently.

With Azure Bicep Registry, you can publish your Bicep modules for reuse across different environments, projects, or teams, enhancing your DevOps pipeline and creating scalable infrastructure-as-code solutions.


2. Prerequisites for Developing and Testing Bicep Modules

Before you start developing and testing your Bicep modules, ensure you have the following:

  • Azure Subscription: A development subscription where resources can be deployed for testing.
  • Azure Bicep Registry: This will store your modules once they are validated and ready for production.
  • Azure DevOps: The main platform to run the CI/CD pipeline for testing and deploying your Bicep modules.
  • Service Connection: An Azure DevOps Service Connection with the right permissions to your Azure environment.

3. Step-by-Step Guide: Creating and Testing Bicep Modules in a Single Repository

When managing multiple Bicep modules in a single shared repository, it’s crucial to establish an efficient structure and process. Here’s the best approach:

Repository Structure:

  • Organize Bicep modules in separate subfolders:
    /modules
        /storageAccount
            - main.bicep
            - parameters.json
        /keyVault
            - main.bicep
            - parameters.json
        /sqlServer
            - main.bicep
            - parameters.json
    
  • Each folder represents a distinct Bicep module, containing the core main.bicep file and relevant parameter files.

Version Control:

  • Implement a strategy to version your modules, such as using Git tags or branches.
  • Each module should be independently testable, with a pipeline that scales as you add new modules.

4. Implementing the Azure DevOps Pipeline with Validation and Deployment

Now that your repository is structured, let's dive into building a robust Azure DevOps pipeline to validate, deploy, and test your Bicep modules. Below is the full pipeline YAML for deploying a selected module, validating it, and ensuring post-deployment success.

Pipeline Breakdown:

  • Manual Trigger: The pipeline is manually triggered via Azure DevOps UI.
  • Validation Stage: Ensures the Bicep code is valid before deployment.
  • Deployment Stage: Deploys the selected module to the specified environment.
  • Post-Deployment Validation: Ensures resources are properly deployed.
trigger: none  # No automatic trigger, manual execution only

pr: none  # No automatic PR validation, triggered manually

parameters:
  - name: moduleName
    displayName: 'Module to Test'
    type: string
    default: 'storageAccount'
    values:
      - storageAccount
      - keyVault
      - sqlServer
      - anyOtherModule

stages:
  # Stage 1: Validate the Bicep Module Syntax
  - stage: ValidateBicepModule
    displayName: 'Validate Bicep Module Syntax'
    jobs:
      - job: ValidateSyntax
        displayName: 'Validate Bicep Syntax'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: self
          - task: AzureCLI@2
            inputs:
              azureSubscription: '$(serviceConnectionName)'
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: |
                echo "Validating Bicep Syntax for module: $(moduleName)..."
                az bicep build --file ./modules/$(moduleName)/main.bicep
                if [[ $? -ne 0 ]]; then
                  echo "Bicep validation failed. Exiting."
                  exit 1
            displayName: 'Validate Bicep Syntax'

  # Stage 2: Deploy the Bicep Module
  - stage: DeployBicepModule
    displayName: 'Deploy Bicep Module'
    dependsOn: ValidateBicepModule
    jobs:
      - job: DeployModule
        displayName: 'Deploy and Test Selected Bicep Module'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: self
          - script: |
              echo "Deploying module: $(moduleName)"
            displayName: 'Log Selected Parameters'
          - task: AzureCLI@2
            inputs:
              azureSubscription: '$(serviceConnectionName)'
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: |
                resourceGroup="my-rg"
                paramFile="./modules/$(moduleName)/parameters.json"
                echo "Deploying $(moduleName) to resource group: $resourceGroup"
                az deployment group create \
                  --resource-group $resourceGroup \
                  --template-file ./modules/$(moduleName)/main.bicep \
                  --parameters $paramFile
            displayName: 'Deploy Bicep Module'

  # Stage 3: Post-Deployment Validation
  - stage: ValidateDeployment
    displayName: 'Post-Deployment Validation'
    dependsOn: DeployBicepModule
    jobs:
      - job: ValidateResources
        displayName: 'Validate Deployed Resources'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - script: |
              echo "Running post-deployment validation for module: $(moduleName)"
              # Custom validation steps
            displayName: 'Post-Deployment Validation'

Pipeline Features:

  • Manual Trigger: Developers can manually run this pipeline by selecting the module they wish to test.
  • Validation Stage: This ensures the Bicep syntax is correct before any deployment happens, reducing errors in production.
  • Dynamic Resource Group: Adjust the resource group and parameter files dynamically based on the module being deployed.

5. Publishing Modules to Azure Bicep Registry

Once your Bicep module passes the testing and validation stages, you can publish it to Azure Bicep Registry.

Steps to Publish:

  1. Log in to your Azure subscription.
  2. Use the Azure CLI to publish the module:
    az bicep publish \
      --file ./modules/storageAccount/main.bicep \
      --target br:<registry-name>.azurecr.io/storageAccount:v1.0.0
    
  3. Replace <registry-name> with your Bicep registry, and update the version number as needed.
  4. Once published, your module is available in the registry for reuse across multiple environments.

Key Benefits of Azure Bicep Registry:

  • Centralized Module Management: Store all your Bicep modules in one place.
  • Version Control: Ensure each module version is tracked and available for deployment.
  • Reuse: Modules in the registry can be easily reused across teams and environments.

6. Conclusion

By following this guide, you now have a fully automated pipeline for creating, testing, validating, and deploying Azure Bicep modules. With the Azure Bicep Registry, you can take your infrastructure as code to the next level by making modules reusable and version-controlled across environments.

Key takeaways:

  • A well-structured repository ensures scalability as new modules are added.
  • The Azure DevOps pipeline ensures that only validated Bicep code reaches your environments.
  • Publishing to the Azure Bicep Registry enables efficient reuse and version control of infrastructure modules.

Start implementing your Bicep modules today and streamline your DevOps processes!