Automation

Azure Automation : valider une migration de runtime avant de basculer les runbooks de production

Un runbook de production pour qualifier un nouvel environnement d'exécution Azure Automation avec versions, packages, Hybrid Workers, tests sans effet, canari, validation et rollback.

09 sept. 2026 azureazure-automationruntime-environmentpowershellmoduleshybrid-runbook-workermanaged-identityautomationobservabilitycanaryrunbookrollbackproduction

Un runbook Azure Automation fonctionne encore sous son environnement historique, mais l’équipe doit changer de version PowerShell ou mettre à jour le package Az. Le test manuel passe, puis le premier job de production échoue sur un module absent, un type sérialisé différemment ou une commande non disponible sur un Hybrid Runbook Worker. Revenir au code précédent ne suffit pas : le code n’a pas changé, c’est son environnement d’exécution qui a dérivé.

Le cas fil rouge est un runbook reconcile-private-dns-records exécuté chaque nuit. Il lit un inventaire, calcule un diff et met à jour uniquement les enregistrements approuvés. L’équipe veut le faire passer vers un environnement PowerShell récent sans transformer la migration en test grandeur nature. L’objectif est de construire un candidat immuable, de le tester avec les mêmes entrées et identités, de limiter le premier effet réel, puis de décider promotion, maintien ou rollback.

Traiter le runtime comme une dépendance versionnée

Un environnement d’exécution Azure Automation regroupe le langage, sa version et les packages requis par le runbook. Le changer peut modifier l’authentification, la résolution des modules, la sérialisation JSON, le comportement des exceptions et les commandes disponibles. Le contrat de migration doit donc décrire davantage que « PowerShell mis à jour ».

yaml runtime-migration-contract.yml
runbook: reconcile-private-dns-records
current_runtime: ps-old-approved
candidate_runtime: ps-candidate-2026-09
execution_targets:
- azure_sandbox
- hybrid_worker_group: hwg-network-prod
packages_to_freeze:
- Az
- Az.Accounts
- Az.PrivateDns
identity: aa-network-prod-managed-identity
inputs_fixture: fixtures/private-dns-small.json
allowed_canary_scope: privatelink.example.internal
success_signal: expected_diff_applied_and_second_run_empty
rollback: relink_runbook_to_ps-old-approved

Créez un environnement candidat au lieu de modifier celui qui porte déjà plusieurs runbooks. La version du langage est immuable, mais une mise à jour de packages sur un environnement partagé se propage à tous les runbooks qui lui sont liés. Un candidat séparé rend le diff, le canari et le retour arrière lisibles.

Inventorier les consommateurs avant le changement

Avant de toucher au lien du runbook, listez les environnements et les runbooks concernés. L’unité de risque n’est pas seulement le script : c’est l’ensemble des consommateurs d’un environnement partagé.

bash 01-runtime-inventory.sh
SUBSCRIPTION="<subscription-id>"
RG="rg-automation-prod"
ACCOUNT="aa-network-prod"
API="2024-10-23"
BASE="https://management.azure.com/subscriptions/$SUBSCRIPTION/resourceGroups/$RG/providers/Microsoft.Automation/automationAccounts/$ACCOUNT"

az rest --method get --url "$BASE/runtimeEnvironments?api-version=$API" --query "value[].{name:name, runtime:properties.runtime, defaultPackages:properties.defaultPackages}" --output json

az rest --method get --url "$BASE/runbooks?api-version=$API" --query "value[].{name:name, state:properties.state, type:properties.runbookType, runtime:properties.runtimeEnvironment, modified:properties.lastModifiedTime}" --output table

Conservez le nom exact de l’environnement actuel, les versions de packages et la liste des runbooks liés. Si le candidat est modifié pendant les tests, changez son nom ou sa version de manifeste : un résultat obtenu contre une cible mouvante n’est pas une preuve de promotion.

Construire une matrice de compatibilité utile

Le test doit couvrir les branches qui dépendent réellement du runtime. Un simple Get-Date vert ne valide ni l’identité ni les effets du runbook.

text runtime-compatibility-matrix.txt
Surface                 Preuve attendue
Langage                 Version exacte et edition de PowerShell
Packages                 Nom, version et etat de provisioning
Imports                  Chaque module requis se charge sans fallback implicite
Authentification         Principal, tenant et abonnement attendus
Lecture Azure            Inventaire retourne avec le meme scope
Serialisation            Fixture d'entree et diff normalise identiques
Gestion d'erreur         Refus attendu conserve code et message exploitables
Hybrid Worker            Binaire runtime present sur chaque worker cible
Effet canari             Une seule cible bornee, puis second run sans changement
Observabilite            Job ID, runtime, worker, correlation et resultat conserves

Ajoutez des tests négatifs : ressource hors scope, package volontairement absent dans un environnement de test, identité sans droit d’écriture et entrée invalide. Une migration est sûre quand le candidat réussit les actions autorisées et continue de refuser celles qui ne le sont pas.

Tester le candidat sans rebascule

Azure Automation permet de lancer le brouillon avec un environnement d’exécution différent avant de modifier le lien publié. Utilisez une fixture sans effet ou un mode WhatIf réellement implémenté par le runbook.

bash 02-test-candidate-runtime.sh
SUBSCRIPTION="<subscription-id>"
RG="rg-automation-prod"
ACCOUNT="aa-network-prod"
RUNBOOK="reconcile-private-dns-records"
CANDIDATE="ps-candidate-2026-09"
API="2024-10-23"
TEST_URL="https://management.azure.com/subscriptions/$SUBSCRIPTION/resourceGroups/$RG/providers/Microsoft.Automation/automationAccounts/$ACCOUNT/runbooks/$RUNBOOK/draft/testJob?api-version=$API"

az rest --method put --url "$TEST_URL" --headers "Content-Type=application/json" --body "{"properties":{"runtimeEnvironment":"$CANDIDATE","runOn":""}}"

Le Test pane ne doit pas devenir un contournement des contrôles de production. Utilisez une identité et des entrées bornées, capturez les streams, et ne placez aucun secret dans la sortie. Pour un runbook qui ne sait pas simuler ses écritures, créez d’abord une cible canari isolée.

Qualifier chaque Hybrid Worker

Un même environnement logique ne garantit pas que tous les workers possèdent le bon exécutable ou les mêmes dépendances locales. Pour PowerShell 7.4 sur Hybrid Worker, le binaire doit être installé et son chemin déclaré sur la machine. Vérifiez chaque membre du groupe avant d’autoriser la distribution du job.

powershell 03-hybrid-worker-runtime-check.ps1
$expectedMajor = 7
$expectedMinor = 4
$runtimePath = [Environment]::GetEnvironmentVariable(
'powershell_7_4_path',
'Machine'
)

[pscustomobject]@{
ComputerName = $env:COMPUTERNAME
RuntimePath = $runtimePath
PathExists = Test-Path $runtimePath
CurrentVersion = $PSVersionTable.PSVersion.ToString()
Compatible = (
  $PSVersionTable.PSVersion.Major -eq $expectedMajor -and
  $PSVersionTable.PSVersion.Minor -eq $expectedMinor
)
} | ConvertTo-Json -Compress

Get-Module -ListAvailable Az.Accounts, Az.PrivateDns |
Select-Object Name, Version, Path |
ConvertTo-Json -Compress

Exécutez ce contrôle comme un job sans effet sur chaque worker ciblé. Si un worker diverge, retirez-le du groupe ou corrigez-le avant le canari. Ne comptez pas sur l’ordonnanceur pour choisir spontanément la bonne machine.

Comparer des sorties normalisées

Les deux runtimes peuvent produire des objets techniquement différents tout en représentant le même état. Comparez une sortie normalisée : identifiants, propriétés métier triées, diff proposé et codes d’erreur. Ignorez les timestamps de job et l’ordre non contractuel des propriétés.

json runtime-comparison-result.json
{
"fixture": "private-dns-small-v3",
"oldRuntime": {
  "readCount": 12,
  "proposedChanges": 1,
  "deniedOutOfScope": true
},
"candidateRuntime": {
  "readCount": 12,
  "proposedChanges": 1,
  "deniedOutOfScope": true
},
"normalizedDiff": [],
"decision": "eligible_for_bounded_canary"
}

Tout écart doit être expliqué avant la promotion. Une propriété absente peut révéler un changement de module ; un nouvel ordre de résultats peut casser une logique non déterministe ; un message d’erreur différent peut rendre une alerte ou un parseur inopérant.

Basculer un seul runbook et limiter le premier effet

Quand les tests sans effet sont verts, reliez uniquement le runbook canari au candidat. Une exécution déjà en cours n’est pas modifiée par la nouvelle association ; la validation porte sur les jobs lancés après la bascule.

bash 04-cutover-one-runbook.sh
SUBSCRIPTION="<subscription-id>"
RG="rg-automation-prod"
ACCOUNT="aa-network-prod"
RUNBOOK="reconcile-private-dns-records"
CANDIDATE="ps-candidate-2026-09"
API="2024-10-23"
RUNBOOK_URL="https://management.azure.com/subscriptions/$SUBSCRIPTION/resourceGroups/$RG/providers/Microsoft.Automation/automationAccounts/$ACCOUNT/runbooks/$RUNBOOK?api-version=$API"

az rest --method patch --url "$RUNBOOK_URL" --headers "Content-Type=application/json" --body "{"properties":{"type":"PowerShell","runtimeEnvironment":"$CANDIDATE"}}"

Le premier job réel doit utiliser un scope réduit, une clé d’idempotence et une validation métier. Pour le cas DNS, limitez la cible à une zone et un record contrôlés. Exécutez ensuite le même job une seconde fois : l’absence de nouveau diff prouve davantage que le seul statut Completed.

Tracer le runtime réellement exécuté

Ajoutez au début du runbook une enveloppe de preuve qui ne contient ni token ni secret. Elle doit permettre de relier la décision de migration au job observé.

powershell 05-runtime-evidence.ps1
$evidence = [ordered]@{
correlationId = [guid]::NewGuid().ToString()
runbook = 'reconcile-private-dns-records'
runtime = $PSVersionTable.PSVersion.ToString()
edition = $PSVersionTable.PSEdition
worker = $env:COMPUTERNAME
azAccounts = (Get-Module -ListAvailable Az.Accounts |
  Sort-Object Version -Descending |
  Select-Object -First 1 -ExpandProperty Version).ToString()
startedAtUtc = [DateTime]::UtcNow.ToString('o')
}

Write-Output ($evidence | ConvertTo-Json -Compress)

Corrélez cette enveloppe avec le Job ID, l’identité visible dans Azure Activity et le signal applicatif. Un job vert sous un runtime inconnu ou exécuté sur un worker non identifié ne clôt pas la migration.

Décider promotion, maintien ou rollback

text runtime-migration-decision.txt
Promouvoir progressivement
Runtime et packages du candidat sont figes
Tests positifs et negatifs sont conformes
Chaque Hybrid Worker cible est qualifie
Le canari applique uniquement le diff attendu
Le second run est sans effet
Traces techniques et validation metier concordent

Maintenir le runtime actuel
Le candidat fonctionne mais un package reste non maitrise
Les sorties different sans explication
Un worker du groupe n'est pas qualifie
Le test ne couvre pas une branche avec effet externe

Rollbacker immediatement
Identite, scope ou refus attendus ont change
Le canari touche une cible hors contrat
Une dependance locale manque sur un worker
Les traces ne permettent pas d'attribuer l'effet
Le second run reproduit une ecriture

Le rollback consiste à relier le runbook à l’environnement précédent, puis à lancer un contrôle de lecture et à traiter séparément tout effet déjà appliqué. Ne supprimez pas immédiatement le candidat : conservez son manifeste et ses sorties pour expliquer l’échec. La suppression d’un environnement d’exécution n’est pas un mécanisme de retour arrière.

Conclusion

Une migration de runtime Azure Automation est une release de production, même lorsque le script reste identique. Le périmètre utile est le triplet code, environnement et cible d’exécution, complété par l’identité et le signal métier.

La décision devient alors simple à défendre : promouvoir si le candidat est figé, testé sur les refus comme sur les succès, qualifié sur chaque worker et validé par un canari idempotent ; maintenir si une preuve manque ; relier immédiatement l’ancien environnement si le scope, l’identité ou l’effet dérive. Le runtime redevient ainsi une dépendance exploitable au lieu d’un contexte implicite découvert pendant l’incident.