In the past, I wrote about using Azure DevOps to deploy an AKS cluster and bootstrap it with Flux v2, a GitOps solution. In an older post, I also described bootstrapping the cluster with Helm deployments from the pipeline.
In this post, we will take a look at doing the above with GitHub Actions. Along the way, we will look at a VS Code extension for GitHub Actions, manually triggering a workflow from VS Code and GitHub and manifest deployment to AKS.
Let’s dive is, shall we?
Getting ready
What do you need to follow along:
- Some experience with Azure and AKS
- A GitHub account: workflows, ARM templates and YAML used in this post are in my azure-deploy repo
- Visual Studio Code with at the GitHub Actions extension
Deploying AKS
Although you can deploy Azure Kubernetes Service (AKS) in many ways (manual, CLI, ARM, Terraform, …), we will use ARM and the azure/arm-deploy@v1 action in a workflow we can trigger manually. The workflow (without the Flux bootstrap section) is shown below:
name: deploy
on:
repository_dispatch:
types: [deploy]
workflow_dispatch:
env:
CLUSTER_NAME: CLUSTERNAME
RESOURCE_GROUP: RESOURCEGROUP
KEYVAULT: KVNAME
GITHUB_OWNER: GUTHUBUSER
REPO: FLUXREPO
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- uses: azure/arm-deploy@v1
with:
subscriptionId: ${{ secrets.SUBSCRIPTION_ID }}
resourceGroupName: rg-gitops-demo
template: ./aks/deploy.json
parameters: ./aks/deployparams.json
- uses: azure/setup-kubectl@v1
with:
version: 'v1.18.8'
- uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: ${{ env.CLUSTER_NAME }}
resource-group: ${{ env.RESOURCE_GROUP }}
To create this workflow, add a .yml file (e.g. deploy.yml) to the .github/workflows folder of the repository. You can add this directly from the GitHub website or use VS Code to create the file and push it to GitHub.
The above workflow uses several of the Azure GitHub Actions, starting with the login. The azure/login@v1 action requires a GitHub secret that I called AZURE_CREDENTIALS. You can set secrets in your repository settings. If you use an organization, you can make it an organization secret.

If you have the GitHub Actions VS Code extension, you can also set them from there:

If you use the gh command line, you can use the command below from the local repository folder:
gh secret set SECRETNAME --body SECRETVALUE
The VS Code integration and the gh command line make it easy to work with secrets from your local system rather than having to go to the GitHub website.
The secret should contain the full JSON response of the following Azure CLI command:
az ad sp create-for-rbac --name "sp-name" --sdk-auth --role ROLE \ --scopes /subscriptions/SUBID
The above command creates a service principal and gives it a role at the subscription level. That role could be contributor, reader, or other roles. In this case, contributor will do the trick. Of course, you can decide to limit the scope to a lower level such as a resource group.
After a successful login, we can use an ARM template to deploy AKS with the azure/arm-deploy@v1 action:
- uses: azure/arm-deploy@v1
with:
subscriptionId: ${{ secrets.SUBSCRIPTION_ID }}
resourceGroupName: rg-gitops-demo
template: ./aks/deploy.json
parameters: ./aks/deployparams.json
The action’s parameters are self-explanatory. For an example of an ARM template and parameters to deploy AKS, check out this example. I put my template in the aks folder of the GitHub repository. Of course, you can deploy anything you want with this action. AKS is merely an example.
When the cluster is deployed, we can download a specific version of kubectl to the GitHub runner that executes the workflow. For instance:
- uses: azure/setup-kubectl@v1
with:
version: 'v1.18.8'
Note that the Ubuntu GitHub runner (we use ubuntu-latest here) already contains kubectl version 1.19 at the time of writing. The azure/setup-kubectl@v1 is useful if you want to use a specific version. In this specific case, the azure/setup-kubectl@v1 action is not really required.
Now we can obtain credentials to our AKS cluster with the azure/aks-set-context@v1 task. We can use the same credentials secret, in combination with the cluster name and resource group set as a workflow environment variable:
- uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: ${{ env.CLUSTER_NAME }}
resource-group: ${{ env.RESOURCE_GROUP }}
In this case, the AKS API server has a public endpoint. When you use a private endpoint, run the GitHub workflow on a self-hosted runner with network access to the private API server.
Bootstrapping with Flux v2
To bootstrap the cluster with tools like nginx and cert-manager, Flux v2 is used. The commands used in the original Azure DevOps pipeline can be reused:
- name: Flux bootstrap
run: |
export GITHUB_TOKEN=${{ secrets.GH_TOKEN }}
msi="$(az aks show -n ${{ env.CLUSTER_NAME }} -g ${{ env.RESOURCE_GROUP }} --query identityProfile.kubeletidentity.objectId -o tsv)"
az keyvault set-policy --name ${{ env.KEYVAULT }} --object-id $msi --secret-permissions get
curl -s https://toolkit.fluxcd.io/install.sh | bash
flux bootstrap github --owner=${{ env.GITHUB_OWNER }} --repository=${{ env.REPO }} --branch=main --path=demo-cluster --personal
For an explanation of these commands, check this post.
Running the workflow manually
As noted earlier, we want to be able to run the workflow from the GitHub Actions extension in VS Code and the GitHub website instead of pushes or pull requests. The following triggers make this happen:
on: repository_dispatch: types: [deploy] workflow_dispatch:
The VS Code extension requires the repository_dispatch trigger. Because I am using multiple workflows in the same repo with this trigger, I use a unique event type per workflow. In this case, the type is deploy. To run the workflow, just right click on the workflow in VS Code:

You will be asked for the event to trigger and then the event type:

The workflow will now be run. Progress can be tracked from VS Code:

Update Jan 7th 2021: after writing this post, the GitHub Action extension was updated to also support workflow_dispatch which means you can use workflow_dispatch to trigger the workflow from both VS Code and the GitHub website ⬇⬇⬇
To run the workflow from the GitHub website, workflow_dispatch is used. On GitHub, you can then run the workflow from the web UI:

Note that you can specify input parameters to workflow_dispatch. See this doc for more info.
Deploying manifests
As shown above, deploying AKS from a GitHub workflow is rather straightforward. The creation of the ARM template takes more effort. Deploying a workload from manifests is easy to do as well. In the repo, I created a second workflow called app.yml with the following content:
name: deployapp
on:
repository_dispatch:
types: [deployapp]
workflow_dispatch:
env:
CLUSTER_NAME: clu-gitops
RESOURCE_GROUP: rg-gitops-demo
IMAGE_TAG: 0.0.2
jobs:
deployapp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: ${{ env.CLUSTER_NAME }}
resource-group: ${{ env.RESOURCE_GROUP }}
- uses: azure/container-scan@v0
with:
image-name: ghcr.io/gbaeke/go-template:${{ env.IMAGE_TAG }}
run-quality-checks: true
- uses: azure/k8s-bake@v1
with:
renderEngine: kustomize
kustomizationPath: ./deploy/
id: bake
- uses: azure/k8s-deploy@v1
with:
namespace: go-template
manifests: ${{ steps.bake.outputs.manifestsBundle }}
images: |
ghcr.io/gbaeke/go-template:${{ env.IMAGE_TAG }}
In the above workflow, the following actions are used:
- actions/checkout@v2: checkout the code on the GitHub runner
- azure/aks-set-context@v1: obtain credentials to AKS
- azure/container-scan@v0: scan the container image we want to deploy; see https://github.com/Azure/container-scan for the types of scan
- azure/k8s-bake@v1: create one manifest file using kustomize; note that the action uses kubectl kustomize instead of the standalone kustomize executable; the action should refer to a folder that contains a kustomization.yaml file; see this link for an example
- azure/k8s-deploy@v1: deploy the baked manifest (which is an output from the task with id=bake) to the go-template namespace on the cluster; replace the image to deploy with the image specified in the images list (the tag can be controlled with the workflow environment variable IMAGE_TAG)
Note that the azure/k8s-deploy@v1 task supports canary and blue/green deployments using several techniques for traffic splitting (Kubernetes, Ingress, SMI). In this case, a regular Kubernetes deployment is used, equivalent to kubectl apply -f templatefile.yaml.
Conclusion
I only touched upon a few of the Azure GitHub Actions such as azure/login@v1 and azure/k8s-deploy@v1. There are many more actions available that allow you to deploy to Azure Container Instances, Azure Web App and more. We have also looked at running the workflows from VS Code and the GitHub website, which is easy to do with the repository_dispatch and workflow_dispatch triggers.