GitHub Actions — Azure Container Apps build/deploy

Richard Basson
8 min readFeb 7, 2023

This is a tutorial on how to deploy Azure Container Apps using GitHub Actions. I will provide examples and step-by-step instructions on how to build custom Github Actions workflows for deploying container apps to Azure. This will show you how to create your own actions for container deployments based on your needs.

Some assumptions:

  • You have an Azure account.
  • You have a GitHub account.
  • You have the Azure CLI.
  • You have access to create resources and service principles in the Azure subscription you’re going to deploy to.
  • You have an image repository where you can store container images.
  • You have code to deploy.

Here is an example of an action workflow that I use to deploy my container apps:

# The name of the deployment
name: Trigger auto deployment for containerapps

# When this action will be executed
on:
# Automatically trigger it when detected changes in repo
push:
# Branched to look out for changes on
branches:
[ main ]

# Allow manually trigger of the jobs
workflow_dispatch:

jobs:
# Job run image.
build:
runs-on: ubuntu-latest

# Steps that need to happen in this job run.
steps:
# Check out the code
- name: Check out code
uses: actions/checkout@v2

# Log in to Azure CLI
- name: Log in to Azure
uses: azure/login@v1
with:
# Azure CLI credentials
creds: ${{ secrets.AZURE_CREDENTIALS }}

# Build and deploy the container app
- name: Build and deploy Container App
uses: azure/container-apps-deploy-action@v0
with:
appSourcePath: ${{ github.workspace }}
acrName: <Container-Registery-Name>
acrUsername: ${{ secrets.REGISTRY_USERNAME }}
acrPassword: ${{ secrets.REGISTRY_PASSWORD }}
containerAppName: <Container-App-Name>
containerAppEnvironment: <Container-App-Environment-Name>
resourceGroup: <Container-App-Resource-Group>
imageToBuild: <Container-Registry>/<Image-Name>:<Tag e.g. ${{ github.sha }}>
dockerfilePath: /Path/to/Dockerfile

This is quite a lot so let’s unpack it.

GitHub Actions File structure

Workflow name

name: Trigger auto deployment for containerapps

This is the name you want to give your worflow.

Workflow triggers

# When this action will be executed
on:
# Automatically trigger it when detected changes in repo
push:
# Branched to look out for changes on
branches:
[ main ]

# Allow manually trigger of the jobs
workflow_dispatch:

The workflow trigger is how you choose to trigger your workflow. This example creates a trigger based on pushing code to the main branch. You can set this based on the branch you trigger deploys from. It’s also possible to use wildcards in the branch name, for example:

branches:
- main
- 'releases/**'

The workflow_dispatch allows you to manually trigger the workflow in GitHub actions. I found this particularly useful when I’ve made changes to the infrastructure and I need to trigger a deployment again.

You can read more about workflow triggers here:

Jobs

This is a grouping of steps that need to run. You can have one or multiple jobs.

The example above only has 1 job called “build” which builds and deploys the project and runs on the ubuntu image.

These jobs run in parallel by default but you can create a dependency on other jobs by using the needs: keyword. You can also conditionally run jobs.

jobs:
build:
review:
name: Deploy/Review
needs: build

staging:
name: Deploy/Staging
needs: build

production:
name: Deploy/Production
if: ${{ always() }}
needs: [staging, review]

In the above image the Deploy/Staging, Deploy/Review and Deploy/Production blocks are jobs.

You can read more about jobs here:

Steps

These are tasks that need to run as part of the job. In this case, we have 3 steps that need to run as part of the job.

  1. Check out code
  2. Log in to Azure
  3. Build and deploy Container App

These can consist of actions from the marketplace or custom actions, but in this case, we are using standard marketplace actions.

The “Check out code” step checks out the code from the repo you’re running this action from into the container that the build/deploy is running in.

The“ Log in to Azure” step logs you into the azure CLI and gives the deployment an access token to deploy the container app.

The “Build and deploy Container App” builds and deploys the container app in the coming section we’ll get into more detail on what that does.

You can check out the GitHub actions marketplace here:

Credentials and secret setup

We need to first of all, create a service principal in Azure. The best way would be to use this Azure CLI.

Make sure you’re logged into the subscription you want to deploy the container app.

You then run the following command to create the service principle:

az ad sp create-for-rbac --name "myApp" --role contributor \
--scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group} \
--sdk-auth

Replace the {subscription-id} and {resource-group} with the subscription Id and the resource group, you want to deploy to respectively. You should also replace myApp with the name you want to give the service principle.

This command will create an RBAC (role-based access control) service principle which then has contributor access to the subscription and resource group.

You will then get an output that looks like the one below. You should be careful not to share this as it is sensitive information. You will need to keep this as you will use it soon.

{
"clientId": "<GUID>",
"clientSecret": "<STRING>",
"subscriptionId": "<GUID>",
"tenantId": "<GUID>",
"resourceManagerEndpointUrl": "<URL>"
(...)
}

Now for creating the Secret in GitHub on the repo so it can be used for the action.

  1. Click on the repo you want to deploy.
  2. Navigate to Settings
  3. Then under Security expand Secrets and variables
  4. Click “Actions”
  5. While on the secrets tab click on “New repository secret”
  6. Create a secret called “AZURE_CREDENTIALS
  7. Set the secret value to the output you got above from creating the service principle.

This part is only required if you want to use a container registry.

While you are creating secrets you will need 2 more. The “REGISTRY_USERNAME” and “REGISTRY_PASSWORD”. This can be obtained from your Azure container registry.

REGISTRY_USERNAME is the username of the registry user.

REGISTRY_PASSWORD is the password of the registry user.

The bare minimum

Your first half of the workflow page should look like this:

# The name of the deployment
name: Trigger auto deployment for containerapps

# When this action will be executed
on:
# Automatically trigger it when detected changes in repo
push:
# Branched to look out for changes on
branches:
[ main ]

# Allow manually trigger of the jobs
workflow_dispatch:

jobs:
# Job run image.
build:
runs-on: ubuntu-latest

The following steps you add will be based on different scenarios. So let's start with the first three.

  1. ) You are deploying an existing container image with public registry access.
steps:

- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Build and deploy Container App
uses: azure/container-apps-deploy-action@v0
with:
imageToDeploy: mcr.microsoft.com/azuredocs/containerapps-helloworld:latest

2.) You are building and deploying an image.

steps:

- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Build and deploy Container App
uses: azure/container-apps-deploy-action@v0
with:
appSourcePath: ${{ github.workspace }}
acrName: mytestacr

3.) You don’t have a dockerfile or built image but you have the runtime stack.

steps:

- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Build and deploy Container App
uses: azure/container-apps-deploy-action@v0
with:
appSourcePath: ${{ github.workspace }}
acrName: mytestacr
runtimeStack: 'dotnetcore:7.0'

With these scenarios, if you don’t have the container app, container environment, or resource group created either manually or by a previous run, it will create these resources for you.

The resources it creates will be named as follows:

  • Container App: github-action-container-app-<github-run-id>-<github-run-attempt>
  • Resource Group: <container-app-name>-rg
  • Container App Environment: <container-app-name>-env

I have found this to not be ideal in most cases, so let’s see what we need to give it to not create these.

Additional arguments

So to prevent the action from creating these resources, you will need to give it the name of existing resources or give it names of resources you want to initially create and subsequently re-use.

You can then add these arguments to your “Build and deploy Container App” step.

containerAppName: <Container-App-Name>
containerAppEnvironment: <Container-App-Environment-Name>
resourceGroup: <Container-App-Resource-Group>

If you have a custom Docker image you want to build and deploy, you need to use these arguments to build the image and deploy it. This is if you have an existing Dockerfile

imageToBuild: <Container-Registry>/<Image-Name>:<Tag e.g. ${{ github.sha }}>
dockerfilePath: /Path/to/Dockerfile

You will also need to log into your container registry to push the image, so you will need the below arguments as well. You will also need these if you’re pulling an image from a private image repository.

acrName: <Container-Registery-Name>
acrUsername: ${{ secrets.REGISTRY_USERNAME }}
acrPassword: ${{ secrets.REGISTRY_PASSWORD }}

Some of the other ones I have not used but can be useful are:

  • environmentVariables A list of environment variable(s) for the container. Space-separated values in 'key=value' format. Empty string to clear existing values. Prefix value with 'secretref:' to reference a secret.
  • targetPort The target port that the Container App will listen on. If not provided, this value will be "80" for Python applications and "8080" for all other supported platforms.

Well done! You’ve made it all the way to the end.

You’ve now unlocked the skill to deploy Azure container apps using GitHub action

If you like this post consider following me here or on Twitter @bassonrichard

If you want to do some more reading here is the documentation for the GitHub Action.

--

--