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¶
Recommended Server Types¶
- 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¶
- Log into Hetzner Cloud Console: https://console.hetzner.cloud
- Navigate to Servers → Add Server
- Select location (Nuremberg, Falkenstein, or Helsinki)
- Choose CCX23 or CCX33 server type
- Select Windows Server 2022 as the image
- Set administrator password (save securely)
- Add to project and create server
Step 2: Initial Server Configuration¶
Connect to Server via RDP¶
On Windows:
- Using Remote Desktop Connection (Built-in):
- Press
Win + R, typemstsc, press Enter - Enter server IP address
- Click Connect
- Enter credentials:
- Username:
Administrator - Password: (password set during server creation or provided by Hetzner)
- Username:
-
Click Yes if prompted about certificate
-
Using PowerShell:
On Mac:
- Install Microsoft Remote Desktop from Mac App Store
- Open Microsoft Remote Desktop
- Click Add PC
- Enter server IP address
- Enter credentials (Administrator + password)
- Click Add and connect
On Linux:
- Install Remmina or another RDP client:
- Create new connection:
- Protocol: RDP
- Server:
<server-ip> - Username:
Administrator - Password: (password from Hetzner)
- 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 Reset → Reset Password 3. New password will be displayed (save it securely)
Install Windows Updates¶
- Open Settings → Update & Security → Windows Update
- Click Check for updates
- Install all available updates
- 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:
- Download Visual Studio Build Tools
- Run installer
- Select .NET desktop build tools workload
- 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:
- Navigate to Organization Settings → Agent Pools → Hetzner-Windows
- Select your agent
- Go to Capabilities tab
- Add custom capabilities:
Agent.OS:Windows_NTAgent.Version:3.248.0DotNet:9.0.xNode:20.x(if Node.js installed)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¶
- In Azure DevOps, navigate to Organization Settings → Agent Pools
- Select Hetzner-Windows pool
- Verify your agent appears in the list
- 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¶
- Set up additional Windows agents for redundancy
- Configure Linux agents if needed
- Set up monitoring and maintenance procedures
- Review troubleshooting guide for common issues