Automation

Azure DevOps : diagnostiquer un workspace contaminé avant de recréer un agent auto-hébergé

Un runbook de production pour distinguer sources, outputs, caches et état machine persistants sur un agent Azure DevOps auto-hébergé avant nettoyage, quarantaine ou recréation.

04 sept. 2026 azure-devopspipelinesself-hosted-agentworkspacecacheci-cdautomationdevopsobservabilitysecurityrunbookrollbackproduction

Un pipeline Azure DevOps réussit sur un agent, échoue sur un autre, puis redevient vert après une relance sans changement de commit. Le symptôme peut être un binaire obsolète, un fichier généré encore présent, une dépendance non restaurée, un cache incohérent ou une configuration laissée par le job précédent. Recréer l’agent semble rapide, mais efface précisément l’état qui permettrait de comprendre l’incident.

Le cas d’usage est un pool auto-hébergé qui exécute des builds et des déploiements privés. Contrairement à un agent hébergé Microsoft renouvelé à chaque job, son répertoire de travail et son état machine peuvent survivre entre les runs. L’objectif du runbook est de décider s’il faut nettoyer les sources, les outputs ou tout le workspace, corriger le pipeline, mettre l’agent en quarantaine ou le reconstruire depuis une image connue.

Figer une paire de runs comparable

Commencez par un run sain et un run en échec qui devraient produire le même résultat. Le numéro du pool ne suffit pas : relevez l’agent, le commit, les paramètres, les artefacts entrants et les versions d’outils.

text workspace-incident-scope.txt
Incident: inc-20260904-002
Pipeline: build-orders-api
Pool: private-linux-prod
Run sain: 20260904.17 / agent ci-linux-02
Run en échec: 20260904.18 / agent ci-linux-04
Commit attendu: 7d0d0d0
Mode: build puis publication d'un artefact immuable

Comparer
Agent.Name, Agent.Version et Agent.OS
Pipeline.Workspace et Build.SourcesDirectory
commit réellement checkouté et sous-modules
versions runtime, SDK, package manager et outils
empreinte des inputs et de l'artefact produit
fichiers présents avant restauration et avant build
variables non secrètes qui pilotent le build

Interdire pendant la collecte
relance en boucle sur le même agent
suppression manuelle de tout le répertoire de travail
mise à jour opportuniste des dépendances
réutilisation d'un artefact dont la provenance est inconnue

Deux runs ne sont comparables que si leurs entrées le sont. Si le pipeline reconstruit depuis une branche mobile, télécharge latest ou résout des dépendances sans lockfile, commencez par stabiliser ces entrées.

Localiser la frontière de persistance

Sur un agent auto-hébergé, le workspace contient plusieurs zones qui n’ont pas le même cycle de vie. Les sources se trouvent généralement sous s, les binaires sous b, les artefacts préparés sous a et les résultats de test dans TestResults. Les deux dernières zones sont nettoyées par défaut entre les runs, mais il ne faut pas en déduire que les sources et outputs le sont aussi.

bash 01-workspace-inventory.sh
set -eu

printf 'agent=%s version=%s os=%s
' "${AGENT_NAME:-unknown}" "${AGENT_VERSION:-unknown}" "${AGENT_OS:-unknown}"

printf 'workspace=%s
sources=%s
binaries=%s
' "${PIPELINE_WORKSPACE:-unknown}" "${BUILD_SOURCESDIRECTORY:-unknown}" "${BUILD_BINARIESDIRECTORY:-unknown}"

git -C "$BUILD_SOURCESDIRECTORY" rev-parse HEAD
git -C "$BUILD_SOURCESDIRECTORY" status --short --untracked-files=all

find "$PIPELINE_WORKSPACE" -maxdepth 2 -type f -printf '%TY-%Tm-%TdT%TH:%TM:%TSZ %s %p
' | sort | tail -n 200

Cet inventaire doit rester sans secret. Ne publiez ni contenu de fichiers de configuration, ni variables, ni credentials persistés. Les chemins, dates, tailles et empreintes suffisent généralement pour prouver qu’un résidu existe.

Séparer quatre classes de contamination

Un git clean ne corrige pas tous les états persistants. Classez le résidu avant de choisir le nettoyage.

text workspace-contamination-classes.txt
Sources Git
fichier non suivi ou ignore encore present
sous-module sur une mauvaise revision
Git LFS incomplet
configuration Git locale modifiee

Outputs du build
binaire compile depuis un ancien commit
dossier dist, bin ou obj reutilise sans preuve
manifeste genere avant la restauration des dependances

Caches explicites
cle de cache trop large ou sans lockfile
cache partage entre branches, architectures ou versions runtime
contenu restaure mais jamais valide

Etat hors workspace
package installe globalement
credential ou configuration dans le home du compte agent
conteneur, volume, processus ou service laisse actif
plusieurs agents partageant le meme work directory

Le dernier groupe est le plus trompeur : workspace.clean: all ne nettoie pas le home utilisateur, un daemon, un volume Docker ou un outil installé sur la machine. Si le canari propre échoue encore, élargissez le diagnostic à l’image et au compte de service au lieu d’empiler des commandes de suppression.

Prouver l’hypothèse avec un canari propre

Créez un run de diagnostic sur un agent isolé ou retiré temporairement du trafic normal. Le canari doit garder les mêmes entrées que le run en échec et ne modifier que la politique de nettoyage.

yaml azure-pipelines-clean-canary.yml
jobs:
- job: clean_workspace_canary
displayName: Clean workspace canary
pool:
  name: private-linux-prod
  demands:
  - Agent.Name -equals ci-linux-canary
workspace:
  clean: all
steps:
- checkout: self
  clean: true
  fetchDepth: 0

- bash: |
    set -eu
    test -z "$(git status --porcelain --untracked-files=all)"
    git rev-parse HEAD
    sha256sum package-lock.json
  displayName: Prove clean inputs

- script: npm ci
  displayName: Restore from lockfile

- script: npm run build
  displayName: Build candidate

- bash: |
    set -eu
    find dist -type f -print0 | sort -z | xargs -0 sha256sum > artifact.sha256
  displayName: Fingerprint outputs

checkout.clean: true remet le dépôt Git dans un état propre avant le fetch. workspace.clean: all supprime le workspace du pipeline avant le job. Utilisés ensemble dans le canari, ils permettent de tester l’hypothèse d’un résidu local, mais ils ne doivent pas devenir une correction permanente sans comprendre le coût et la source de la contamination.

Corriger la propriété de l’état

Le pipeline ne doit pas dépendre du fait de retomber sur le même agent. Tout état nécessaire à un job suivant doit être publié comme artefact, restauré par un cache explicitement versionné ou reconstruit depuis des entrées verrouillées.

yaml workspace-policy.yml
workspace_policy:
sources:
  owner: checkout
  validation: exact commit and clean status
build_outputs:
  owner: current run
  reuse: forbidden unless artifact digest is verified
dependency_cache:
  key_parts:
    - operating_system
    - architecture
    - runtime_version
    - lockfile_hash
  fallback: clean restore
machine_state:
  owner: agent image
  drift_detection:
    - agent version
    - tool manifest
    - running services
    - container and volume inventory
secrets:
  persistence: forbidden
  validation: no credential file after job

Choisissez ensuite le nettoyage minimal. resources cible les sources, outputs les binaires, et all l’ensemble du workspace. Pour une toolchain qui écrit dans le home ou lance des services, la correction appartient au script, au conteneur de job ou à l’image de l’agent, pas au checkout.

Valider sans masquer la récidive

Exécutez d’abord le canari propre, puis deux runs consécutifs sur le même agent et enfin un run sur un autre agent du pool. Cette séquence vérifie à la fois la reproductibilité et l’absence de dépendance cachée à une machine.

text workspace-validation-gates.txt
Promouvoir la correction
meme commit et memes inputs sur les trois runs
artefact final identique ou differences expliquees
aucun fichier canari du run precedent
aucune credential persistante
temps de build et volume de cache acceptables
diagnostic disponible si le probleme revient

Mettre l'agent en quarantaine
le run propre echoue uniquement sur cet agent
toolchain, service ou stockage local a derive
l'etat ne peut pas etre inspecte sans risque

Reconstruire l'agent
preuve collectee et cause rattachee a l'image
image connue, manifeste d'outils et procedure d'enrolement disponibles
canari negatif puis positif prevu avant retour dans le pool

Rollback
retirer la nouvelle politique si elle supprime un cache requis
restaurer le YAML precedent depuis le controle de version
maintenir l'agent suspect hors pool
revenir a la derniere image d'agent validee

Le rollback ne consiste pas à réintroduire un workspace contaminé. Il restaure la politique de pipeline précédente tout en gardant l’agent suspect hors production, le temps de corriger la propriété de l’état.

Conclusion

Un agent Azure DevOps auto-hébergé apporte des caches, un accès réseau privé et une toolchain maîtrisée, mais cette persistance doit rester explicable. Quand deux runs identiques divergent, il faut figer les entrées, localiser le résidu, tester un canari propre et comparer plusieurs agents avant de recréer la machine.

La décision finale devient alors précise : nettoyer une zone définie, corriger le cache ou le build, mettre un agent en quarantaine, reconstruire une image connue, ou revenir à la politique précédente. Le pipeline retrouve une propriété essentielle : le résultat dépend des entrées déclarées, pas de l’historique invisible du worker.