Zum Inhalt

Troubleshooting-How-tos

Schritt-für-Schritt-Anleitungen für Probleme, auf die das Plattform-Team in den pipelines, auf den Build-Agents und in der lokalen Entwicklung stößt. Für einen ersten Blick auf einen Incident siehe Wo du zuerst nachschaust. Für bekannte Defekte siehe Bekannte Probleme.

Problem Weiter zu
Ein plan oder apply schlägt fehl oder hängt ohne klaren Fehler Terraform-Logging einschalten
Build-Agents haben nach einem destroy ihre Route zu Azure DevOps verloren Defekte Agent-Konnektivität wiederherstellen
Pipeline-Jobs warten auf Agents, oder Agents registrieren sich nie Den agent pool mit den Smoke-Tests prüfen
Du erreichst von deinem Entwicklungsrechner keine privaten Ressourcen Private Ressourcen aus einem Dev Container erreichen

Terraform-Logging einschalten

Verwende das, wenn ein Terraform-plan- oder apply-Schritt ohne klaren Fehler fehlschlägt oder hängt, wenn du die API-Aufrufe sehen musst, die ein Provider macht, oder wenn der support nach Terraform-Debug-Logs fragt.

Terraform hat zwei Log-Einstellungen, die du getrennt einschalten kannst:

Einstellung Umgebungsvariable Was sie zeigt
Core-Log-Level TF_LOG_CORE Terraforms eigenes Log: Graph-Aufbau, Orchestrierung von plan und apply
Provider-Log-Level TF_LOG_PROVIDER Das Log der Provider-Plugins (zum Beispiel azurerm), einschließlich der Requests und Responses, die sie machen

Die Werte sind TRACE, DEBUG, INFO, WARN und ERROR. Ein pipeline-Parameter mit dem Wert DEFAULT lässt die Variable ungesetzt, sodass sich das Logging wie gewohnt verhält.

Beginne mit dem Core-Log auf DEBUG: Meist reicht es, um zu sehen, was Terraform tut. Verwende das Provider-Log nur kurz und nur für ein bestimmtes Problem.

Provider-Logs können Secrets enthalten

TF_LOG_PROVIDER auf DEBUG oder TRACE kann unmaskierte Secrets aus den Request- und Response-Payloads der Provider ausgeben. Stelle es wieder auf DEFAULT, sobald du hast, was du brauchst. Behandle die Logs dieses Laufs als sensibel: Beschränke den Zugriff und lösche sie, wenn sie nicht mehr gebraucht werden.

Azure DevOps

Die älteren Environment-pipelines (pipelines/environment_canary.yaml und environment_prod.yaml) stellen zwei Lauf-Parameter bereit, terraformLogCore und terraformLogProvider (Default DEFAULT):

  1. Öffne die pipeline und wähle Run pipeline.
  2. Setze in den Parametern Terraform core log level oder Terraform provider log level.
  3. Starte sie. Die plan- und apply-Jobs setzen TF_LOG_CORE und TF_LOG_PROVIDER für die Dauer des Laufs.

GitHub Actions

Die wiederverwendbaren Workflows environment_canary.yml und environment_prod.yml akzeptieren dieselben zwei Inputs. Übergib sie vom aufrufenden Workflow:

jobs:
  deploy:
    uses: <organisation>/<code repository>/.github/workflows/environment_canary.yml@<version>
    with:
      environment: canary
      regions: '["weu"]'
      deployList: ${{ inputs.deployList }}
      defaultRunnerGroup: <runner group>
      terraformLogCore: DEBUG
      terraformLogProvider: DEFAULT

Prüfe deine Entry-pipeline

Diese Parameter sind für die Environment-pipelines des Code-Repositorys dokumentiert. Öffne das Template, das deine Entry-pipeline (hub, spoke) aufruft, und suche nach terraformLogCore und terraformLogProvider. Wenn sie dort nicht sind, frage den Owner des Code-Repositorys, wie du Terraform-Logging für deine Version aktivierst.

Das Logging prüfen

Im Job-Log Terraform Plan oder Terraform Apply (Azure DevOps) oder in der Step-Ausgabe (GitHub) erscheinen Terraform-Logzeilen mit dem Level in Klammern, etwa [DEBUG], neben der normalen Ausgabe.

Wenn keine zusätzliche Ausgabe erscheint, sind beide Parameter noch DEFAULT, oder der Wert ist keines der obigen Level. Starte erneut mit einem expliziten Level.

Defekte Agent-Konnektivität nach einem destroy wiederherstellen

Das Problem

Wenn der connectivity-layer zerstört wird, wird die virtual hub connection zwischen dem virtuellen Netzwerk der Agents und dem hub entfernt. Wenn die Verbindung internet_security_enabled = true hat, läuft der gesamte Internet-Traffic der Agents über die hub- Firewall, und diese Route verschwindet mit der Verbindung. Die Agents verlieren dann ihren Weg zu Azure DevOps und melden keine Job-Ausgabe mehr, sodass der Lauf, der den destroy ausgeführt hat, zu hängen scheint.

Vorbeugen

Setze internet_security_enabled = false auf jeder hub connection, deren Netzwerk pipeline-Agents hostet. Die Agents erreichen dann Azure DevOps direkt über das Internet, unabhängig vom hub. In environments/<env>/<region>/connectivity.auto.tfvars:

virtual_hub_connections = {
  <connection name> = {
    internet_security_enabled = false   # agents use direct internet, not the hub firewall
    remote_virtual_network_id = "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Network/virtualNetworks/<agents vnet>"
    routings                  = null
    virtual_hub_id            = null
  }
}

Siehe auch die Einstellung für spokes in der Spoke-JSON-Referenz (InternetSecurityEnabled). Die Agents der Plattform selbst laufen im Netzwerk der Job runners, siehe Provisioning.

Ohne Terraform apply wiederherstellen

Wenn die Konnektivität bereits defekt ist, ändere die Verbindung direkt über die Azure REST API. Die Option az network vhub connection update --internet-security wird in älteren Azure-CLI-Versionen nicht unterstützt, und der Resource Provider akzeptiert kein PATCH auf hub connections. Lies die Verbindung, ändere sie und schreibe sie zurück (GET und PUT).

  1. Finde den virtual hub und seine resource group.

    az network vhub list `
      --subscription <connectivity-subscription-id> `
      --query "[].{name:name, rg:resourceGroup}" `
      -o table
    
  2. Prüfe, dass die Verbindung existiert.

    az network vhub connection list `
      --vhub-name <vhub-name> `
      --resource-group <resource-group> `
      --subscription <connectivity-subscription-id> `
      --query "[].{name:name, internetSecurity:properties.enableInternetSecurity}" `
      -o table
    
  3. Deaktiviere Internet Security mit GET und PUT.

    $url = "https://management.azure.com/subscriptions/<connectivity-subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Network/virtualHubs/<vhub-name>/hubVirtualNetworkConnections/<connection-name>?api-version=2023-09-01"
    
    $conn = az rest --method get --url $url | ConvertFrom-Json
    $conn.properties.enableInternetSecurity = $false
    az rest --method put --url $url --body ($conn | ConvertTo-Json -Depth 20 -Compress)
    
  4. Warte, bis das Update abgeschlossen ist.

    do {
        Start-Sleep -Seconds 10
        $state = az rest --method get --url $url | ConvertFrom-Json
        Write-Host "$(Get-Date -Format 'HH:mm:ss') - provisioningState: $($state.properties.provisioningState)"
    } until ($state.properties.provisioningState -ne 'Updating')
    
  5. Übernimm den Fix in den Code. Setze internet_security_enabled = false in der connectivity.auto.tfvars der betroffenen Region, damit das nächste apply deine Änderung nicht rückgängig macht (Änderungs-Workflow). Eine Änderung in Azure ohne passende Änderung im Code ist Drift (Incident-Management).

Den Managed DevOps Pool mit den Smoke-Tests prüfen

Die eigenen Job runners der Plattform laufen auf einem Managed DevOps Pool (MDP) (Provisioning). Die Smoke-Tests prüfen, dass der Pool korrekt deployt ist und VMs starten und Agents bei Azure DevOps registrieren kann. Führe sie nach jedem infrastructure-Deployment aus und wenn Jobs auf Agents warten.

Was sie prüfen

Bereich Prüfungen
Azure Resource Manager Der Pool existiert in der resource group. Sein Provisioning-Status ist Succeeded. Die maximale Concurrency liegt über 0. Die SKU ist gültig. Statusinformationen sind verfügbar (ein null-Status deutet auf Netzwerkprobleme)
Registrierung bei Azure DevOps Der Pool ist in der Organisation registriert, hat den Typ automation (managed pool) und ist als hosted markiert
Images Die erwarteten Images sind konfiguriert: Windows Server 2025 und 2022, Ubuntu 24.04 und 22.04 (jeweils als <image>/latest)
Agent-Konnektivität Agents lassen sich über die Azure-DevOps-API abfragen, und keiner steckt im Zustand never connected (ein Zeichen für ein fehlgeschlagenes Bootstrap)
Netzwerk Das Agent-Subnetz ist konfiguriert und referenziert. Das DevCenter-Projekt existiert und ist verknüpft. Das Organisationsprofil zeigt auf die richtige Azure-DevOps-Organisation. Parallelität ist konfiguriert

Ausführen

Die Smoke-Tests laufen automatisch nach Invoke-XebiaInfrastructure -Action Apply, das die infrastructure-pipeline aufruft. Um sie separat auszuführen, zum Beispiel bei der Fehlersuche, verwende den Dev Container (Die Tests lokal ausführen):

# In the dev container, from the root of the code repository
Import-Module ./src/Xebia.Mcp/Xebia.Mcp.psd1 -Force
Initialize-XebiaDotEnv
$config = Resolve-XebiaMcpConfiguration -Path $env:MCP_CONFIG
Test-XebiaMcpSmoke -Config $config

Du musst mit az login angemeldet sein und Zugriff auf die subscription des Pools haben, plus die Berechtigung Agent Pools (Read & manage) in Azure DevOps. Das Ergebnis wird auf der Konsole angezeigt und als NUnit-XML-Datei geschrieben (artifacts/testresults-smoke.xml), die eine pipeline veröffentlichen kann.

Für ausführliche Ausgabe setze vor dem Aufruf $VerbosePreference = 'Continue'.

Häufige Fehler

Symptom Ursache Lösung
statuses: null oder resources: [] Die Firewall blockiert den CIDR des Agent-Subnetzes Füge das Agent-Subnetz einer Firewall-IP-group mit Outbound-Allow-Regeln für Port 443 hinzu (und Port 80, wenn deine Agents einfaches HTTP brauchen). Prüfe ip_groups/ und firewall_rule_configuration/ der Region (Zugriff der Build-Agents)
agentConnectedTime: null und receiveTime: null Das NAT Gateway ist aus, oder die Firewall blockiert den ausgehenden Traffic der Agents Aktiviere das NAT Gateway auf dem Agent-Subnetz oder erlaube dem Agent-Subnetz auf der Firewall den Zugang zum Internet. Wenn das Netzwerk mit aktivierter Internet Security an den hub angebunden ist, läuft der gesamte Internet-Traffic über die Firewall, und das NAT Gateway wird umgangen
Agents starten, können aber Azure-Hostnamen nicht auflösen Eine Catch-all-Weiterleitungsregel (.) im DNS Resolver schickt jede Anfrage an einen öffentlichen DNS-Server. Azure-interne Namen, die der Pool verwendet, lassen sich nur über Azure DNS (168.63.129.16) auflösen Entferne die Catch-all-Regel. Erstelle nur Weiterleitungsregeln pro Domain, nie eine Catch-all-Regel (DNS-Design)
MDP quota preflight failed Nicht genug Quota für die SKU-Familie Fordere beim Azure support mehr Quota für Microsoft.DevOpsInfrastructure an

Weiter gehen

  • Lies die Pool-Ressource direkt mit az rest gegen Microsoft.DevOpsInfrastructure/pools/<pool> der resource group der Job runners (API-Version 2024-10-19).
  • Liste die Agents mit der Azure-DevOps-REST-API auf: Hole die Pool-ID aus _apis/distributedtask/pools?poolName=<pool>, lies dann _apis/distributedtask/pools/<id>/agents?includeCapabilities=true, mit einem Token für die Azure-DevOps-Ressource.
  • End-to-End-Test. Stelle eine kleine Test-pipeline in die Queue, die pro Image einen Job auf dem Pool ausführt. Jeder Job sollte innerhalb von 5 bis 10 Minuten einen Agent bekommen und abschließen, und der Agent sollte danach deallokiert werden.

Private Ressourcen aus einem Dev Container erreichen

Arbeit an spokes braucht oft Zugriff auf private Ressourcen (Key Vaults, Storage-Accounts) aus einem Entwicklungs-Container. Diese Anleitung verbindet einen Windows-Rechner mit WSL2 und Docker Desktop über Point-to-Site-VPN mit dem hub und korrigiert DNS im Container, damit private Namen aufgelöst werden.

Bevor du anfängst

  • Ein Windows-Host mit WSL2 und Docker Desktop und lokale Admin-Rechte, um den Azure VPN Client zu installieren.
  • Zugriff auf die subscription und resource group des Point-to-Site-VPN-Gateways und die Berechtigung, VPN-Client- Profile zu erzeugen.
  • Das Xebia.Mcp-Modul im Dev Container.
  • Ein Point-to-Site-VPN-Gateway im hub. Es ist optional, standardmäßig aus und wird mit einer Standardänderung eingerichtet (Point-to-Site-VPN aktivieren unten und Entwicklerzugriff).

1. Das Point-to-Site-Gateway prüfen

Öffne im Azure-Portal das Point-to-Site-VPN-Gateway in der connectivity resource group, dann Point-to-site configuration. Prüfe den Adresspool der Clients, die Authentifizierungsmethode (zum Beispiel Microsoft Entra ID oder Zertifikate) und den Tunneltyp. Wenn die Plattform-Automatisierung es konfiguriert hat, prüfe nur die Einstellungen.

2. Das VPN-Client-Profil herunterladen

Aus dem Portal: Wähle im Gateway Download VPN client und speichere die Zip-Datei. Oder aus dem Dev Container:

$profile = Invoke-XebiaAzureCli -ScriptBlock {
    az network p2s-vpn-gateway vpn-client generate `
    --subscription '<subscription-id>' `
    --resource-group '<resource-group-name>' `
    --name '<p2s-gateway-name>'
} -WhatIf:$false | ConvertFrom-Json

Invoke-WebRequest -Uri $profile.profileUrl -OutFile ./vpn-client-profile.zip
Expand-Archive -Path ./vpn-client-profile.zip -DestinationPath ./vpn-client-profile -Force

3. Den Azure VPN Client installieren und das Profil importieren

  1. Installiere den Azure VPN Client aus dem Microsoft Store oder über deine Software-Verteilung im Unternehmen.
  2. Starte ihn und wähle Import. Wähle die Zip-Datei oder navigiere zur entpackten AzureVPN/VpnSettings.xml.
  3. Wähle das importierte Profil und Connect. Melde dich an, wenn du gefragt wirst. Der Status muss Connected sein.

Schnelle Prüfungen unter Windows:

Resolve-DnsName '<private-resource-name>'
Test-NetConnection '<private-ip-or-fqdn>' -Port 443

Test-NetConnection bestätigt nur, dass TCP-Port 443 offen ist. Um zu bestätigen, dass der Endpoint das erwartete Service- Zertifikat präsentiert, führe die TLS-Prüfung unter Die Verbindung prüfen aus.

4. WSL-Networking konfigurieren

Aktualisiere WSL aus Windows PowerShell mit wsl --update und prüfe wsl --version. Erstelle oder bearbeite %UserProfile%\.wslconfig:

[wsl2]
networkingMode=mirrored
dnsTunneling=true
firewall=true
autoProxy=true

Wende es an: Schließe alle WSL-Terminals, führe wsl --shutdown in Windows PowerShell aus, verbinde den Azure VPN Client neu und öffne dann WSL und den Dev Container erneut.

5. Den Container auf den hub-DNS ausrichten

Wenn Namen im Container weiterhin zu öffentlichen Adressen aufgelöst werden, erzwinge vorübergehend den hub-DNS-Server:

Import-Module Xebia.Mcp -Force
Set-XebiaLocalDns -NameServer <hub DNS server IP>

Wenn du die Aufgabe beendest, stelle den ursprünglichen DNS wieder her:

Set-XebiaLocalDns -Restore

Set-XebiaLocalDns behält eine Backup-Datei von resolv.conf (/tmp/xebia-localdns-resolv.conf.bak). Ein erneuter Aufruf mit -NameServer, solange das Backup existiert, wird absichtlich blockiert: Stelle zuerst wieder her. -Restore stellt die Datei wieder her und löscht das Backup.

Die Verbindung prüfen

Führe diese Befehle im Dev Container aus, nachdem das VPN verbunden ist. Ersetze den Vault-Namen und die private IP durch die der Ressource, die du testest.

# 1. The name resolves to the private endpoint address
[System.Net.Dns]::GetHostAddresses('<vault-name>.vault.azure.net') | Select-Object -ExpandProperty IPAddressToString

# 2. TCP port 443 is open
$tcpClient = [System.Net.Sockets.TcpClient]::new(); $tcpClient.Connect('<private-ip>', 443); "TCP OK"; $tcpClient.Dispose()

# 3. The endpoint presents the expected certificate
$tcpClient = [System.Net.Sockets.TcpClient]::new('<private-ip>', 443)
$callback = [System.Net.Security.RemoteCertificateValidationCallback] { param($sender, $certificate, $chain, $errors) $true }
$sslStream = [System.Net.Security.SslStream]::new($tcpClient.GetStream(), $false, $callback)
$sslStream.AuthenticateAsClient('<vault-name>.vault.azure.net')
$sslStream.RemoteCertificate.Subject
$sslStream.RemoteCertificate.Issuer
$sslStream.Dispose(); $tcpClient.Dispose()
# 4. A real call
az keyvault secret list --vault-name <vault-name> --maxresults 1 -o table

Erwartet: Der Name wird zur privaten IP aufgelöst, TCP OK wird ausgegeben, ein von Microsoft ausgestelltes Zertifikat für vault.azure.net wird angezeigt, und der letzte Befehl schlägt nicht mit SSL: UNEXPECTED_EOF_WHILE_READING fehl. Ein leerer Vault liefert eine leere Ausgabe, die den Netzwerk- und TLS-Pfad trotzdem belegt. Wenn die TLS-Prüfung mit einem EOF fehlschlägt oder kein Zertifikat zurückgibt, zeigt DNS auf eine private IP, aber der Container erreicht den Private Endpoint nicht.

Probleme

Symptom Was zu tun ist
Das VPN ist verbunden, aber private Namen werden zu öffentlichen Adressen aufgelöst Prüfe, dass das Windows-VPN verbunden ist. Prüfe, dass mirrored Networking nach wsl --shutdown aktiv ist. Führe im Container Set-XebiaLocalDns -NameServer <hub DNS server IP> aus
Set-XebiaLocalDns meldet, dass das Backup bereits existiert Führe Set-XebiaLocalDns -Restore aus und setze dann den neuen Name Server
Keine Route vom Container zum privaten DNS-Server Prüfe, dass die Routen des VPN-Profils den hub und die privaten DNS-Bereiche enthalten. Verbinde das Profil neu. Starte WSL neu und öffne den Container erneut
DNS löst zum Private Endpoint auf und TCP 443 ist offen, aber TLS schlägt mit EOF fehl Siehe unten

TLS schlägt mit EOF fehl, während TCP gelingt, sowohl von Windows als auch aus dem Container. Das ist nicht nur ein WSL-Problem: Die TCP- Verbindung wird geöffnet, aber der TLS-Handshake mit dem Azure-Service wird nicht abgeschlossen. Prüfe in dieser Reihenfolge:

  1. Die IP des Private Endpoints überschneidet sich nicht mit einem lokalen oder Firmennetzwerk. Zum Beispiel steht ein lokales Netzwerk 192.168.123.0/24 im Konflikt mit einem Private-Endpoint-Bereich eines spokes darin.
  2. Das VPN-Profil routet den gesamten Adressbereich des virtuellen Netzwerks des spokes, nicht nur den Bereich des DNS-Servers.
  3. DNS löst den Namen sowohl von Windows als auch aus dem Container zur IP des Private Endpoints auf.
  4. Die Private-Endpoint-Verbindung ist genehmigt und zeigt auf die erwartete Ressource.
  5. Wenn es weiterhin fehlschlägt, eskaliere an den Netzwerk-Owner mit dem DNS-Ergebnis, der Quell-IP unter Windows, dem Namen des VPN-Interfaces, der IP des Private Endpoints und dem TLS-EOF-Fehler.

Das Point-to-Site-VPN-Feature aktivieren

Die Plattform kann im hub ein Point-to-Site-VPN-Gateway erstellen. Das ist eine Standardänderung: Plattform-Features aktualisieren (Plattform-Features). Die Schritte für dieses Feature:

  1. Erstelle im Konfigurations-Repository einen branch. Ändere in <env-folder>/<region>/connectivity.auto.tfvars der Umgebung:
    • Setze create_point_to_site_vpn_gateway = true.
    • Setze point_to_site_vpn_gateway_connection_configuration: einen name, der deiner Namenskonvention folgt (das ältere Runbook verwendet vpnconf-<company>-connectivity-<env>-<region>-001), und einen vpn_client_address_pool mit address_prefixes (der CIDR für die VPN-Clients) und internet_security_enabled (das ältere Runbook setzt true).
    • Setze entra_tenant_id auf die Tenant-ID der Organisation.
    • Setze point_to_site_vpn_enterprise_app_id auf die Application-ID der Azure-VPN-Anwendung (siehe den Hinweis unten).
  2. Prüfe die zugehörigen Variablen in variables_point_to_site_vpn_gateway.tf (der DNS-Server des Gateways muss virtual_network_dns_servers entsprechen, wie es ein Config-Test verlangt).
  3. Führe hub im Modus Plan für die geänderte Umgebung und Region aus. Nur der connectivity-layer sollte Änderungen zeigen. Wende auf canary an, dann auf prod, mit Genehmigung für prod.
  4. Prüfe im Portal: Virtual WAN > Hubs > der hub der Region > Point-to-site configuration. Lade die Konfigurationsdatei herunter und teste die Verbindung mit dem Azure VPN Client.
  5. Merge den pull request.

Um das VPN für die Entwicklung zu nutzen, mache weiter mit Private Ressourcen aus einem Dev Container erreichen. Zu Forced Tunnelling und zum Design des Zugriffs pro workload siehe Entwicklerzugriff.

Die Application-ID des Azure VPN

Das ältere Runbook verwendet 41b23e61-6c1e-4545-b367-cd054e0ed4b4 für point_to_site_vpn_enterprise_app_id. Prüfe sie anhand der aktuellen Dokumentation von Microsoft zu Azure VPN mit Entra-ID-Authentifizierung, bevor du sie verwendest, denn der Wert hängt von der Cloud ab.