Azure DevOps Agent in Docker with Deployment Groups

Recently started learning how to user Azure DevOps, formerly VSTS, to do automated build and deploy tasks to several docker hosts. In this POC I am running I will have about 600 machines running docker in remote locations to act as an edge/branch server. These docker hosts will be running 5-6 containers to handle basic services such as DNS, FTP, FTE and PowerShell for automation. I wanted an easy way to deploy and update containers on these 600 remote machines. I looked at various things from using Rancher to custom PowerShell scripts to hit the docker API and/or SSH into each machine and run the docker commands. For this post, we are going to focus on how I accomplished these tasks using Azure DevOps.

Microsoft has already released pre-built Docker images for the VSTS agent (https://hub.docker.com/_/microsoft-azure-pipelines-vsts-agent). While this worked great it only allowed me to register the agent into an agent pool in DevOps. In my case, I wanted the agent to register with a deployment group so I could run the same task on every agent. Turns out Microsoft has no documented way of how to accomplish that. So I started digging through how they built their container, the agent looks exactly like the one they install on Linux (no surprise there) which they do provide instructions for on how to register the agent in a deployment group. So now its just down to getting the container version to accept the options for deployment groups. Heres how I did it.

Pull the image down from the hub to your localhost. In this case, I want the agent to have the docker command line utilities installed so I can interface with docker on the host through the Unix socket. Microsoft has an image already built for this:

docker pull  mcr.microsoft.com/azure-pipelines/vsts-agent:ubuntu-16.04-docker-18.06.1-ce 

Now, if you look at the directions in DevOps on how to deploy an agent to Linux (Pipelines > Deployment Groups > Register) you will find that two options are needed that are not in the documentation for the docker based agent, nor is the container from Microsoft setup to accept these options. Those options are –deploymentgroup and –deploymentgroupname.

After inspecting the image we can see that it is launching the start.sh script. CMD /bin/sh -c ./start.sh

This script can be downloaded from github here:
https://github.com/Microsoft/vsts-agent-docker

To make this do what we need we will need to modify the start.sh script and then build a custom container with our changes.

#Lines 40-46
if [ -n "$VSTS_DEPLOYMENTPOOL" ]; then
  export VSTS_DEPLOYMENTPOOL="$(eval echo $VSTS_DEPLOYMENTPOOL)"
fi

if [ -n "$VSTS_PROJECTNAME" ]; then
  export VSTS_PROJECTNAME="$(eval echo $VSTS_PROJECTNAME)"
fi

#Lines 91-100.  Remove --pool. 
#Add --deploymentgroup, --deploymentgroupname, --projectname

./bin/Agent.Listener configure --unattended \
  --agent "${VSTS_AGENT:-$(hostname)}" \
  --url "https://$VSTS_ACCOUNT.visualstudio.com" \
  --auth PAT \
  --token $(cat "$VSTS_TOKEN_FILE") \
  --work "${VSTS_WORK:-_work}" \
  --deploymentgroup \
  --deploymentgroupname "${VSTS_DEPLOYMENTPOOL:-default}" \
  --projectname "${VSTS_PROJECTNAME:-projectnamedefault}" \
  --replace & wait $!

Create a new dockerfile that will copy in your modified start.sh and build your new container.

FROM mcr.microsoft.com/azure-pipelines/vsts-agent:ubuntu-16.04-docker-18.06.1-ce

COPY start.sh /vsts

WORKDIR /vsts

RUN chmod +x start.sh

CMD ./start.sh

Now you can run your container. Specify your deployment group and project name as environment variables at runtime.

docker run -e VSTS_ACCOUNT=yourAccountName \
-e VSTS_TOKEN=yourtoken \
-e VSTS_DEPLOYMENTPOOL=yourpoolname \
-e VSTS_PROJECTNAME=yourprojectname \
-v /var/run/docker.sock:/var/run/docker.sock \
-d \
--name DevOps-Agent \
--hostname $(hostname) \
-it \
yourprivateregistry/devops-agent

And boom, achievement badge unlocked! You now have agents reporting to a deployment group.