slide

Using azure container instances for an azure dev ops self hosted agent

Ned Bellavance
6 min read

Cover

When you create a new organization in Azure DevOps, you’ll quickly discover you’re not able to run any pipeline jobs using Microsoft-hosted agents. In fact, if you kick off a pipeline, you’ll get the error: No hosted parallelism has been purchased of granted. This post will detail how you can host your own pipeline agents for almost free using Azure Container Instances.

Background

In 2021, the good folks over at Microsoft were fed up with people abusing the free Microsoft-hosted agents that come with an Azure DevOps organization. Because you can make the agent machine run pretty much any arbitrary script, shitty people were creating organizations en masse and using the free compute to do all sorts of nefarious and unsavory things (read: cryptocurrency and spambots).

Microsoft had tried to combat this by restricting how many pipeline jobs were available for use. First they removed the 10 parallel jobs for public projects. Then they restricted private projects to a single parallel job. Nevertheless, the shitty people persisted.

So Microsoft said ENOUGH! And rescinded the previous grant of one parallel job per organization on private projects for new Free-tier organization. Now when you sign up you get ZERO parallel jobs.

That’s intensely annoying, and it stymies your ability to try out various functions of Azure DevOps, as well as do some of the exercises on Microsoft’s Learn platform! While I don’t necessarily agree with Microsoft’s draconian approach, I get it.

So does that mean the shitty people have won and you are SOL? Not necessarily.

You have one of three options to remedy this situation:

  1. You can fill out this form and wait 1-3 days for them to grant you a single parallel job for free. 🥱
  2. You can pay for an Azure DevOps subscription that includes parallel jobs. 🤑
  3. You can use a self-hosted agent. 😊

This post is meant to assist you with the third option. The linked repository will spin up an Azure Container Instance (ACI) that has the Azure DevOps agent installed. Through the use of environment variables, you can configure the agent to be part of one of the pools provisioned in your Azure DevOps organization.

To make things even easier, I’ve added an option to provision the agent pool in AZDO for you as well.

Prerequisites

If you want to follow along, you’ll need the following:

  • An Azure subscription
  • An Azure DevOps organization (free tier)
  • The Azure CLI installed on your machine
  • A Personal Access Token (PAT) for your Azure DevOps organization
    • The token should have Full Access
  • The repository folder copied locally

By default, the configuration uses my ned1313/azp-agent:1.2.0 image on Docker hub. The image is based on the ubuntu:22.04 image and has a startup script that installs the latest version of the Azure DevOps agent your organization supports.

It also includes some common tools like curl, jq, and unzip. Here’s a link to the image build repository in case you’d like to see what’s included, or fork it for yourself.

On startup, the image will grab the latest version of the Azure DevOps agent for your organization and configure it to run as a service. The agent will be added to the pool you specify in the environment variables.

Let’s walk through a sample deployment shall we?

Deployment Process

The deployment process is simple, but you need to set up a few things first.

Start by logging into Azure using the Azure CLI and selecting the subscription you want to use:

az login
az account set -s SUBSCRIPTION_NAME

Next, create a personal access token (PAT) in Azure DevOps with Full Access and store that token value in an environment variable:

export TF_VAR_azp_token=TOKEN_VALUE

Now fill out the terraform.tfvars file with your desired location and AZDO organization name.

location     = "eastus"
azp_org_name = "tacocat"

If you want to use an existing agent pool you’ve already provisioned, add that as well:

azp_pool = "tacopool"

If you don’t give a value for the azp_pool input variable, the configuration will create a pool named aci-agents instead.

Now just do the standard Terraform deployment dance (it’s like the Neutron Dance, but way cooler):

terraform init
terraform apply

Once the deployment is complete, you can look at your agent pool and see that it is deployed.

Azure DevOps self-hosted agent pool

The configuration is set up to make the pool visible to all projects by default, but you’ll need to go into the desired project and grant pipelines permission to use the pool.

Azure DevOps project permissions

In the YAML for the pipeline, you can configure a job to use the pool with the following syntax:

stages:
- stage: Apply
  displayName: Apply
  jobs:
  - job: apply
    pool: aci-agents # or whatever your pool name is...

Like I said, the image has all the tooling I would want for a basic Terraform deployment pipeline, but it doesn’t come with build tools for every language. If you need to add more tools, take my Dockerfile and build your own. You might find the public repo for the Microsoft-hosted agents helpful for installation scripts.

Cost

ACI is not free, but it is relatively inexpensive. If you want totally free, spin the image up on your local workstation instead. There’s a start-agent.sh script included in the image repository.

The cost of an ACI group varies by region, so I recommend consulting the Azure pricing page for the most up-to-date information. My configuration uses 1 vCPU and 2 GB of memory, which if you run it for an hour in US East would come out to about $0.05 or $1.20 per day. You might be able to save a little money by using less vCPU or memory, but I haven’t tested that.

You can also destroy the ACI instance when you’re not using it and leave the pool in place. The pool doesn’t cost anything to maintain.

Final Thoughts

It sucks that shitty people ruin everything™️. Personally, I think Microsoft has over-corrected on this one. When I went to go create a new Azure DevOps organization, I had to jump through two separate captcha hurdles and a bunch of other nonsense. It seems like they’ve put sufficient controls in place to start granting that single parallel job to Free tier accounts again.

The form and 1-3 day wait isn’t forever, so I’d recommend filling out the form and using my solution as a stop-gap measure. It’s also a chance to learn about self-hosted agents and options for deploying them. That’s nice and sometimes we can have nice things.

Share and enjoy!