Azure DevOps Self-hosted Agent einrichten

Self-hosted Azure DevOps Agent auf einem lokalen Windows Server Schritt für Schritt einrichten.
Michel W.
06/2026

TL;DR

Wenn Cloud-Hosting keine Option ist, ermöglicht ein Self-hosted Azure DevOps Agent vollautomatische CI/CD-Pipelines im eigenen Rechenzentrum. Der Agent verbindet sich aktiv von innen nach außen – ohne eingehenden Traffic, offene Ports oder aufwendige Firewall-Regeln. Der Beitrag zeigt die komplette Einrichtung auf einem lokalen Windows Server: von der TLS-Konfiguration über PAT-Scopes, Agent Pools und die Dienst-Account-Anpassung für IIS bis zur fertigen azure-pipelines.yml für eine ASP.NET Core Blazor Anwendung. Inklusive der typischen Stolpersteine aus der Praxis – TLS 1.2, fehlende Manage-Berechtigungen und Pool-Konfiguration – und ihrer Lösungen. Ergebnis: ein reproduzierbares On-Premise-Deployment vom Pull Request bis zum laufenden IIS.

Azure DevOps Self-hosted Agent einrichten

Einleitung

In vielen Unternehmen läuft die Entwicklung über Azure DevOps – CI/CD-Pipelines, Repositories, Work Items. Doch nicht immer landet die App am Ende in der Cloud. Lokale Server, On-Premise-Infrastruktur und Sicherheitsanforderungen machen ein klassisches Cloud-Deployment oft unmöglich.

Dieser Artikel zeigt, wie man einen Self-hosted Azure DevOps Agent auf einem lokalen Windows Server einrichtet und eine vollautomatische CI/CD-Pipeline für eine ASP.NET Core Blazor Anwendung aufbaut – vom Pull Request bis zum laufenden IIS-Deployment.

Dabei wird nicht nur der Idealfall behandelt, sondern auch die typischen Stolpersteine aus der Praxis: TLS-Probleme, fehlende Berechtigungen, falsche PAT-Scopes und die richtige Konfiguration des Dienst-Accounts.

Voraussetzungen

  • Azure DevOps Organisation vorhanden
  • Lokaler Windows Server mit IIS
  • RDP- oder direkter Zugang zum Server
  • Admin-Rechte auf dem Server
  • Azure DevOps Grundkenntnisse (Pipelines, Repositories)

Architektur & Funktionsprinzip

Der entscheidende Unterschied zu einem Microsoft-hosted Agent: Der Self-hosted Agent verbindet sich aktiv von innen nach außen zu Azure DevOps – nicht umgekehrt. Eingehender Traffic, geöffnete Ports oder eine komplizierte Firewall-Konfiguration sind nicht notwendig.

Schritt 1: Netzwerk & TLS prüfen

Bevor der Agent installiert wird, muss sichergestellt werden, dass der Server Azure DevOps überhaupt erreichen kann.

Verbindungstest in PowerShell:

Test-NetConnection -ComputerName dev.azure.com -Port 443

Häufiges Problem – TLS-Version:

Ältere Windows Server verwenden standardmäßig TLS 1.0, Azure DevOps erfordert jedoch mindestens TLS 1.2. Symptom: Test-NetConnection funktioniert, aber Invoke-WebRequest schlägt mit „Die zugrunde liegende Verbindung wurde geschlossen“ fehl.

Test:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12Invoke-WebRequest -Uri "https://dev.azure.com" -UseBasicParsing

Funktioniert es damit, muss TLS 1.2 dauerhaft als Standard gesetzt werden (Admin-PowerShell erforderlich):

Set-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -Name 'SystemDefaultTlsVersions' -Value 1 -Type DWordSet-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value 1 -Type DWordSet-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -Name 'SystemDefaultTlsVersions' -Value 1 -Type DWordSet-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value 1 -Type DWord

Hinweis: Keine Ausgabe = Erfolg.

Zur Verifikation in PowerShell:

Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' | Select-Object SystemDefaultTlsVersions, SchUseStrongCrypto

Beide Werte sollten „1“ zeigen.

Schritt 2: Personal Access Token (PAT) erstellen

Der Agent authentifiziert sich über einen PAT gegen Azure DevOps. Wichtig: Der PAT braucht explizit den Agent-Pools-Scope – andere Scopes reichen nicht aus.

Warum reicht „Full Access auf Projektebene“ nicht?

Agent Pools sind eine organisationsweite Ressource, keine projektspezifische. Ein PAT mit Full Access auf Projektebene hat keine Rechte auf Organisation-Level-Ressourcen.

PAT erstellen:

  1. Azure DevOps → Profilbild → Personal Access Tokens
  2. New Token
  3. Organisation: eigene Organisation wählen
  4. Scopes: Custom defined → Agent Pools: Read & manage aktivieren
  5. Token kopieren und sicher aufbewahren – er wird nur einmal angezeigt

Scope-Erklärung:

ScopeBerechtigungReadPool und Agents sehen, Status abfragenManageAgents registrieren, löschen, konfigurieren

Für die Agent-Registrierung wird Manage benötigt, da config.cmd intern einen POST-Request gegen die Azure DevOps API sendet – ohne Manage kommt ein „403 Forbidden“.

Schritt 3: Agent Pool anlegen

Agent Pools werden auf Organisation-Ebene verwaltet – nicht auf Projektebene.

Navigation:

https://dev.azure.com/EURE-ORGANISATION/_settings/agentpools

  1. Add Pool klicken
  2. Type: Self-hosted
  3. Name vergeben (z. B. LocalServer)
  4. Grant access permission to all pipelines aktivieren
  5. Create

Wichtig: Der ausführende Account benötigt Administrator-Rechte am Pool, um Agents registrieren zu können. Dies muss vom Organisation Admin einmal eingerichtet werden: Organisation Settings → Agent Pools → [Pool] → Security → Account als Administrator eintragen.

Schritt 4: Agent herunterladen & installieren

Den Agent direkt aus Azure DevOps herunterladen, das garantiert die aktuelle Version:_settings/agentpools → Pool auswählen → New Agent → Windows → DownloadVersionsnummer notieren, wird später gebraucht.

Die Zip-Datei nach C:\azagent entpacken.

Konfiguration in Admin-PowerShell:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12cd C:\azagent\vsts-agent-win-x64-X.XXX.X.\config.cmd --url https://dev.azure.com/ORGANISATION `--auth pat `--token DEIN_PAT_TOKEN `--pool "LocalServer" `--agent local-server-01 `--runAsService `--work C:\azagent\_work

Das Backtick (`) am Ende der Zeile ist für PowerShell wichtig, da es signalisiert, dass die Anweisung in der nächsten Zeile weitergeht.

Interaktive Fragen während der Konfiguration:

Frage Empfehlung Begründung
SERVICE_SID_TYPE UNRESTRICTED (J/N) N Nur für spezielle AD-Umgebungen nötig
Benutzerkonto für den Dienst Enter (leer) Verwendet NETWORK SERVICE als Standard
Dienst sofort starten verhindern (J/N) N Dienst soll direkt starten

Hinweis zu weiteren möglichen Fragen: In Umgebungen mit Proxy wird nach Proxy-URL, Benutzername und Passwort gefragt. Bei self-signed Zertifikaten nach der CA-Zertifikatdatei.

Agent-Status prüfen:

Nach erfolgreicher Konfiguration in Azure DevOps prüfen:

Organisation Settings → Agent Pool → [Pool] → Agent

Der Agent sollte mit einem grünen Punkt als Online erscheinen.

Schritt 5: Dienst-Account für IIS-Steuerung anpassen

Standardmäßig läuft der Agent-Dienst als NETWORK SERVICE – dieser Account hat keine Rechte, IIS zu steuern. Für automatisches Stoppen und Starten von IIS-Websites muss der Dienst-Account auf LocalSystem geändert werden.

In PowerShell:

Dienstname ermitteln:

Get-Service | Where-Object { $_.Name -like "*vstsagent*" }

Account ändern:

sc.exe config "DIENSTNAME" obj= "LocalSystem"

Dienst neustarten:

Restart-Service "DIENSTNAME"

Schritt 6: IIS Test-Umgebung einrichten

Niemals direkt auf der Produktionsumgebung testen. Eine separate IIS-Website auf einem anderen Port ist schnell eingerichtet:

PowerShell:

Ordner anlegen:

mkdir C:\inetpub\wwwroot\app_test

Neue Website erstellen:

New-Website -Name "MeineApp Test" -PhysicalPath "C:\inetpub\wwwroot\apptest" -Port 8080 -Force

(Hinweis: Ordnerpfad und -PhysicalPath sollten identisch sein – siehe Code-Hinweise oben.)

Schritt 7: azure-pipelines.yml erstellen

Die Pipeline-Konfiguration gehört als azure-pipelines.yml in den Root des Repositories.

trigger:
  branches:
    include:
      - main

# Self-hosted Agent Pool angeben
pool:
  name: LocalServer

variables:
  buildConfiguration: Release
  websiteName: 'MeineApp'             # Exakter IIS Website-Name
  deployPath: 'C:\inetpub\wwwroot\meineapp'

stages:

# CI – Build & Publish
- stage: CI
  jobs:
  - job: Build
    steps:

    - task: DotNetCoreCLI@2
      displayName: Build
      inputs:
        command: build
        projects: '**/*.csproj'
        arguments: '--configuration $(buildConfiguration)'

    - task: DotNetCoreCLI@2
      displayName: Publish
      inputs:
        command: publish
        publishWebProjects: true
        arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
        zipAfterPublish: false

    - task: PublishPipelineArtifact@1
      displayName: Artifact hochladen
      inputs:
        targetPath: '$(Build.ArtifactStagingDirectory)'
        artifact: 'webapp'

# CD – Deploy auf lokalen IIS
- stage: CD
  dependsOn: CI
  condition: succeeded()
  jobs:
  - deployment: DeployIIS
    environment: 'local-server'
    strategy:
      runOnce:
        deploy:
          steps:

          - task: DownloadPipelineArtifact@2
            inputs:
              artifact: 'webapp'
              path: '$(Pipeline.Workspace)/webapp'

          - task: PowerShell@2
            displayName: IIS Website stoppen
            inputs:
              targetType: inline
              script: |
                Import-Module WebAdministration
                Stop-Website -Name '$(websiteName)'

          - task: CopyFiles@2
            displayName: Dateien deployen
            inputs:
              SourceFolder: '$(Pipeline.Workspace)/webapp'
              Contents: '**'
              TargetFolder: $(deployPath)
              OverWrite: true

          - task: PowerShell@2
            displayName: IIS Website starten
            inputs:
              targetType: inline
              script: |
                Import-Module WebAdministration
                Start-Website -Name '$(websiteName)'

Warum PowerShell statt IIS-Tasks?

Der offizielle IISWebAppManagementOnMachineGroup Task erfordert über 16 Pflichtfelder. Der PowerShell-Ansatz mit WebAdministration macht dasselbe mit deutlich weniger Konfigurationsaufwand.

Schritt 8: Pipeline in Azure DevOps anlegen

In Azure DevOps:

  1. Pipelines → New Pipeline
  2. Azure Repos Git auswählen
  3. Repository auswählen
  4. Existing Azure Pipelines YAML file
  5. Branch: main, Path: /azure-pipelines.yml
  6. Continue → Run

Beim ersten Run fragt Azure DevOps, ob die Pipeline den Pool verwenden darf → Permit klicken.

Typische Stolpersteine

Problem Ursache Lösung
TcpTestSucceeded: True, aber Invoke-WebRequest schlägt fehl TLS 1.0 Standard TLS 1.2 in Registry setzen
403 Forbidden bei Agent-Registrierung PAT ohne Agent Pools Manage Scope Neuen PAT mit korrektem Scope erstellen
Pool not found Pool auf Projektebene statt Organisationsebene Pool in Organisation Settings anlegen
Access denied. Needs Manage permissions Account nicht als Pool-Administrator eingetragen Admin am Pool eintragen lassen
UnauthorizedAccessException beim Stoppen von IIS Agent-Dienst läuft als NETWORK SERVICE Dienst-Account auf LocalSystem ändern
Deps.json does not exist --no-build sucht Debug-Build, Release gebaut --configuration Release ergänzen
Agent bereits konfiguriert Vorherige fehlgeschlagene Konfiguration config.cmd remove ausführen, dann neu konfigurieren

Ergebnis

Nach Abschluss dieser Einrichtung läuft der komplette Deployment-Prozess vollautomatisch:

  1. Entwickler erstellt Pull Request
  2. PR wird approved und in „main“ gemerged
  3. Pipeline wird automatisch getriggert
  4. CI: Build → Tests → Publish
  5. CD: IIS stoppen → Dateien deployen → IIS starten
  6. Neue Version läuft auf dem lokalen Server

Kein manuelles Deployment mehr. Keine vergessenen Schritte. Keine Downtime durch menschliche Fehler.

Fazit

Ein Self-hosted Azure DevOps Agent ist eine geeignete Lösung, wenn Cloud-Hosting keine Option ist. Die Einrichtung erfordert etwas Geduld – besonders beim Thema Berechtigungen und TLS – aber das Ergebnis ist eine vollautomatische, robuste Deployment-Pipeline, die on-premise genauso zuverlässig funktioniert wie in der Cloud.

Der entscheidende Vorteil: Der Agent verbindet sich aktiv nach außen. Keine eingehenden Verbindungen, keine offenen Ports, keine Kompromisse bei der Netzwerksicherheit.

No items found.
Foto von Michael
Michel W.

Mehr zum Thema

Pfeil nach rechts (Verlinkung)
No items found.
No items found.
No items found.
No items found.
Devware GmbH verpflichtet sich, Ihre Privatsphäre zu schützen. Wir benötigen Ihre Kontaktinformationen, um Sie bezüglich unserer Produkte und Dienstleistungen zu kontaktieren. Mit Klick auf Absenden geben Sie sich damit einverstanden. Weitere Informationen finden Sie unter Datenschutz. Ihre Daten behandeln wir vertraulich. Versprochen.
Vielen Dank für Ihr Vertrauen.
Unser Team prüft Ihre Anfrage sorgfältig und meldet sich in der Regel innerhalb von 48 Stunden bei Ihnen zurück.
Falls es besonders eilig ist, erreichen Sie uns auch telefonisch:
+ 49 (0) 202 478 269 0.
Da ist etwas schief gegangen beim Absenden des Formulars.