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 . 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
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
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
The pipeline will run at every push made to the main branch.
permissions:
id-token: write
contents: read
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
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
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.
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.

SOCIAL SHARE CARD GENERATOR