Deploying Azure Bicep using GitHub Actions
Tags: azure, github, devops
Published: 2022-12-01 14:00:00


Deploying your resources to Azure using [Infrastructure as Code (IaC](https://www.techielass.com/infrastructure-as-code-the-benefits-and-the-tools/)) is often a more efficient and reliable way to deploy resources to Azure than deploying them manually. 

There are many ways for you to deploy your IaC templates,  you can manually send commands instructing the templates to deploy.  Alternatively you can use DevOps tooling such as Azure DevOps, [GitHub Actions](https://youtu.be/bnoh9cLxbLM), or something similar. 

In this blog post I want to walk you through the process of deploying an [Azure Bicep](https://www.techielass.com/getting-started-with-azure-bicep/) template to Azure using GitHub Actions. 

## Azure Bicep template files

I am going to deploy an Azure Storage account to Azure during this process. If you are unfamiliar with Bicep templates, check out my video which walks you through [building your first Bicep template](https://youtu.be/yTMYp2cR_Bg). 

## Create a repository within GitHub

The first thing that we want to do is create a repository within GitHub so we can store our Bicep templates and also create our GitHub Action.

If you don’t already have a GitHub account, it is fast and [free to sign up for an account] (https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F&source=header-home). 

In the upper right corner of any page of GitHub use the + drop down menu and select **New repository. **


![Create new repository in GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxoh3yela26679hsqyjq.png)

You will have the option of using a template to create your repository, in this case a template isn’t needed. 

In the Owner drop-down select the account you wish to create the repository on. 


![Select GitHub repository owner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zvpt3co1rf8j3rn3ktp.PNG)

Type a name for your repository and an optional description. 

Choose a repository visibility. 

![GitHub repository visibility](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kk3a99p0bfpg167tdmmd.PNG)

Click **Create repository.**

![Create repository](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rbb9q9fx2vd9kw0f8tmh.PNG)

## Store Azure Bicep files within GitHub

Now that a repository within GitHub has been created it’s time to get the Bicep files into that repository. 

There are a number of ways this can be done.   Using the Git command line tool is usually my preferred way. If you are unfamiliar with Git you can follow along my [14 days of Git series](https://www.techielass.com/tag/14daysofgit/) to learn more. 

However, as there are only two files to add to the repository at this time I am going to create them manually within the GitHub website. 

Navigate to the new repository you have just created. 

Click on **Add File**

![GitHub add file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q221j20c442z36s5028b.png)

Then select **Create New File**

![Create a new GitHub file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9m2irxcak8c5luhm8evc.png)

This will open an editor. 

Within the name file type **Storage-Template/main.bicep** - this will create a folder called Storage-Template and then the file main.bicep. 


![Name a file on GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sj6fflwmuzxbmdlactdk.png)

Within the file editor copy the following Bicep template file. 

```bicep
@description('Storage Account type')
@allowed([
  'Premium_LRS'
  'Premium_ZRS'
  'Standard_GRS'
  'Standard_GZRS'
  'Standard_LRS'
  'Standard_RAGRS'
  'Standard_RAGZRS'
  'Standard_ZRS'
])
param storageAccountType string = 'Standard_LRS'

@description('Location for all resources.')
param location string = resourceGroup().location

@description('The name of the Storage Account')
param storageAccountName string = 'store${uniqueString(resourceGroup().id)}'

@description('The name of the Owner of this resource for the Azure Tag declaration')
param ownerTag string = 'Sarah'

@description('The purpose of this resource for the Azure Tag declaration')
param purposeTag string = 'Demo'

resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = {
  name: storageAccountName
  location: location
  tags: {
    Owner: ownerTag
    Purpose: purposeTag
  }
  sku: {
    name: storageAccountType
  }
  kind: 'StorageV2'
  properties: {}
}

output storageAccountName string = storageAccountName
output storageAccountId string = storage.id
```

At the bottom of the editor there is a box asking for information about the new file you are about to commit, type in why you are adding this new file and then select **Commit New File**.

![GitHub commit a new file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53rs26zuicwnhxzhoki3.png)

_Best practice would be to create a new branch then create a pull request to move the file into the working branch, but as this is an empty repository we are just going to commit straight into the working branch._

Now the Bicep template is stored within our repository, the next file that is needed inside the repository is the [Bicep lint file](https://www.techielass.com/what-is-linting/). 

The linter file checks for any syntax errors and [best practice violations within our Bicep files](https://learn.microsoft.com/azure/azure-resource-manager/bicep/linter).   The linter file can help to enforce code standards that your organisation has specified. 

You can customise the linter file to match your needs. 

To add our linter file to the repository navigate to inside the **Storage-Template **folder. 

Click on **Add File**

![GitHub add file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q221j20c442z36s5028b.png)

Then select** Create New File**

![Create a new GitHub file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9m2irxcak8c5luhm8evc.png)

This will open an editor. 

Within the name file type **bicepconfig.json** 

![GitHub file name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/edspinsx7s7mu5tnapfj.png)

Within the file editor copy the following linter file configuration. 

```bicep
{
  "analyzers": {
    "core": {
      "verbose": false,
      "enabled": true,
      "rules": {
        "no-hardcoded-env-urls": {
          "level": "error",
          "disallowedhosts": [
            "management.core.windows.net",
            "gallery.azure.com",
            "management.core.windows.net",
            "management.azure.com",
            "database.windows.net",
            "core.windows.net",
            "login.microsoftonline.com",
            "graph.windows.net",
            "trafficmanager.net",
            "vault.azure.net",
            "datalake.azure.net",
            "azuredatalakestore.net",
            "azuredatalakeanalytics.net",
            "vault.azure.net",
            "api.loganalytics.io",
            "api.loganalytics.iov1",
            "asazure.windows.net",
            "region.asazure.windows.net",
            "api.loganalytics.iov1",
            "api.loganalytics.io",
            "asazure.windows.net",
            "region.asazure.windows.net",
            "batch.core.windows.net"
          ]
        },
        "no-unused-params": {
          "level": "error"
        },
        "no-unused-vars": {
          "level": "error"
        },
        "prefer-interpolation":{
          "level": "error"
        },
        "secure-parameter-default":{
          "level": "error"
        },
        "simplify-interpolation":{
          "level": "error"
        }
      }
    }
  }
}
```

At the bottom of the editor there is a box asking for information about the new file you are about to commit, type in why you are adding this new file and then select **Commit New File**.

![GitHub commit new file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yyk06xhqj440hks3er5v.png)

The repository now contains the Bicep template file and the linter file. 

## Create credentials within Azure to programmatically deploy resources

The next step is to create an [Azure Service Principal](https://docs.microsoft.com/azure/active-directory/develop/app-objects-and-service-principals?WT.mc_id=AZ-MVP-5004737) that can be used to connect to the Azure Subscription and deploy or manage resources inside that subscription. 

You can create the Service Principal in a couple of ways, using the [Azure Portal](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal?WT.mc_id=AZ-MVP-5004737), [PowerShell](https://www.techielass.com/create-azure-credentials-for-use-in-github-actions/) or the CLI.  Below is the CLI method. 

Open the [Azure Cloud Shell](https://shell.azure.com) and set it to use bash.  Enter the following command: 

```bash
subscriptionId=$(az account show --query id --output tsv)
az ad sp create-for-rbac -n "GitHubActionsCrednetial" --role "Owner" --scopes /subscriptions/$subscriptionId
```

The output will be similar to this: 

```bash
{
"appId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"displayName": "GitHubActionsCredential",
"password": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"tenant": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
```

Take a copy of this output as it’s needed in the next step. 

## Use GitHub secrets to store Azure credentials

In order for GitHub to use the Azure Service Principal credentials it needs to be stored inside a GitHub secret. 

Within the repository where your workflow is click on **Settings** > **Secrets** > **Actions** and then click on new repository secret.

Give the new secret a name, I usually go with "Azure_Credentials" and then take the output from the CLI script and paste it into the value section.

## Create GitHub Action to deploy Azure Bicep template

Now that we have our Bicep files stored within GitHub and we have the necessary credentials created and stored within GitHub it’s time to create the GitHub action to deploy our resources. 

Within your GitHub repository head over to Actions and then select New Workflow. 

There are templates that you can use but we are going to start from scratch, so select “set up a workflow yourself”.

The first thing that we want to do is give the workflow a name and also instruct why and when this workflow should trigger. 

```YML
# This workflow 

name: Bicep-Template_Build
on:
  workflow_dispatch:
  push:
    branches:
    - main
  pull_request:
    branches:
    - main
 ```

This workflow will trigger on three occasions:

* Manually
* When code is pushed into the main branch of the repository
* When a pull request is pushed into the main branch of the repository

The next section of the workflow is to define variables.  This will allow us to use the template in other scenarios. 

```yml
env:
  resourceGroupName: TemplateDeploymentRG
  resourceGroupLocation: uksouth
  bicepfilePath: ./Storage-Template/main.bicep
```

Now it’s time to start defining the job and steps for this workflow.  This workflow will have two jobs and multiple steps.   The first job is to build or validate our template. 

```yml
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v3
            
      # Builds the Bicep template
      - name: bicep-build-output
        uses: Azure/bicep-build-action@v1.0.1
        with:
            bicepFilePath: ${{  env.bicepfilePath }}
            outputFilePath: ./main.json
      
      - name: Upload a Build Artifact
        uses: actions/upload-artifact@v3.1.1
        with:
          path: ./main.json
          
          
 ```       

The build part of the workflow does several things.  The first is to take a copy of the code within the repository and make it available for the [GitHub worker](https://www.techielass.com/github-runners/) to use. 

The next step is to build the Bicep file.  This validates the file we have created against the linter file and then creates an Azure ARM template file. 

Within the workflow the ARM template file that is built from the Bicep is uploaded as an artifact and is available to view or use either later in the workflow or after the workflow has finished.  This is an entirely optional stage, it’s one I build into the process just for more complex deployments and potential troubleshooting. 

Our second job is to deploy the template and create our Azure Storage account inside Azure. 

```yml
  # Deploys the template to Azure
  deploy: 
   runs-on: ubuntu-latest
   needs: [build]

   steps:
    - uses: actions/checkout@v3

    - name: Azure Login
      uses: Azure/login@v1.4.3
      with:
           creds: ${{ secrets.AZURE_CREDENTIALS }}
    
    - name: Az CLI Create Resource Group
      uses: Azure/CLI@v1
      with:
        inlineScript: |
          az group create \
          --name ${{ env.resourceGroupName }} \
          --location ${{ env.resourceGroupLocation }}
    - name: Deploy bicep to Azure
      uses: Azure/cli@v1
      with:
        inlineScript: |
          az deployment group create \
          --template-file ${{  env.bicepfilePath }} \
          --resource-group ${{ env.resourceGroupName }}
```

The first step in this job is logging into Azure, this is calling the credentials we created earlier and using them to authenticate.  
The next step is to create an Azure resource group, this is using one of the variables that you created right at the start of the template.   If your resource group already exists, then this step will just continue on to the next.
 
This third step is now deploying the Bicep template. 

After a few minutes of running within the Azure Portal the storage account will be available and the GitHub Action will report as having finished. 

![Completed GitHub workflow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cff0gquupsjqq7fzjuj1.png)

## Conclusion

Hopefully this article has guided you through the process of deploying your first Azure Bicep template using GitHub Actions.  I look forward to seeing what else you automate!