Skip to content

Self-Hosted Azure DevOps Agents - Windows Setup Guide

Prerequisites

  • Hetzner Cloud account with active project
  • Windows Server 2022 server provisioned on Hetzner Cloud
  • RDP access to the server
  • Azure DevOps organization access
  • Personal Access Token (PAT) with Agent Pools (Read & Manage) scope

Step 1: Server Selection and Purchase

  • CCX23: Dedicated CPU, 4 vCPU, 8 GB RAM, 160 GB SSD (~€25.90/month) - Minimum for Windows
  • CCX33: Dedicated CPU, 8 vCPU, 16 GB RAM, 240 GB SSD (~€49.90/month) - Recommended for production

Note: Windows requires dedicated CPU instances (CCX series), not shared CPU (CPX series).

Purchase Steps

  1. Log into Hetzner Cloud Console: https://console.hetzner.cloud
  2. Navigate to ServersAdd Server
  3. Select location (Nuremberg, Falkenstein, or Helsinki)
  4. Choose CCX23 or CCX33 server type
  5. Select Windows Server 2022 as the image
  6. Set administrator password (save securely)
  7. Add to project and create server

Step 2: Initial Server Configuration

Connect to Server via RDP

On Windows:

  1. Using Remote Desktop Connection (Built-in):
  2. Press Win + R, type mstsc, press Enter
  3. Enter server IP address
  4. Click Connect
  5. Enter credentials:
    • Username: Administrator
    • Password: (password set during server creation or provided by Hetzner)
  6. Click Yes if prompted about certificate

  7. Using PowerShell:

    # Connect via RDP
    mstsc /v:<server-ip>
    

On Mac:

  1. Install Microsoft Remote Desktop from Mac App Store
  2. Open Microsoft Remote Desktop
  3. Click Add PC
  4. Enter server IP address
  5. Enter credentials (Administrator + password)
  6. Click Add and connect

On Linux:

  1. Install Remmina or another RDP client:
    # Ubuntu/Debian
    sudo apt install remmina remmina-plugin-rdp
    
    # Start Remmina
    remmina
    
  2. Create new connection:
  3. Protocol: RDP
  4. Server: <server-ip>
  5. Username: Administrator
  6. Password: (password from Hetzner)
  7. Click Connect

Troubleshooting RDP Connection: - Verify server IP address in Hetzner Cloud Console - Ensure RDP is enabled (usually enabled by default on Windows Server) - Check Windows Firewall allows RDP (port 3389) - Wait a few minutes after server creation for Windows to fully boot - Verify administrator password is correct

Initial Connection and Setup

First Connection: - When connecting for the first time, Windows may take 5-10 minutes to fully initialize - You may see "Getting Windows ready" screen - wait for it to complete - After first login, Windows may install additional drivers and updates

Get Server IP Address: 1. Log into Hetzner Cloud Console: https://console.hetzner.cloud 2. Navigate to Servers 3. Click on your Windows server 4. Copy the IPv4 address shown

Get Administrator Password: - If you set a password during server creation, use that - If you forgot, you can reset it in Hetzner Cloud Console: 1. Go to server details 2. Click ResetReset Password 3. New password will be displayed (save it securely)

Install Windows Updates

  1. Open SettingsUpdate & SecurityWindows Update
  2. Click Check for updates
  3. Install all available updates
  4. Restart if required

Alternative (PowerShell):

# Check for updates
Get-WindowsUpdate

# Install updates (requires PSWindowsUpdate module)
Install-WindowsUpdate -AcceptAll -AutoReboot

Configure Windows Firewall

# Allow RDP (if not already enabled)
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

# Allow outbound HTTPS (usually enabled by default)
# Verify with:
Get-NetFirewallRule -DisplayName "*HTTPS*" | Select-Object DisplayName, Enabled

Create Agent User

# Run PowerShell as Administrator

# Create dedicated user for Azure DevOps agent
$Password = ConvertTo-SecureString "YourSecurePassword123!" -AsPlainText -Force
New-LocalUser -Name "azdevops" -Password $Password -Description "Azure DevOps Agent User"

# Add to Administrators group
Add-LocalGroupMember -Group "Administrators" -Member "azdevops"

# Verify user creation
Get-LocalUser -Name "azdevops"

Step 3: Install Azure DevOps Agent

Download Agent

# Create agent directory
New-Item -ItemType Directory -Path "C:\azagent" -Force
Set-Location C:\azagent

# Download latest agent (check https://github.com/microsoft/azure-pipelines-agent/releases for latest version)
$AgentVersion = "3.248.0"
$AgentUrl = "https://vstsagentpackage.azureedge.net/agent/$AgentVersion/vsts-agent-win-x64-$AgentVersion.zip"
Invoke-WebRequest -Uri $AgentUrl -OutFile "agent.zip"

# Extract agent
Expand-Archive -Path "agent.zip" -DestinationPath . -Force
Remove-Item "agent.zip"

Configure Agent

Before configuring, ensure you have: - Azure DevOps organization URL (e.g., https://dev.azure.com/ConnectSoft) - Personal Access Token (PAT) with Agent Pools (Read & Manage) scope - Agent pool name (e.g., Hetzner-Windows)

# Configure agent
.\config.cmd `
  --url https://dev.azure.com/ConnectSoft `
  --auth pat `
  --token <YOUR_PAT_TOKEN> `
  --pool "Hetzner-Windows" `
  --agent "hetzner-windows-01" `
  --work _work `
  --acceptTeeEula `
  --unattended `
  --replace

Configuration Options: - --url: Your Azure DevOps organization URL - --auth pat: Use Personal Access Token authentication - --token: Your PAT token - --pool: Agent pool name (create in Azure DevOps first) - --agent: Unique agent name - --work: Working directory for agent - --acceptTeeEula: Accept the Team Explorer Everywhere license - --unattended: Run without prompts - --replace: Replace existing agent configuration if present

Install as Windows Service

# Install service
.\svc.cmd install

# Start service
.\svc.cmd start

# Check status
Get-Service | Where-Object {$_.Name -like "*vsts*"}

Verify Agent Status

# Check service status
Get-Service | Where-Object {$_.Name -like "*vsts*"}

# View service logs
Get-EventLog -LogName Application -Source "vsts*" -Newest 20

Step 4: Install Build Tools

Install .NET SDK

# Download .NET 9 SDK installer
$DotNetUrl = "https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1"
Invoke-WebRequest -Uri $DotNetUrl -OutFile "$env:TEMP\dotnet-install.ps1"

# Install .NET 9 SDK
& "$env:TEMP\dotnet-install.ps1" -Channel 9.0 -InstallDir "$env:ProgramFiles\dotnet"

# Add to PATH (if not automatically added)
$env:Path += ";$env:ProgramFiles\dotnet"

# Verify installation
dotnet --version

Install Visual Studio Build Tools (Optional)

For projects requiring MSBuild or Visual Studio components:

  1. Download Visual Studio Build Tools
  2. Run installer
  3. Select .NET desktop build tools workload
  4. Install

Install Git for Windows

# Download Git
$GitUrl = "https://github.com/git-for-windows/git/releases/download/v2.43.0.windows.1/Git-2.43.0-64-bit.exe"
Invoke-WebRequest -Uri $GitUrl -OutFile "$env:TEMP\GitInstaller.exe"

# Install Git (silent mode)
Start-Process -FilePath "$env:TEMP\GitInstaller.exe" -ArgumentList "/VERYSILENT /NORESTART" -Wait

# Add Git to PATH
$env:Path += ";C:\Program Files\Git\cmd"

# Verify installation
git --version

Install Node.js (Optional)

# Download Node.js installer
$NodeUrl = "https://nodejs.org/dist/v20.10.0/node-v20.10.0-x64.msi"
Invoke-WebRequest -Uri $NodeUrl -OutFile "$env:TEMP\nodejs.msi"

# Install Node.js
Start-Process msiexec.exe -ArgumentList "/i $env:TEMP\nodejs.msi /quiet /norestart" -Wait

# Verify installation
node --version
npm --version

Install Additional Tools

Based on your pipeline requirements:

# Chocolatey (package manager) - Optional but recommended
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

# Install tools via Chocolatey (examples)
choco install python -y
choco install jdk8 -y
choco install docker-desktop -y

Step 5: Configure Agent Capabilities

In Azure DevOps:

  1. Navigate to Organization SettingsAgent PoolsHetzner-Windows
  2. Select your agent
  3. Go to Capabilities tab
  4. Add custom capabilities:
  5. Agent.OS: Windows_NT
  6. Agent.Version: 3.248.0
  7. DotNet: 9.0.x
  8. Node: 20.x (if Node.js installed)
  9. VisualStudio: true (if Visual Studio Build Tools installed)

Step 6: Network Configuration

Firewall Rules

Ensure the server can communicate with Azure DevOps:

# Allow outbound HTTPS (usually enabled by default)
# Verify with:
Get-NetFirewallRule -DisplayName "*HTTPS*" | Select-Object DisplayName, Enabled

# Allow RDP (if managing remotely)
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

Test Connectivity

# Test Azure DevOps connectivity
Test-NetConnection -ComputerName dev.azure.com -Port 443

# Test DNS resolution
Resolve-DnsName dev.azure.com

Step 7: Verify Agent Registration

  1. In Azure DevOps, navigate to Organization SettingsAgent Pools
  2. Select Hetzner-Windows pool
  3. Verify your agent appears in the list
  4. Agent status should be Online (green)

Step 8: Test with a Pipeline

Create a simple test pipeline:

pool:
  name: 'Hetzner-Windows'
  demands:
    - Agent.OS -equals Windows_NT

steps:
  - powershell: |
      Write-Host "Running on self-hosted Windows agent"
      systeminfo | Select-String "OS Name", "OS Version"
      Get-PSDrive C | Select-Object Used, Free
      dotnet --version
    displayName: 'Test Agent'

Maintenance

Update Agent

# Stop service
.\svc.cmd stop

# Download new version
$AgentVersion = "3.248.0"  # Update to latest version
$AgentUrl = "https://vstsagentpackage.azureedge.net/agent/$AgentVersion/vsts-agent-win-x64-$AgentVersion.zip"
Invoke-WebRequest -Uri $AgentUrl -OutFile "agent.zip"
Expand-Archive -Path "agent.zip" -DestinationPath . -Force
Remove-Item "agent.zip"

# Restart service
.\svc.cmd start

Uninstall Agent

# Stop and remove service
.\svc.cmd stop
.\svc.cmd uninstall

# Remove agent directory
Remove-Item -Path "C:\azagent" -Recurse -Force

Troubleshooting

Agent Not Appearing in Azure DevOps

  • Verify PAT token has correct permissions
  • Check agent configuration: Get-Content C:\azagent\.agent
  • Review Windows Event Log: Get-EventLog -LogName Application -Source "vsts*" -Newest 50

Agent Offline

  • Check service status: Get-Service | Where-Object {$_.Name -like "*vsts*"}
  • Verify network connectivity: Test-NetConnection -ComputerName dev.azure.com -Port 443
  • Check Windows Firewall rules
  • Review agent logs in Event Viewer

Build Failures

  • Verify required tools are installed
  • Check disk space: Get-PSDrive C
  • Review build logs in Azure DevOps
  • Check agent capabilities match pipeline demands
  • Verify PATH environment variable includes required tools

RDP Connection Issues

  • Verify firewall allows RDP: Get-NetFirewallRule -DisplayGroup "Remote Desktop"
  • Check RDP service is running: Get-Service TermService
  • Verify administrator password is correct
  • Check server is accessible from your network

Security Considerations

  • Use strong passwords for administrator and agent user accounts
  • Regularly update Windows and installed software
  • Configure Windows Firewall appropriately
  • Use PAT tokens with minimal required permissions
  • Rotate PAT tokens regularly
  • Consider using Windows Defender or additional antivirus software

Next Steps

References