Exercise - Publish a module to a registry

Completed

In your toy company, you've been publishing your Bicep modules into a registry. You've been running the publishing process manually from your own computer. Now, you want to create a workflow to handle the publishing process.

In this exercise, you'll:

  • Create a container registry for your Bicep modules.
  • Add a lint job to the workflow.
  • Add a workflow job to publish the module to your registry.
  • Verify that your workflow runs successfully.
  • Check the published module in your registry.

Create a container registry

Before you can publish modules, you need to create a registry for your organization to use. Here, you use the Azure portal to create a registry.

  1. In your browser, create a new container registry within the Azure portal.

  2. On the Basics tab, select your target subscription and the ToyReusable resource group that you created earlier.

  3. Enter a name for your registry and a location that's close to you.

    Important

    The registry name must be unique within Azure and contain 5-50 alphanumeric characters. A check mark next to the registry name indicates that the name you chose is available.

  4. For Pricing plan, select Basic.

    Leave the default values for the other configuration settings.

  5. Select Review + create.

    Screenshot of the Azure portal that shows the container registry creation page.

  6. When the Validation passed message appears, select Create.

    Wait for the deployment to finish, which usually takes 1-2 minutes.

  7. When the Deployment succeeded message appears, select Go to resource to open the container registry.

    Screenshot of the Azure portal that shows the container registry deployment, with the button for going to a resource highlighted.

  8. In the container registry's Overview area, note the value of the Login server setting. The name resembles yourregistryname.azurecr.io.

    Screenshot of the Azure portal that shows the container registry's details, with the login server highlighted.

    You'll need this value shortly.

Add a module metadata file

In the preceding unit, you learned about the importance of having a versioning strategy for your modules. You also learned how to use module metadata files to specify the major and minor version number of your module within a workflow. Here, you add a metadata file for your storage account module.

  1. In Visual Studio Code, expand the modules/storage-account folder in the root of your repository.

  2. Create a new file named metadata.json.

    Screenshot of Visual Studio Code that shows the location of the metadata dot J S O N file.

  3. Add the following content to the file:

    {
      "version": {
        "major": 1,
        "minor": 2
      }
    }
    

    In the metadata file, you separately define the major and minor version numbers. Whenever your workflow runs, the workflow combines these numbers together with the workflow's run number to form a complete version number.

  4. Save your changes to the file.

Update your workflow definition and add a lint job

Your repository contains a draft of a workflow that you can use as a starting point.

  1. In Visual Studio Code, expand the .github/workflows folder in the root of the repository.

  2. Open the module-storage-account.yml file.

    Screenshot of Visual Studio Code that shows the location of the workflow definition file.

  3. Update the value of the MODULE_REGISTRY_SERVER environment variable to your container registry's server name. You copied that name earlier in this exercise.

    For example, if your registry's login server is yourregistryname.azurecr.io, your code is like this example:

    env:
      MODULE_NAME: storage-account
      MODULE_REGISTRY_SERVER: yourregistryname.azurecr.io
      MODULE_FILE_PATH: modules/storage-account/main.bicep
      MODULE_METADATA_FILE_PATH: modules/storage-account/metadata.json
    
  4. At the bottom of the file, for the # To be added comment, add the following lint job definition:

    jobs:
      lint:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
        - name: Run Bicep linter
          run: az bicep build --file ${{ env.MODULE_FILE_PATH }}
    

Add a publish job to your workflow

Now, you can add a second job to publish the module to your container registry.

  1. At the bottom of the module-storage-account.yml file, add the first part of the publish job's definition.

    publish:
      runs-on: ubuntu-latest
      needs: [ lint ]
      steps:
      - uses: actions/checkout@v3
      - uses: azure/login@v1
        name: Sign in to Azure
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
    

    The initial two steps in this definition are to check out the code from your repository and sign in to Azure.

  2. Below the code that you just added, add another step that reads the version number from your module's metadata.json file and sets it as an environment variable.

    - name: Get module version number
      run: |
        majorMinorVersionNumber=$(jq '(.version.major | tostring) + "." + (.version.minor | tostring)' ${{ env.MODULE_METADATA_FILE_PATH }} -r )
        versionNumber="$majorMinorVersionNumber.${{ github.run_number }}"
        echo "MODULE_VERSION=$versionNumber" >> $GITHUB_ENV
    

    The step runs a script that uses the jq command-line application to parse the JSON file.

  3. After the step that you created, add a final step to publish the module to the registry.

    - uses: azure/cli@v1
      name: Publish module
      with:
        inlineScript: |
          az bicep publish \
            --target 'br:${{ env.MODULE_REGISTRY_SERVER }}/${{ env.MODULE_NAME }}:${{ env.MODULE_VERSION }}' \
            --file ${{ env.MODULE_FILE_PATH }}
    

    This step constructs the value of the --target argument dynamically. It combines the value of the registry server, the module name, and the version number.

  4. Save your changes to the file.

Verify and commit your workflow definition

  1. Verify that your module-storage-account.yml file looks like the following example:

    name: module-storage-account
    concurrency: module-storage-account
    
    on:
      workflow_dispatch:
      push:
        branches:
          - main
        paths:
          - 'modules/storage-account/**'
    
    permissions:
      id-token: write
      contents: read
    
    env:
      MODULE_NAME: storage-account
      MODULE_REGISTRY_SERVER: yourregistryname.azurecr.io
      MODULE_FILE_PATH: modules/storage-account/main.bicep
      MODULE_METADATA_FILE_PATH: modules/storage-account/metadata.json
    
    jobs:
      lint:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
        - name: Run Bicep linter
          run: az bicep build --file ${{ env.MODULE_FILE_PATH }}
    
      publish:
        runs-on: ubuntu-latest
        needs: [ lint ]
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - name: Get module version number
          run: |
            majorMinorVersionNumber=$(jq '(.version.major | tostring) + "." + (.version.minor | tostring)' ${{ env.MODULE_METADATA_FILE_PATH }} -r )
            versionNumber="$majorMinorVersionNumber.${{ github.run_number }}"
            echo "MODULE_VERSION=$versionNumber" >> $GITHUB_ENV
        - uses: azure/cli@v1
          name: Publish module
          with:
            inlineScript: |
              az bicep publish \
                --target 'br:${{ env.MODULE_REGISTRY_SERVER }}/${{ env.MODULE_NAME }}:${{ env.MODULE_VERSION }}' \
                --file ${{ env.MODULE_FILE_PATH }}
    

    If the file contents are different, update it to match this example, and then save the file.

  2. Commit and push your changes to your Git repository by running the following commands in the Visual Studio Code terminal:

    git add .
    git commit -m "Add lint and publish jobs to storage account module workflow"
    git push
    

Trigger the workflow

  1. In your browser, go to your GitHub repository and select the Actions tab.

  2. Select the module-storage-account workflow.

    Notice that a workflow run is already in progress. The push trigger fired because you modified the metadata.json file within the module's folder.

  3. Select the latest run in the list.

    Screenshot of GitHub that highlights the latest run of the module's workflow.

    Wait for the workflow run to finish. The Bicep module is published to your container registry.

    Note the workflow's run number, which is probably 3.

Review the module in the registry

You can also view the published module in the Azure portal.

  1. In your browser, go to the Azure portal.

  2. Go to the ToyReusable resource group.

  3. In Resources, select the container registry that you created previously.

  4. Select Services > Repositories from the menu. Then, select the modules\storage-account repository, which represents the module that your workflow published.

    Screenshot of the Azure portal that shows a Bicep module in the container registry.

    Notice that there's a single tag, which matches the version number of the module that your workflow published. The major version (1) and minor version (2) match the version numbers that you defined in the metadata.json file. The revision number (3) matches the workflow's run number.

Clean up the resources

Now that you've completed the exercise, you can remove the resources so you aren't billed for them.

In the Visual Studio Code terminal, run the following command:

az group delete --resource-group ToyReusable --yes --no-wait

The resource group is deleted in the background.

Remove-AzResourceGroup -Name ToyReusable -Force

You can also remove the GitHub secrets and repository, and the Azure workload identities.

  • GitHub secrets

    1. From the GitHub repository, go to Settings > Secrets and variables > Actions.
    2. For each saved GitHub secret, select the Delete <secret-name> icon and follow the prompts.
  • GitHub repository

    1. Go to Settings > General.
    2. Select Delete this repository and follow the prompts.
  • Azure App registration's federated credentials and service principal.

    1. From the portal home page, search for Microsoft Entra ID and select it from the list of Services.
    2. Go to Manage > App registrations.
    3. In the Owned applications tab, select toy-reusable.
    4. Select Delete and follow the prompts.
    5. Select the Deleted applications tab.
    6. Select toy-reusable, select Delete permanently, and then select Yes to permanently delete the app registration.

    Important

    It's possible to have duplicate app registration and service principal names. We recommend that you verify the application ID to make sure you're deleting the correct resource.