Automation

Azure Automation : valider un runbook planifié avant qu'il change la production

Un runbook de production pour qualifier un job Azure Automation planifié avec déclencheur, identité managée, paramètres, dry-run, logs, validation et rollback avant d'autoriser une action réelle.

03 juil. 2026 azureazure-automationrunbookmanaged-identityscheduleautomationobservabilitykqlguardrailsrollbackproduction

Un runbook Azure Automation planifié devient vite une zone grise. Il part d’une bonne intention : nettoyer une ressource, relancer un traitement bloqué, corriger une configuration, appliquer une hygiène de nuit ou préparer une opération récurrente. Mais une fois branché à un horaire, avec une identité managée et des droits Azure, il peut modifier la production sans qu’un opérateur soit devant l’écran.

Le cas d’usage est un runbook aa-prod-maintenance appelé chaque nuit pour traiter des ressources applicatives dans rg-app-prod. Il doit lire l’état, corriger uniquement les objets marqués comme éligibles, publier une trace exploitable et s’arrêter si le périmètre dépasse ce qui était prévu. L’objectif n’est pas d’écrire un script parfait. Il est de prouver que le déclencheur, l’identité, les paramètres, les logs et le rollback sont sous contrôle avant d’activer l’horaire de production.

Cadrer le job comme une action de production

Un runbook planifié n’est pas seulement un bout de PowerShell ou de Python. C’est une action de production avec un déclencheur, une identité, un périmètre, des effets attendus et des modes d’échec. Commencez par écrire ce contrat avant de discuter du code.

text scheduled-runbook-contract.txt
Runbook
Name: aa-prod-maintenance
Environment: production
Trigger: schedule nightly-maintenance-prod
Identity: mi-aa-prod-maintenance
Target scope: rg-app-prod only
Allowed action: update resources already tagged maintenance=approved
Forbidden action: create broad role assignment, delete resource, change network exposure

Expected evidence
Input parameters
Target resources selected
Dry-run diff
Real action result
Correlation id
Rollback instruction
Operator handover note

Ce cadrage évite une erreur classique : valider le script seul alors que le risque vient du mode d’exécution. Un runbook manuel exécuté par un administrateur n’a pas le même profil de risque qu’un job planifié avec une identité persistante.

Séparer déclencheur, identité et cible

Trois contrôles doivent rester séparés. Le schedule décide quand le job part. L’identité décide ce qu’il peut faire. Les paramètres et filtres décident ce qu’il va toucher. Si ces couches se compensent entre elles, le rollback devient confus.

text automation-control-layers.txt
Trigger
Schedule name and timezone
Disabled by default until validation
One production schedule per environment
No webhook public without explicit justification

Identity
Managed identity dedicated to the runbook family
Role assignments scoped to resource group or resource
No inherited broad contributor rights at subscription level
Sign-in and activity logs reviewed during test

Targeting
Explicit subscription and resource group
Required tag or allowlist
Maximum number of resources per run
Stop when the selection is empty or unexpectedly large

Le bon test consiste à casser volontairement un de ces trois étages. Si l’identité a trop de droits, si le filtre sélectionne trop large ou si le schedule pointe vers le mauvais environnement, le job doit échouer proprement avant l’action.

Exécuter un dry-run qui produit une preuve

Le dry-run ne doit pas être un commentaire dans le code. Il doit produire un artefact relisible : ressources trouvées, action qui aurait été appliquée, raison de sélection, raison d’exclusion et conditions de blocage.

powershell 01-runbook-dry-run.ps1
param(
[string]$SubscriptionId,
[string]$ResourceGroupName,
[switch]$DryRun = $true,
[int]$MaxTargets = 10
)

Set-AzContext -SubscriptionId $SubscriptionId | Out-Null

$targets = Get-AzResource -ResourceGroupName $ResourceGroupName |
Where-Object { $_.Tags["maintenance"] -eq "approved" }

if ($targets.Count -eq 0) {
Write-Output "decision=stop reason=no-approved-target"
exit 0
}

if ($targets.Count -gt $MaxTargets) {
throw "Guardrail blocked run: selected $($targets.Count) targets, max is $MaxTargets"
}

$targets | Select-Object Name, ResourceType, ResourceGroupName, Tags |
ConvertTo-Json -Depth 5 |
Write-Output

if ($DryRun) {
Write-Output "decision=preview-only action=none"
exit 0
}

Write-Output "decision=apply targetCount=$($targets.Count)"

Le dry-run doit être exécuté avec la même identité que le job final. Sinon, vous validez seulement la logique depuis un poste administrateur, pas le comportement réel du runbook planifié.

Lire les logs comme un journal de décision

Azure Automation produit des jobs, des streams et des erreurs. Ils doivent suffire à reconstituer la décision sans ouvrir le code. Ajoutez un identifiant de corrélation et des messages courts : périmètre, nombre de cibles, mode dry-run ou apply, blocage, résultat.

kusto 02-automation-job-evidence.kql
let RunbookName = "aa-prod-maintenance";
let WindowStart = datetime(2026-07-03 00:00:00);
let WindowEnd = datetime(2026-07-03 02:00:00);
AzureDiagnostics
| where TimeGenerated between (WindowStart .. WindowEnd)
| where Category has_any ("JobLogs", "JobStreams")
| where RunbookName_s == RunbookName
| project TimeGenerated, RunbookName_s, JobId_g, StreamType_s, ResultDescription
| order by TimeGenerated asc

La requête exacte dépend de la configuration Diagnostic Settings et de la destination Log Analytics. Le point important est le niveau de preuve attendu : un reviewer doit voir si le job a sélectionné les bonnes ressources, si le garde-fou a bloqué l’action ou si le runbook a appliqué une modification.

Vérifier l’identité réelle avant d’élargir les droits

Un échec de runbook est souvent traité par ajout rapide de droits. C’est dangereux si l’équipe ne sait pas quelle identité a vraiment appelé Azure Resource Manager. Avant d’élargir un rôle, reliez le job à son identité managée, puis lisez les journaux d’activité.

bash 03-check-automation-identity.sh
AUTOMATION_ACCOUNT="aa-prod-ops"
RESOURCE_GROUP="rg-automation-prod"

az automation account show --name "$AUTOMATION_ACCOUNT" --resource-group "$RESOURCE_GROUP" --query "{name:name,principalId:identity.principalId,type:identity.type}" --output json

az role assignment list --assignee "<managed-identity-principal-id>" --all --query "[].{role:roleDefinitionName,scope:scope}" --output table

La décision saine n’est pas “mettre Contributor”. Elle consiste à réduire le rôle au scope utile, puis à rejouer le dry-run. Si le dry-run ne peut pas lister les ressources attendues avec le rôle cible, le problème est un contrat d’autorisation, pas un incident à contourner.

Ajouter un interrupteur d’arrêt simple

Un job planifié doit pouvoir être neutralisé sans supprimer le runbook. Le rollback opérationnel le plus rapide est souvent de désactiver le schedule, de forcer DryRun=true, ou de retirer temporairement le rôle de l’identité managée.

text scheduled-runbook-stop-card.txt
Stop card
Disable schedule nightly-maintenance-prod
Set runbook parameter DryRun=true
Keep the runbook code deployed for investigation
Remove write role from mi-aa-prod-maintenance if action risk remains
Preserve last job logs and input parameters
Open manual procedure for the affected maintenance action

Validation after stop
No new job starts from the schedule
Existing running job is completed, stopped or suspended intentionally
Activity Log shows no further write from the managed identity
Operators know the manual fallback

Évitez les rollbacks qui effacent la preuve. Supprimer le runbook ou l’identité peut rendre l’incident plus difficile à comprendre. Il vaut mieux suspendre l’action, conserver les traces et réduire les droits.

Décider l’activation du schedule

L’activation du schedule doit être une décision, pas la dernière ligne du déploiement. Le runbook peut être publié sans être planifié. L’horaire de production ne doit être activé que lorsque le contrat, le dry-run, l’identité et le rollback sont validés ensemble.

text schedule-go-no-go.txt
Go
The schedule targets the right timezone and environment
Dry-run ran with the production managed identity
Target selection is bounded by tag or allowlist
Maximum target count guardrail is enforced
Logs are visible from the operations workspace
Stop card has been tested

No-go
Identity has broad subscription-level rights
Dry-run was executed from an administrator workstation only
Targeting depends on naming convention alone
Logs are missing or not correlated to the job
Rollback requires deleting the automation account
Operators cannot explain what the next scheduled run will touch

Ce go/no-go peut être attaché au ticket de changement. Il transforme une automation “qui devrait marcher” en opération vérifiable.

Conclusion

Un runbook Azure Automation planifié doit être exploité comme une action de production autonome. Le code compte, mais il ne suffit pas. La validation doit couvrir le schedule, l’identité managée, le périmètre de ressources, le dry-run, les logs, le garde-fou de volume et le chemin d’arrêt.

La bonne décision est simple : activer le schedule seulement si une exécution de preuve montre ce qui sera touché et comment revenir en arrière. Sinon, le runbook peut rester publié, mais il doit rester en dry-run ou désactivé jusqu’à ce que l’action soit observable, bornée et réversible.