Deploy ARM Templates to Azure Using GitHub Actions

The Plan

This post goes over the process used to automate the deploy of this blog to Azure using GitHub actions and Azure ARM templates. The items that will be covered are:

  • GitHub project setup
  • GitHub Branching Strategy
  • Create ARM Templates for Deployment
  • Creating Deployment Action in GitHub
  • Putting it all Together

GitHub Project Setup

There are a few things we need to configure in the GitHub project to allow authentication into Azure.

Create Azure AD Application Registration

For authentication into Azure we will be using a Service Principal via an Azure AD App Registration. This allows us to have a specific identity and associated permissions assigned to the GitHub Project. To create an Azure AD App Registration:

  • Go to the Subscriptions service and take note of your target Subscription ID
  • Browse to App registrations in the Azure Active Directory service and select New registration
  • Set the name to something like GitHubActions
  • Leave the Supported account types as the default Single tenant option
  • We can ignore the Redirect URI
  • Hit Register to create the App Registration

Once you have registered the application there will be a few values to take note of from the App Registration Overview:

  • Application (client) ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  • Directory (tenant) ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

You will then need to go to Certificates & secrets section of your app registration and create a new client secret. Take note of this value also.

Configure GitHub Secrets

Once you have the required values the next step is to add them to the secret store in your GitHub project. You will need to format your credentials as a block of JSON:

{
  "clientId": "<GUID>",
  "clientSecret": "<GUID>",
  "subscriptionId": "<GUID>",
  "tenantId": "<GUID>"
}

This block of JSON will be added as a single secret: AZURE_CREDENTIALS

You will also need to add the Subscription ID as a separate credential as well

Assign Permissions to Azure Ad App Registration

Lastly you will need to assign appropriate permissions to the Azure App Registration. To do this from the Azure portal:

  • Navigate to the Subscription you will be deploying into
  • Select IAM -> Role Assignments
  • Add -> Add Role Assignment

Note that you will need to search specifically for the name of your App Registration as they are not shown in the list by default.

In this instance I have given Contributor access to the subscription. In the case you were manually creating a resource group for the action to deploy into, you would assign IAM roles at that scope (which is recommended from a least privilege perspective). I am granting access at the subscription scope as I want GitHub Actions to have permission to create Resource Groups.

GitHub Branching Strategy

In this case the branching strategy will be kept straight forward as there is no DEV or UAT environment, only a Prod environment… Don’t @ me. Whatever is in the master branch reflects the state in production, in this case master reflects the live blogs state. To make an update in production:

  • Pull master locally
  • Create a new local branch
  • Push the local branch
  • Create a pull request to master
  • Merge the pull request to master -> This kicks off the deploy action

Well, thats probably what I should be doing, but since it’s just my blog I am Yeeting directly to master.

Create ARM Templates for Deployment

For this deployment we will need 2 ARM templates, one to deploy the resource group and one for the blog. This is due to the scope that ARM templates can be deployed at. Specifically a template can be deployed to either create Resource Groups or to create resources within a Resource Group. Typically I have seen the Resource Group being created as a manual step, but I wanted to do the entire deployment from GitHub Actions. These templates are too large put inline on a blog post, they can be found in my GitHub repo:

ARM_Templates/deployResourceGroup.json

ARM_Templates/deployBlog.json

I have also defined parameter files for each of the ARM templates to keep all the parameters neatly organised.

Creating Deployment Action in GitHub

The Github action needs to do a few things, I have added comments inline to the actions yaml below. The original .yaml without the inline comments can be found here

name: Deploy Blog

on:
  push:
    branches:
      - master

jobs:
  Deploy:
    name: Deploy
    runs-on: ubuntu-latest
    steps:

    - name: Checkout_Repo
      uses: actions/checkout@v2

    # Login to the Azure CLI using the "AZURE_CREDENTIALS" secret we created earlier
    - uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}

    # This deploys the Resource Group. Its important to note this is using the "azure/CLI@v1" 
    # action. This is because the "azure/arm-deploy@v1" action only supports ARM deployments
    # within the scope of a Resource Group. But as I mentioned earlier I want to be able to
    # deploy the Resource Group and Blog in one go. The Azure CLI is able to deploy the ARM
    # template at a subscription level allowing for Resource Groups to be created.
    - name: Deploy Resource Group
      uses: azure/CLI@v1
      with:
        azcliversion: 2.0.72
        inlineScript: |
          az deployment create --name "blog-kewley-resource-group" --template-file "ARM_Templates/deployResourceGroup.json" --parameters "ARM_Templates/resourceGroupParameters.json" --location "australiaeast"

    # Finally we deploy the actual blog, this time we can use the "azure/arm-deploy@v1" action
    # since our target for the deployment is the Resource Group created in the previous step.
    - name: Deploy_Blog
      uses: azure/arm-deploy@v1
      with:
        subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        resourceGroupName: blog-kewley
        template: ARM_Templates/deployBlog.json
        parameters: ARM_Templates/blogParameters.json

    # Since I an deploying a static website I want to update the properties of the storage container
    - name: Update Blob Storage Properties
      uses: azure/CLI@v1
      with:
        inlineScript: |
          az storage blob service-properties update --account-name nkew4630blog --static-website --index-document index.html --auth-mode login

  # and finally sync the site
  SyncSite:
    name: Sync Site Content
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: kheiakiyama/install-azcopy-action@v1.0.1
      with:
        version: 'v10'
        creds: ${{ secrets.AZURE_CREDENTIALS }}
    - run: azcopy_v10 sync "./blogContent/_site" "$BLOB_CONTAINER_URL" --recursive=true 
      env: 
        BLOB_CONTAINER_URL: 'https://nkew4630blog.blob.core.windows.net/$web'    

Putting it all Together

That’s it, with this all in place updating the blog is as easy as writing new content and pushing it to master!

The code is on my GitHub