The past few months, I’ve been working with GitHub Actions as the CI/CD platform for Azure hosted applications. Whilst that wasn’t using Azure Stack Hub, I wanted to see how I could use Actions within my environment, as I really like the low entry barrier, and GitHub is fairly ubiquitous.
From a high level, in order to achieve the integration, we need to use an Action Runner, which in simple terms is an agent running on a VM (or container) that polls for workflow action events and executes those actions on the Action runner system where the agent resides. GitHub can provide containerized runners out-of-the-box, and that’s great if you’re using a public cloud, but in the vast majority of cases, Azure Stack Hub is in a corporate network, so we can use a self-hosted runner for these scenarios, and that’s what I’ll be using here.
There are already some tutorials and videos on how you can integrate into your Azure Stack Hub environment, but I wanted to go a little deeper, from showing and explaining how to get your environment setup, to automating the creation of a self-hosted runner in your Azure Stack Hub tenant.
First, here are some links to some great content:
https://channel9.msdn.com/Shows/DevOps-Lab/GitHub-Actions-on-Azure-Stack-Hub
and the official docs:
As ever, the documentation assumes you are familiar with the platform. I won’t, so will go through step-by-step what you need to do and hopefully be successful first time!
Pre-requisites
Before you get started, here’s what you’ll need up-front:
A GitHub account and a repository. I would highly recommend a private repo, as you will be deploying a self-hosted runner which is linked to this repo - this will be able to run code in your environment. GitHub make this recommendation here.
An Azure Stack Hub environment with a Tenant Subscription that you can access (assumption here is we’re using Azure AD as the identity provider)
(Optional) A service principal with contributor rights to the Azure Stack Hub tenant subscription. If you don’t have this, I detail how this can be done later..
A GitHub Personal Access Token (PAT)
Credentials to connect to Azure Stack Hub
First thing we need is a service principal that has rights to the tenant subscription (I recommend contributor role). We will use these credentials to create a secret within our GitHub repo that we will use to connect to the subscription.
If you don’t already have an SP, following the official documentation helps us create one.
All my examples are being run from Linux and Azure CLI (Ubuntu WSL to be specific :) )
First, register your Azure Stack Hub tenant environment (if not already done so)
az cloud register \ -n "AzureStackHubTenant" \ --endpoint-resource-manager "https://management.<region>.<FQDN>" \ --suffix-storage-endpoint ".<region>.<FQDN>" \ --suffix-keyvault-dns ".vault.<region>.<FQDN>" \ --endpoint-active-directory-graph-resource-id "https://graph.windows.net/" \ --profile 2019-03-01-hybrid \
We need to connect to our Azure Stack Hub environment so that when the Service Principal is created, we can assign it to a scope.
Once the environment is defined, we need to make sure this is active.
az cloud set -n AzureStackHubTenant
Run this command to confirm that it is set correctly:
az cloud list -o table
Next, let’s connect to our subscription hosted on Azure Stack Hub.
az login
If there’s more than one subscription, you might need to specify which subscription you want to connect to.
az account set --subscription <subName>
We can see above that I have set the active subscription to ‘DannyTestSub’.
Next, we want to create our service principal. To make things easier, let’s have the CLI do the work in assigning the scope to the user subscription:
#Retrieve the subscription ID SUBID=$(az account show --query id -o tsv) az ad sp create-for-rbac --name "ASH-github-runner" --role contributor \ --scopes /subscriptions/$SUBID \ --sdk-auth
Running that should produce something like the following in my environment:
It is important that we copy the JSON output as-is , we need this exact format to create our GitHub secret. Theoretically, if you already have a clientID and secret, you could construct your own JSON formatted credential like this:
{ "clientId": "<your_ClientID>", "clientSecret": "<your_Client_secret>", "subscriptionId": "<Azure_Stack_Hub_Tenant SubscriptionID>", "tenantId": "<Your_Azure_AD_Tenant_Id>", "activeDirectoryEndpointUrl": "https://login.microsoftonline.com/", "resourceManagerEndpointUrl": "https://management.<REGION>.<FQDN>", "activeDirectoryGraphResourceId": "https://graph.windows.net/", "sqlManagementEndpointUrl": null, "galleryEndpointUrl": "https://providers.<REGION>.local:30016/", "managementEndpointUrl": "https://management.<REGION>.<FQDN>" }
Now we have the credentials, we need to set up a secret within GitHub.
From the GitHub portal, connect to your private repo that you will use for Azure Stack Hub automation.
So we have our credentials and have setup an Actions runner secret, now we need an Actions Runner to run our workflows against within our Azure Stack Hub environment.
If you already have a Windows Server or Linux host running in your Azure Stack Hub tenant subscription, you can follow the manual steps, per the guidance given under the Settings/ Actions / Runner config page:
You can select the OS type of the system you have running and follow the commands.
Note: The ./config.(cmd|sh) command uses a token which has a short lifetime, so be aware if you use this method and are expecting to use it for automating self-hosted runner deployments!
The above method works and is OK if you want to quickly test capabilities. However, I wanted the ability to automate the runner provisioning process, from the VM to the installation of the runner agent.
I did this by creating an ARM template that deploys an Ubuntu VM and runs a Bash script that installs necessary tools (e.g. Azure CLI, Docker, Kubectl, Helm, etc.) and most importantly, deploys the agent and dynamically retrieves a token from GitHub to add our runner. One crucial parameter we need is a GitHub Personal Access Token (PAT). We need this to authenticate to the GitHub Actions API to generate the actions token.
To create the PAT, highlight your user account from the top right of the GitHub portal:
Now we have the PAT, we can go ahead and deploy the VM using the ARM template I created.
Go ahead and get it from
https://github.com/dmc-tech/AzsHubTools/blob/main/ghRunner/template.json
There’s nothing fancy; it deploys a VNET, NIC, Public IP, VM (it uses Ubuntu 18.04 - make sure you have it available via the Azure Stack Hub Marketplace!) and then deploys a Custom script to install a bunch of tools and the runner agent. The only parameters you will need to provide are:
Parameter | Description |
---|---|
gitHubPat | Personal Access Token used to access GitHub |
gitHubRepo | GitHub Repo to create the Self Hosted Runner |
gitHubOwner | GitHub Owner or Organisation where the repo is located for the Runner |
adminPublicKey | Public SSH key used to login to the VM |
If you’re not sure how the Owner and Repo are derived, it’s simple:
To generate the adminPublicKey on Windows systems, I prefer to use MobaXterm. See the end of this post on how to generate the Private/ Public key
Deploy using the ARM template within your tenant subscription.
When deployed, it takes me about 10 minutes in my environment to complete.
We can see that the agent has successfully deployed by checking the Actions / Runners settings within our GitHub repo:
Now we can go ahead and test a workflow.
Within your repo, if it doesn’t already exist, create the following directory structure:
/.github/workflows
This is where the workflow yaml files are stored that our actions will use.
For a simple test, go ahead and copy the following into this folder in your repo (and commit it to the main branch!):
https://github.com/dmc-tech/AzsHubTools/blob/main/.github/workflows/testAzureStackHub.yml
The workflow is manually triggered ( workflow_dispatch ), and prompts for a parameter ( the name of the subscription you want to run the action against)
on:
workflow_dispatch:
inputs:
subscription:
description: 'Azure Stack Hub User subscription'
required: true
default: 'TenantSubscription'
name: Test GitHub Runner in an Azure Stack Hub environment
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
jobs:
azurestackhub-test:
runs-on: self-hosted
steps:
- uses: actions/checkout@main
- name: Login to AzureStackHub with CLI
uses: azure/login@releases/v1
with:
creds: ${ }
environment: 'AzureStack'
enable-AzPSSession: false
- name: Run Azure CLI Script Against AzureStackHub
run: |
hostname
subId=$(az account show --subscription ${ } --query id -o tsv)
az account set --subscription ${ }
az group list --output table
You can see that the workflow refers to the secret we defined earlier: secrets.AZURESTACKHUB_CREDENTIALS
The workflow configures the Azure Stack Hub tenant environment on the runner VM (using the values from the JSON stored in the secret), connects using the service principal and secret and then runs the Azure CLI commands to list the resource groups in the specified subscription.
To run the workflow, head over to the GitHub site:
With that, we’ve shown how we can automate the deployment of a self-hosted runner on Azure Stack Hub and demonstrated how to run a workflow.
I really like GitHub Actions and there’s scope for some powerful automation, so although what I’ve shown is very simple, I hope you find this of use and helps you get started.