Table of contents
  1. Create a Self-Hosted Agent in Docker Windows Container
    1. Setup
    2. Install Docker for Windows
    3. Switch Docker to Use Windows Containers
    4. Create and Build the Dockerfile
    5. Start the Image

Create a Self-Hosted Agent in Docker Windows Container

You can run your Azure Pipeline agent in Windows, Windows Container, and Ubuntu Container.

This guide will show you how to set up your Azure Pipeline agent in a Windows Container.

Setup

We’ll use a Windows laptop/desktop with Docker Desktop installed. First, we’ll switch Docker Desktop to Windows Mode (it runs in Linux Mode by default) because the container will be a Windows OS.

Then we’ll create the image, and the rest will be easy.

Install Docker for Windows

First, make sure Docker Desktop is installed on your Windows machine.

Switch Docker to Use Windows Containers

Note: Docker can run both Linux and Windows containers. By default, Docker runs in Linux mode. For more details, refer to this guide.

In PowerShell, run this command:

Note: If you have existing Linux containers, they will disappear when you switch to Windows mode but will return when you switch back to Linux mode. You will get a new Docker desktop!

& $Env:ProgramFiles\Docker\Docker\DockerCli.exe -SwitchDaemon

Switch to Windows containers

To switch back to Linux mode later, refer to the image below:

Switch to Linux containers

Create and Build the Dockerfile

Next, create the Dockerfile.

Open a command prompt and create a new directory:

mkdir "C:\azp-agent-in-docker\"
cd "C:\azp-agent-in-docker\"

Save the following content to a file called C:\azp-agent-in-docker\azp-agent-windows.dockerfile:

FROM mcr.microsoft.com/windows/servercore:ltsc2022

WORKDIR /azp/

COPY ./start.ps1 ./

CMD powershell .\start.ps1

Save the following content to C:\azp-agent-in-docker\start.ps1:

Note, this script is taken as-is from this location.

function Print-Header ($header) {
  Write-Host "`n${header}`n" -ForegroundColor Cyan
}

if (-not (Test-Path Env:AZP_URL)) {
  Write-Error "error: missing AZP_URL environment variable"
  exit 1
}

if (-not (Test-Path Env:AZP_TOKEN_FILE)) {
  if (-not (Test-Path Env:AZP_TOKEN)) {
    Write-Error "error: missing AZP_TOKEN environment variable"
    exit 1
  }

  $Env:AZP_TOKEN_FILE = "\azp\.token"
  $Env:AZP_TOKEN | Out-File -FilePath $Env:AZP_TOKEN_FILE
}

Remove-Item Env:AZP_TOKEN

if ((Test-Path Env:AZP_WORK) -and -not (Test-Path $Env:AZP_WORK)) {
  New-Item $Env:AZP_WORK -ItemType directory | Out-Null
}

New-Item "\azp\agent" -ItemType directory | Out-Null

# Let the agent ignore the token env variables
$Env:VSO_AGENT_IGNORE = "AZP_TOKEN,AZP_TOKEN_FILE"

Set-Location agent

Print-Header "1. Determining matching Azure Pipelines agent..."

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$(Get-Content ${Env:AZP_TOKEN_FILE})"))
$package = Invoke-RestMethod -Headers @{Authorization=("Basic $base64AuthInfo")} "$(${Env:AZP_URL})/_apis/distributedtask/packages/agent?platform=win-x64&`$top=1"
$packageUrl = $package[0].Value.downloadUrl

Write-Host $packageUrl

Print-Header "2. Downloading and installing Azure Pipelines agent..."

$wc = New-Object System.Net.WebClient
$wc.DownloadFile($packageUrl, "$(Get-Location)\agent.zip")

Expand-Archive -Path "agent.zip" -DestinationPath "\azp\agent"

try {
  Print-Header "3. Configuring Azure Pipelines agent..."

  .\config.cmd --unattended `
    --agent "$(if (Test-Path Env:AZP_AGENT_NAME) { ${Env:AZP_AGENT_NAME} } else { hostname })" `
    --url "$(${Env:AZP_URL})" `
    --auth PAT `
    --token "$(Get-Content ${Env:AZP_TOKEN_FILE})" `
    --pool "$(if (Test-Path Env:AZP_POOL) { ${Env:AZP_POOL} } else { 'Default' })" `
    --work "$(if (Test-Path Env:AZP_WORK) { ${Env:AZP_WORK} } else { '_work' })" `
    --replace

  Print-Header "4. Running Azure Pipelines agent..."

  .\run.cmd
} finally {
  Print-Header "Cleanup. Removing Azure Pipelines agent..."

  .\config.cmd remove --unattended `
    --auth PAT `
    --token "$(Get-Content ${Env:AZP_TOKEN_FILE})"
}

Build the Docker image:

docker build --tag "azp-agent:windows" --file "./azp-agent-windows.dockerfile" .

The final image will be tagged as azp-agent:windows.

Start the Image

Now that you have created an image, you can run a container. This will install the latest version of the agent, configure it, and run the agent. It targets the specified agent pool (the Default agent pool by default) of a specified Azure DevOps or Azure DevOps Server instance of your choice:

docker run -e AZP_URL="<Azure DevOps instance>" -e AZP_TOKEN="<Personal Access Token>" -e AZP_POOL="<Agent Pool Name>" -e AZP_AGENT_NAME="Docker Agent - Windows" --name "azp-agent-windows" azp-agent:windows