Automation
Azure Policy : diagnostiquer une exemption expirée avant de désactiver l'assignment
Un runbook de production pour relier expiration, scope, assignment et conformité avant de renouveler une exemption Azure Policy ou de désactiver la garde.
Un pipeline Azure déployait encore hier. Aujourd’hui, le même artefact échoue avec RequestDisallowedByPolicy. L’assignment n’a pas été modifié et l’exemption apparaît toujours dans Azure. Sous pression, deux actions semblent rapides : prolonger sa date sans autre contrôle, ou passer l’assignment en DoNotEnforce pour débloquer la production.
Le piège vient du cycle de vie de l’exemption. Une exemption arrivée à expiresOn n’est plus honorée, mais son objet reste présent pour conserver l’historique. Son simple affichage ne prouve donc pas qu’elle protège encore la ressource. Ce runbook vise une décision plus précise : corriger la ressource, renouveler temporairement l’exemption, réparer son scope ou ses références, ou maintenir le blocage sans affaiblir toute la garde.
Figer le premier refus et le contrat attendu
Commencez par le premier déploiement refusé, pas par le dernier retry. Conservez le commit, l’artefact, l’heure UTC, la ressource, l’opération ARM, le correlation ID et l’erreur complète. Reconstituez ensuite le contrat de l’exemption tel qu’il avait été approuvé.
incident:
pipeline_run: deploy-prod-20261004.3
commit: 8d2c1f4
first_deny_utc: 2026-10-04T05:47:12Z
target_resource: /subscriptions/<sub>/resourceGroups/rg-prod-app/providers/Microsoft.Web/sites/orders-api
operation: Microsoft.Web/sites/write
error: RequestDisallowedByPolicy
correlation_id: <correlation-id>
expected_exemption:
name: orders-api-diagnostics-waiver
assignment: deny-missing-diagnostic-settings
category: Waiver
owner: platform-operations
approved_ticket: CHG-1842
expected_expiry_utc: 2026-10-04T05:30:00Z
exit_condition: deploy diagnostic settings from the application template Si personne ne peut nommer le risque accepté et la condition de sortie, le renouvellement n’est pas une restauration technique. C’est une nouvelle décision de risque qui demande un propriétaire.
Prouver l’état effectif de l’exemption
Lisez l’objet à son scope exact. Capturez son expiresOn, sa catégorie, son assignment, ses références de définition, ses sélecteurs et ses métadonnées. Comparez la date en UTC à l’heure du premier refus.
SCOPE="/subscriptions/<sub>/resourceGroups/rg-prod-app/providers/Microsoft.Web/sites/orders-api"
EXEMPTION="orders-api-diagnostics-waiver"
az policy exemption show --name "$EXEMPTION" --scope "$SCOPE" --query '{id:id,name:name,assignment:policyAssignmentId,category:exemptionCategory,expiresOn:expiresOn,references:policyDefinitionReferenceIds,selectors:resourceSelectors,metadata:metadata,systemData:systemData}' --output jsonc
date -u +'%Y-%m-%dT%H:%M:%SZ' Une date dépassée explique pourquoi l’exemption n’est plus appliquée ; elle ne prouve pas encore que le deny actuel correspond au risque autrefois accepté. Si expiresOn est absent ou futur, cherchez plutôt un mauvais scope, une autre assignment, une référence d’initiative qui a changé ou une ressource enfant non couverte.
Ne recréez pas immédiatement l’exemption sous un nom neuf. Vous perdriez le lien direct avec l’approbation, l’historique et la date qui expliquent l’incident.
Relier le deny à l’assignment exacte
L’erreur ARM doit fournir l’identifiant de l’assignment et, pour une initiative, le policyDefinitionReferenceId. Comparez ces valeurs à l’exemption conservée. Un nom lisible identique n’est pas une preuve : les resource IDs complets doivent correspondre.
ASSIGNMENT_ID="/providers/Microsoft.Management/managementGroups/mg-prod/providers/Microsoft.Authorization/policyAssignments/landing-zone-guardrails"
az policy assignment show --ids "$ASSIGNMENT_ID" --query '{id:id,scope:scope,enforcementMode:enforcementMode,definition:policyDefinitionId,parameters:parameters,notScopes:notScopes}' --output jsonc
DEFINITION_ID=$(az policy assignment show --ids "$ASSIGNMENT_ID" --query policyDefinitionId -o tsv)
az policy set-definition show --id "$DEFINITION_ID" --query 'policyDefinitions[].{reference:policyDefinitionReferenceId,definition:policyDefinitionId,parameters:parameters}' --output table Si l’initiative a remplacé une référence, l’ancienne exemption peut rester lisible sans couvrir la nouvelle règle. Si l’assignment a été recréée, son ID peut avoir changé. Si le scope de l’exemption est le resource group, vérifiez que la ressource refusée est bien sous cette hiérarchie. Corrigez l’écart prouvé ; n’élargissez pas le scope pour faire disparaître l’erreur.
Vérifier que la non-conformité est toujours la même
Une exemption expirée peut coïncider avec une vraie dérive. Exportez l’état actuel de la ressource et comparez-le à la condition Policy. Dans le cas fil rouge, l’exemption acceptait temporairement l’absence de diagnostic settings pendant qu’un template était corrigé. Si le template n’a jamais été corrigé, l’expiration fonctionne comme prévu. Si les diagnostics existent, le deny peut venir d’un alias, d’un paramètre ou d’une initiative modifiée.
RESOURCE_ID="$SCOPE"
az policy state list --resource "$RESOURCE_ID" --query "[?policyAssignmentId=='$ASSIGNMENT_ID'].{timestamp:timestamp,state:complianceState,assignment:policyAssignmentName,definition:policyDefinitionName,reference:policyDefinitionReferenceId,effect:policyDefinitionAction}" --output table
az resource show --ids "$RESOURCE_ID" --query '{id:id,type:type,location:location,tags:tags,properties:properties}' --output json > resource-effective-state.json L’état de conformité peut arriver après le refus transactionnel. Utilisez-le pour confirmer le contexte, sans attendre indéfiniment qu’une vue converge. Le message du déploiement, l’assignment effective et l’état réel de la ressource forment la preuve principale.
Reconstituer la chronologie sans confondre expiration et suppression
L’expiration ne supprime pas l’objet. Cherchez donc séparément les écritures sur l’exemption, les changements d’assignment et les déploiements refusés. Une chronologie simple permet de distinguer une échéance normale d’une modification de gouvernance non annoncée.
let StartTime = datetime(2026-10-03T18:00:00Z);
let EndTime = datetime(2026-10-04T07:00:00Z);
AzureActivity
| where TimeGenerated between (StartTime .. EndTime)
| where ResourceProviderValue =~ "MICROSOFT.AUTHORIZATION"
or Properties has "RequestDisallowedByPolicy"
| where ResourceId has "policyExemptions"
or ResourceId has "policyAssignments"
or Properties has "policyAssignmentId"
| project TimeGenerated,
OperationNameValue,
ActivityStatusValue,
Caller,
ResourceId,
CorrelationId,
Properties
| order by TimeGenerated asc L’absence d’une opération delete est attendue pour une simple expiration. Elle ne justifie ni un renouvellement automatique, ni une désactivation de l’assignment.
Décider entre correction et renouvellement borné
Corrigez la ressource quand la condition de sortie initiale est réalisable : diagnostic settings manquants, tag absent, chiffrement incomplet ou SKU non conforme. Corrigez l’exemption quand l’approbation reste valide mais que son mapping est faux : mauvais assignment ID, référence d’initiative obsolète ou scope plus étroit que la ressource réellement approuvée.
Un renouvellement n’est défendable que si le risque reste accepté, le périmètre n’a pas grandi, un owner confirme une nouvelle échéance et la correction permanente possède un changement planifié. Mettez à jour l’objet existant pour préserver sa continuité.
NEW_EXPIRY="2026-10-04T18:00:00Z"
az policy exemption update --name "$EXEMPTION" --scope "$SCOPE" --expires-on "$NEW_EXPIRY" --metadata '{"owner":"platform-operations","ticket":"CHG-1907","exit":"deploy diagnostic settings"}' --output jsonc Ne modifiez pas enforcementMode, l’effet de la définition ou les notScopes pour traiter un seul workload. Ces changements déplacent la décision vers toutes les ressources de l’assignment et rendent le rollback beaucoup plus risqué.
Canaryer le même artefact et préparer le retour arrière
Après correction ou renouvellement, rejouez d’abord une opération bornée avec le même commit et la même identité de pipeline. Vérifiez que le deny attendu disparaît uniquement sur la ressource approuvée et qu’une ressource témoin hors exemption reste refusée.
Canari autorisé
Même artefact et mêmes paramètres que le run refusé
Même ressource couverte par l'exemption
Aucun changement hors du plan approuvé
Probe applicative saine après déploiement
Contrôle de refus
Ressource témoin hors scope
Même condition non conforme
Deny toujours observé avec l'assignment attendue
Rollback
Arrêter les retries si le deny change de règle ou de scope
Restaurer l'artefact précédent si la correction applicative régresse
Restaurer l'ancien expiresOn ou supprimer le remplacement selon la politique d'audit
Ne jamais laisser DoNotEnforce comme contournement temporaire
Conserver correlation IDs, diff et décision finale Fermez l’incident seulement quand la conformité a été relue après propagation et que la sortie d’exemption possède une date. Un pipeline vert sans contrôle de refus prouve seulement qu’une barrière a disparu.
Conclusion
Une exemption Azure Policy visible peut être parfaitement expirée : l’objet reste comme preuve, mais n’est plus honoré. Le diagnostic doit relier l’heure du refus à expiresOn, à l’assignment exacte, aux références d’initiative et à l’état réel de la ressource.
La décision devient alors exploitable : corriger la non-conformité, réparer un mapping précis, renouveler brièvement avec une nouvelle approbation, ou maintenir le blocage. La validation finale doit prouver le déploiement autorisé et le refus qui protège encore le reste du périmètre.