Trigger Azure DevOps Build pipeline from Docker image update

This is the first in a series of posts about automating Docker image builds.

How to trigger a build?

I was recently asked how to trigger a classic (gui) Azure DevOps build pipeline when a Docker base image updates. You might want to do this, for example, if your build pipeline includes unit, functional, or security tests.

My immediate answer was “you can’t.” However, after a little investigation and a little hacking, I was able to prototype a solution using the automated trigger & build feature of Azure Container Registry (ACR). The hint for how to do this was buried in the Q&A on documentation page, where it’s suggested to use a dummy image to trigger the AzDo pipeline. Here’s a walkthrough of how I did this:

Sample Repo & Pipeline

As a sample repo, I used the canonical Azure voting app, but I simplified it a bit to suit my needs. My Dockerfile is pretty simple; my goal is for the build pipeline to run whenever the base python:3.6-slim image is updated on Dockerhub.

FROM python:3.6-slim
RUN pip install redis
RUN pip install flask
ADD /azure-vote /app

CMD [ "python", "/app/main.py" ]

Step 1: Create a dummy base image and task in ACR

As I mentioned above, I’m going to build a dummy base image using ACR, and then use a webhook when this image updates to trigger my Azure DevOps pipeline.

First, I’ll need to follow the instructions here to create a dummy base image and a task to update it when its reference base updates on Dockerhub.

To start, build the base image in ACR:

ACR_NAME=larrymsvoting
REPO="https://larrydevopstest@dev.azure.com/larrydevopstest/AzureVotingApp/_git/AzureVotingApp"
DOCKER_FILE="azure-vote/Dockerfile-pythonbase"

az acr build --registry $ACR_NAME -t baseimages/python-3.6-slim-base \
    --file $DOCKER_FILE $REPO
    

Step 2: Create a task

az acr task create \
    --registry $ACR_NAME \
    --name taskpythonbase \
    -t baseimages/python-3.6-slim-base:{{.Run.ID}} \
    --arg REGISTRY_NAME=$ACR_NAME.azurecr.io \
    --context $REPO \
    --file Dockerfile-pythonbase \
    --commit-trigger-enabled false \
    -o table

Assuming it runs successfully, you’ll see output like:

NAME            PLATFORM    STATUS    SOURCE REPOSITORY                                                                         TRIGGERS
--------------  ----------  --------  ----------------------------------------------------------------------------------------  ----------
taskpythonbase  linux       Enabled   https://larrydevopstest@dev.azure.com/larrydevopstest/AzureVotingApp/_git/AzureVotingApp  BASE_IMAGE

Step 3: Logic App

Now comes the fun part: getting the base image update to trigger a build in Azure DevOps. We’ll start by creating a logic app. In the portal, create a logic app and give it a relevant name, eg ‘pythonbasetrigger’. In the Logic Apps Designer pane, use “When an HTTP Request is Received” as the trigger.

Add a new step, then type ‘Azure DevOps’ in the search field, then choose ‘Queue a new build’ as the action. After authenticating, choose the account name, project name, and build id (eg, pipleline). Once it’s saved, it should like like: logic app

You can test the logic app by pressing ‘Run’, which should kick off an Azure DevOps pipeline build.

Finally, in your logic apps’s first step ‘When an HTTP request is received’, find the post URL and copy it, as it will be needed for the next step.

Step 4: ACR webhook

Next, we’ll create an ACR webhook which fires whenever the base image updates.

URI=<webhook post url from prior step>

az acr webhook create -n pythonbasehook -r $ACR_NAME \
    --scope baseimages/python-3.6-slim-base \
    --actions push --uri $URI

Step 5: Enjoy!

We’ve done it! We now have a set up a set of integrations that does the following: flow

Step 6: Test

Since the Dockerhub base image is only going to be updated periodically, you can test your integration by manually running a build in ACR:

az acr build --registry $ACR_NAME -t baseimages/python-3.6-slim-base \
    --file $DOCKER_FILE $REPO

If all is working, this should trigger a build in Azure DevOps.

Final Thoughts

An alternative to using a webhook would be to use an Event Grid to trigger the Logic App. This is slightly more work to set up, but it might better if you have a number of different pipelines that want to receive notifications when a base image updates. With a webhook, you’d need to set up a trigger for each and every one of them. Eventgrid, having a pubsub architecture, would fan-out one:many more effectively than webhooks.