How to deploy an Azure Bicep template with Github Actions

An easy way to automate your infrastructure with Github Actions

On this post, we’ll see how to deploy an Azure Bicep template using the power of Github Actions

For security reasons, all the GUIDs you see in this post, whether in text or image format, are fictitious

Step 1: Make your Azure Bicep template

The following Azure Bicep template will create some resources within a subscription:

  • Resource Group
  • Static Web App

We could include all the necessary resources, dependencies, and secrets capture, but for now that’s enough.

This is the structure of our example:

├── 📁 modules
│   ├── 📄 static-web-app-module.bicep
└── 📄 template.bicep

Here is the template.bicep file:

targetScope = 'subscription'

@description('the location of the resource group')
param location string

resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
  name: 'my-resource-group'
  location: location
}

module staticAppDeployment './modules/static-web-app-module.bicep' = {
  name: 'static-web-app-deployment'
  scope: resourceGroup
  params: {
    staticAppName: 'my-static-web-app'
  }
}

And here is the static-web-app-module.bicep file:

@description('static app name')
param staticAppName string

resource staticWebSite 'Microsoft.Web/staticSites@2022-03-01' = {
  location: resourceGroup().location
  name: staticAppName 
  sku: {
    name: 'Free' 
    tier: 'Free'
  }  
}

Step 2: Generate a Service Principal

Here are some steps to create a Service Principal on Azure, but you can find a better and more comprehensive explanation in the Microsoft Learning docs.

There are two flavors to create a Service Principal

With command line

az login --tenant <tenantId>

After login process, you will see the subscriptions where you can access. Capture the property id of subscription where you want to deploy your resources.

az ad app create --display-name <service-principal-name>

Capture the property appId from Azure CLI response and pass it to the next command.

az ad app credential reset --id <appId>

Azure CLI will show a response like this:

{
  "appId": "a62a6111-0ffd-4ebd-8c84-a53c86fc59c8",
  "name": "a62a6111-0ffd-4ebd-8c84-a53c86fc59c8",
  "password": "KkkBeJ6a~eC6S_huBs-G9rD0-RovtBPIz~",
  "tenant": "d03f08c9-a5cc-4f4c-b063-b3775fb9824b"
}

Then capture the properties password and tenant from Azure CLI response

With Azure Portal

First, you should access the Azure Active Directory in witch you want to deploy your resources and navigate to the App Registration blade.

Next, click on New registration and assign a propper name.

Now, navigate to Certificates & secrets, generate a new secret, and capture its value (be careful).

We also need to navigate to Certificates & secrets, generate a new one secret and capture the value (be carefull).

Then go to your subscription (who pays the party) to capture the property Subscription ID, then access to Access control (IAM) and assing the recently created Service Principal the Contributor permission.

Contributor permissions could be excessive, so you should fine-tune the access level.

At this checkpoint you should have the following properties:

  • Application (client) ID
  • Directory (tenant) ID
  • Secret value
  • Subscription ID

Step 3: Store configuration in your Github repository

The previous values should be stored in Github Actions secrets settings section (or the vault you prefer, like Azure Key Vault) witch is available on this section: https://github.com/{user}/{repository}/settings/secrets/actions

🚨 But remember, never hardcode your secrets!

Step 4: Make the Github Action

The following .yml file will do some things:

  • Log in into Azure using the action azure/login@v1 and the parameters you obtained in previous steps. This action does exactly the same thing that you do on your own machine when you run the az login command, but using a JSON.
{
  "clientId": "ec8b0ea7-436c-4a4e-93e9-ded6152c27c1",
  "clientSecret": "KkkBeJ6a~eC6S_huBs-G9rD0-RovtBPIz~",
  "subscriptionId": "864358cc-35af-456d-96da-2244f9f2aa76",
  "tenantId": "864358cc-35af-456d-96da-2244f9f2aa76"
}
  • It will then deploy the Azure Bicep template using the action azure/arm-deploy@v1. In this case, the action does the same as the command az deployment sub create --location <location> --template-file <path-to-bicep>
name: deploy bicep template
on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  contents: read 

jobs:

  main:
    runs-on: ubuntu-latest
        
    steps: 

      - name: '🔻 checkout'
        uses: actions/checkout@v2
        
      - name: '🔓 az login'
        uses: azure/login@v1
        with:
          creds: '{"clientId": "${{ secrets.APPLICATION_ID }}", "clientSecret": "${{ secrets.APPLICATION_SECRET }}", "subscriptionId": "${{ secrets.SUBSCRIPTION_ID }}", "tenantId": "${{ secrets.TENANT_ID }}"}'

      - name: '🚀 deploy'
        uses: azure/arm-deploy@v1
        with:
          subscriptionId: ${{ secrets.SUBSCRIPTION_ID }}
          template: './template.bicep'
          region: 'westeurope'
          deploymentName: "deployment-${{ github.run_id }}"
          parameters: 'location="westeurope"'
          failOnStdErr: false
          scope: 'subscription'
              
      - name: '🔒 az logout'
        run: |
            az logout             
        if: always()

After executing the template, you can navigate to your subscription and then to the Deployments section, usually accesible via this link

https://portal.azure.com/#@{tenantName}.onmicrosoft.com/resource/subscriptions/{subscriptionId}/subdeployments

And… that’s it folks!