DEV Community

Cover image for Create a GitHub pipeline to test, review, and deploy a Bicep template.
Olivier Miossec
Olivier Miossec

Posted on

Create a GitHub pipeline to test, review, and deploy a Bicep template.

Working with Bicep means you must deploy your infrastructure with a pipeline. There are a few steps to create your pipeline using GitHub actions.

  • Having an identity to interact with your Azure tenant
  • Testing your code before trying to deploy anything, you don’t have to leave an error before going further
  • Optional, you may want to verify what will happen, before deploying
  • Deploying the bicep file

The code used in this post is available on my GitHub account

To illustrate this point I will use a simple Bicep template. This template deploys a virtual network with subnets. It is a very simple template to illustrate the process.

To start, we need a GitHub repository with a Bicep folder and the main template. An identity is required to deploy the template in Azure.

We need a managed identity

$managedIdentityName = "mi-BicepDemo"
$repoName = "bicep-pipeline"
$githubOrga = "omiossec"
$environmentName = "DevTo-Demo"

$managedIdentity = New-AzUserAssignedIdentity -Name $managedIdentityName -ResourceGroupName managed-identity -Location northeurope
Enter fullscreen mode Exit fullscreen mode

And create a federated identity.

$subjectUri =  "repo:$($githubOrga)/$($repoName):environment:$($environmentName)"

New-AzFederatedIdentityCredential -ResourceGroupName managed-identity -IdentityName $managedIdentity.name  -Name fed-bicepDemo -Issuer "https://token.actions.githubusercontent.com" -Subject $subjectUri
Enter fullscreen mode Exit fullscreen mode

After that, the environment “DevTo-Demo” must be created in the GitHub repository. To create the environment, go to “Settings” in the top menu and click “Environments”. After creating the environment, secrets need to be added. By using a federated identity you only need to use 3 values, the managed identity Client ID, the Tenant ID, and the target subscription. The managed identity secret is not required.

  • AZURE_CLIENT_ID, you can have the value with $managedIdentity.ClientID
  • AZURE_TENANT_ID, the value is given by $managedIdentity.TenantId
  • AZURE_SUBSCRIPTION_ID, the target subscription ID

But before using the federated identity we need to ensure that mi-BicepDemo has valid access to the subscription, if not the pipeline will not be able to log in, and an error with exit code 1 will be generated.

Assign the contributor role to the mi-BicepDemo identity on the target resource group. The resource group name can be added in the environment variable RESOURCE_GROUP.

Now, we can start to create the workflow. At the root of the GitHub repository, in a .github/workflow folder, create a yaml file, demo-bicep.yaml

name: Bicep Pipeline Demo

on: 
  push: 
    branches: 
      - main

permissions:
  id-token: write
  contents: read

jobs:

  Bicep-demos-login:
    name: run azure tests
    runs-on: ubuntu-latest
    environment: DevTo-Demo
    steps:

      - name: Checkout
        uses: actions/checkout@v4

      - name: login to Azure 
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
          enable-AzPSSession: true
Enter fullscreen mode Exit fullscreen mode

The pipeline will run at every push made to the main branch.

permissions:
  id-token: write
  contents: read
Enter fullscreen mode Exit fullscreen mode

This is needed to use the federated identity.

Then we can check out and log in to Azure.

Before deploying the infrastructure to Azure, we want to test the validity of the bicep file.
The easiest way to do it is to use Bicep Lint.

So we can have a step like this.

      - name: Bicep Lint
        uses: azure/cli@v2
        with:
          azcliversion: latest
          inlineScript: |
            az bicep lint --file ./Bicep/main.bicep
Enter fullscreen mode Exit fullscreen mode

But there is a problem with this approach: the pipeline will fail only with the linter error but not with a warning. A malformed resource will trigger an error but exposing a secret will only trigger a warning.
To change the behavior we need to create bicepconfig.json in the same directory as the bicep file, or in a parent directory.

You can find the file here

I have changed a few rules to trigger an error when the location is hard-coded or when variables or parameters are not used.

After testing the bicep file for errors and best practices, the next step is to see what will be changed. Azure Bicep has the option to manage that, what-if.

      - name: Bicep What-If 
        uses: Azure/cli@v2 
        with: 
          azcliversion: latest
          inlineScript: | 
            az deployment group create --what-if --name whatif --template-file ./Bicep/main.bicep --resource-group ${{ vars.RESOURCE_GROUP }}
Enter fullscreen mode Exit fullscreen mode

The last step is to deploy the Bicep file. But we want to ensure that there is a review of the what-if output before deploying anything. GitHub has an approval mechanism with environments. An environment can require approval before running a deployment (for GitHub Pro and free, the repository must be public).

In our case, we need to create a new environment, bicep-deploy, with the same variable and secret as the devTo-demo. The option “Required reviewers” must be selected and reviewers added.

We need to configure the managed identity to allow the new environment.

$subjectUri =  "repo:$($githubOrga)/$($repoName):environment:$($environmentName)"

New-AzFederatedIdentityCredential -ResourceGroupName managed-identity -IdentityName $managedIdentity.name  -Name fed-bicepDemo -Issuer "https://token.actions.githubusercontent.com" -Subject $subjectUri
Enter fullscreen mode Exit fullscreen mode

A new job using this environment needs to be added.

Bicep-deploy:
    name: run azure deploy
    runs-on: ubuntu-latest
    environment: bicep-deploy
    needs: Bicep-demos-login
    steps:

      - name: Checkout
        uses: actions/checkout@v4

      - name: login to Azure 
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
          enable-AzPSSession: true

      - name: Bicep deploy
        uses: Azure/cli@v2 
        with: 
          azcliversion: latest
          inlineScript: | 
            az deployment group create --name whatif --template-file ./Bicep/main.bicep --resource-group ${{ vars.RESOURCE_GROUP }}
Enter fullscreen mode Exit fullscreen mode

The complete workflow is available here

After updating the workflow, you will see the two linked jobs. The instruction “needs: Bicep-demos-login” will make the second job, Bicep-Deploy, after completing the first. As the Bicep-deploy environment requires approval, one reviewer will need to check the output of the what-if before deploying to Azure.

Image description

This is a simple example of managing a pipeline for Bicep deployment with testing, review, approval, and deployment. This is a minimalistic approach. For example, if a resource type doesn’t exist, the linter will only show a warning, which is not enough to stop the pipeline. You may need to dig into the linter output.

Top comments (0)