Github actions have already shown a lot of promise and some distance to travel before it can stand next to its big brother ‘Azure DevOps’. When I say ‘big brother’, they’re both technically Microsoft Products now.
Azure DevOps for historical reasons was tagged as Azure native and Azure only. But, it’s not really an Azure only and I’m aware of organisations using Azure DevOps to manage their infrastructure. Don’t believe me… time to take the red pill and read about the AWS Toolkit for Microsoft Azure DevOps
Common sense prevailing, it’s right to market GitHub as the true multi-cloud champion instead of Azure DevOps (previously known as Microsoft VSTS,TFS). Which is what Microsoft seems to be doing with the addition of number features from areas such as Azure Pipelines and Visual Studio Codespaces to make the product more robust and enterprise friendly.
In this post I’m going to explain how to create a custom action for Azure, using my experience building an action as a part of https://githubhackathon.com
I’m in the Winners List :)
Actions documentation is pretty concise and helps you to get started quickly. Below links cover the basics for you to get started;
I went for the reliability and decided to live with the extra time taken to build the container.
To make ‘GitHub Actions’ ready, you need to tick off few things as a part of your github repository;
LICENSE- A license that permits others to use the action. I’ve gone wit MIT
README.md- This will be used as the marketplace description. Try to provide sufficient information on how to use the action with examples. Here’s mine: https://github.com/marketplace/actions/manage-nsg
- Metadata file —
- inputs — input variables
- outputs — output variables which can be used later in the workflow
- branding — required for publishing to the marketplace
- runs —
image: 'Dockerfile'and the inputs set as
entrypoint.shexpressing what you need to run on the container.
- A Dockerfile — [Doc]
For my first attempt tried to start with a vanilla alpine and quickly realised that I’m trying to re-invent the wheel. Best starting ground for azure is to use the official azure cli container.
Below is the working Dockerfile content of my action;
# Using latest might cause issues with breaking changes.
# FROM mcr.microsoft.com/azure-cli:latest# https://hub.docker.com/_/microsoft-azure-cli
FROM mcr.microsoft.com/azure-cli:2.8.0# Copies your code file from your action repository to the filesystem path `/` of the container.
COPY entrypoint.sh /entrypoint.sh# Set the missing exec permission, just in case if you're on on a *nix.
RUN chmod +x ./entrypoint.sh# Enable dig to find the runner's public IP
RUN apk update && apk add --no-cache bind-tools && rm -rf /var/cache/apk/*# Code file to execute when the docker container starts up (`entrypoint.sh`)
- Consideration #1
FROM mcr.microsoft.com/azure-cli:latest. Versions change pretty fast and you wouldn’t want your workflow to break due to uncontrollable changes. Refer to the list of tags and use the latest at the point of creation and rollout updates overtime. I’ve started with
2.2.0 and moved to
2.8.0 at the point current update to the post.
- Consideration #2
Make sure your
entrypiont.sh file is executable. Use
RUN chmod +x ./entrypoint.sh in your
My action is capable of two things.
- Create a temporary Azure NSG Rule allowing a given port of the runner’s public IP to access resources behind resource protected by an Azure NSG.
E.g. Deploy to a protected Web App running on an ASE or an Azure VM.
- Remove the temporary rule after deployment is completed.
Let’s look at what the
entrypoint.sh does. Comments should tell you all about it!
#!/bin/sh -l# Ensure the workflow fails on error
_rule_public_ip=$(dig +short myip.opendns.com @resolver1.opendns.com) # Get runner's public IP
# break the azure credientials JSON
_client_id=$(echo $_azure_credentails | jq -r '.clientId')
_client_secret=$(echo $_azure_credentails | jq -r '.clientSecret')
_tenant_id=$(echo $_azure_credentails | jq -r '.tenantId')
_subscription_id=$(echo $_azure_credentails | jq -r '.subscriptionId')# Login to azure using service principal
az login --service-principal -u $_client_id -p $_client_secret --tenant $_tenant_id# Select the subscription
az account set --subscription $_subscription_idif [ $_rule_id_for_removal ]
# removing the rule
echo "Removing rule $_rule_id_for_removal"
az network nsg rule delete -g $_rule_nsg_resource_group --nsg-name $_rule_nsg -n $_rule_id_for_removal
echo "::set-output name=rule_name::$_rule_id_for_removal"
# adding the rule
echo _rule_port: $_rule_port
echo _rule_priority_start: $_rule_priority_start
echo _rule_priority_end: $_rule_priority_end _rule_priority=$(shuf -i $_rule_priority_start-$_rule_priority_end -n 1)
_rule_name=manage-nsg-github-actions-$_rule_priority echo "Adding rule.... $_rule_name"
az network nsg rule create -g $_rule_nsg_resource_group --nsg-name $_rule_nsg \
-n $_rule_name --priority $_rule_priority \
--source-address-prefixes $_rule_public_ip --source-port-ranges '*' \
--destination-address-prefixes '*' --destination-port-ranges '*' \
--access Allow --protocol '*' \
--description 'Allow from IP address of github actions hosted runner temporarily'
echo "::set-output name=rule_name::$_rule_name"
- Publishing is easy. Go to the releases and tick the box to “Publish this Action to the GitHub Marketplace”
- Define a tag which allows the users to define which version of the action to use.
- Select your categories.
- Give a Title and a Description for the release.
- Hit “Publish Release”
- Make sure to tag the unstable releases as “This is a pre-release” to let the users know that it’s not ready for consumption.
Testing is easy. All you need is a workflow that runs
on: push for the relevant branches and test the action.
Note: Instead of using a specific release I’m using the branch to ensure that I’m testing the latest code on the branch.
name: Run GitHub Action Tests
steps: - name: dig +short myip.opendns.com @resolver1.opendns.com
run: dig +short myip.opendns.com @resolver1.opendns.com - name: Add NSG Rule
rule-nsg-name: ManageNsg - name: Print Created NSG Rule Name
run: echo "Rule Name $" - name: Remove NSG Rule
You can test the action even before publishing using the local actions. This feature allows for private actions that are specific to a repository.
- name: Check out repository
- name: Use local my-action