Automation

Azure Event Grid : diagnostiquer les dead letters avant de rejouer les événements

Un runbook de production pour qualifier un échec de livraison Azure Event Grid avec filtres de subscription, santé endpoint, stockage dead-letter, diagnostics, périmètre de rejeu, validation et rollback avant retraitement.

05 juil. 2026 azureevent-griddead-letterevent-drivenautomationobservabilitykqlstoragerunbookrollbackproduction

Des événements Event Grid en dead-letter ne sont pas une file à vider sans réfléchir. Ce sont des preuves : la livraison a échoué, un filtre a changé, le subscriber a rejeté la requête, l’authentification ne passe plus, l’endpoint est devenu injoignable ou le payload ne correspond plus au contrat du consommateur. Les rejouer trop vite peut dupliquer des actions, rouvrir un incident ou pousser une donnée périmée dans un système déjà reconstruit.

Le cas d’usage est une plateforme Azure où Event Grid route des événements de production vers des Functions, Logic Apps, webhooks, API internes ou workers d’automatisation. Un container de dead-letter se remplit après un déploiement ou une panne de dépendance. Le but du runbook est de décider s’il faut rejouer, écarter, mettre en attente ou rollbacker un changement de subscription, avec assez de preuves pour ne pas traiter deux fois les mauvais événements.

Figer la subscription et le consommateur

Commencez par une seule event subscription. Ne diagnostiquez pas tous les topics et tous les consommateurs en même temps. Le premier travail consiste à capturer le contrat de livraison : source, filtres, endpoint, comportement de retry et destination dead-letter.

text event-grid-delivery-contract.txt
Contrat de livraison Event Grid
Topic ou system topic: eg-prod-orders
Event subscription: sub-order-automation-prod
Event types: Microsoft.Storage.BlobCreated, OrderAccepted
Filtre subject: /orders/prod/
Endpoint: orders-worker.internal/api/events
Type endpoint: webhook, Function, Logic App ou API interne
Authentification: identite managee, cle, validation webhook ou policy APIM
Destination dead-letter: storage account, container et chemin
Changement recent: deploiement, filtre, release endpoint, rotation auth ou regle reseau

Preuves requises avant rejeu
Configuration de l'event subscription
Raison d'echec de livraison et fenetre temporelle
Echantillon et volume des blobs dead-letter
Logs endpoint pour les event IDs correspondants
Preuve d'idempotence du consommateur
Perimetre de rejeu et regles d'exclusion
Decision de rollback ou de mise en attente

Si l’équipe ne sait pas nommer l’effet métier du consommateur, elle ne doit pas encore rejouer. Un événement de notification, de facturation ou de provisioning n’a pas le même rayon d’impact.

Vérifier si la subscription a changé

Une hausse de dead letters après déploiement vient souvent d’une petite dérive de subscription : filtre subject trop étroit, nouveau filtre avancé, changement d’URL endpoint, identité de livraison expirée ou destination dead-letter déplacée sans visibilité opérationnelle.

bash 01-event-subscription-snapshot.sh
RESOURCE_GROUP="rg-prod-events"
TOPIC_NAME="eg-prod-orders"
SUBSCRIPTION_NAME="sub-order-automation-prod"

az eventgrid event-subscription show --source-resource-id "$(az eventgrid topic show   --resource-group "$RESOURCE_GROUP"   --name "$TOPIC_NAME"   --query id -o tsv)" --name "$SUBSCRIPTION_NAME" --output json

az monitor activity-log list --resource-group "$RESOURCE_GROUP" --offset 24h --query "[?contains(operationName.value, 'eventSubscriptions')].{time:eventTimestamp, operation:operationName.value, caller:caller, status:status.value}" --output table

Traitez les filtres comme du code de production. Un rejeu ne sert à rien si la subscription rejette toujours la même forme d’événements ou pointe encore vers le mauvais endpoint.

Lire les blobs dead-letter comme des preuves

Le stockage dead-letter doit répondre à trois questions : qu’est-ce qui a échoué, quand, et l’événement est-il encore sûr à traiter ? Échantillonnez avant d’agir.

bash 02-dead-letter-sample.sh
STORAGE_ACCOUNT="stprodeventdeadletter"
CONTAINER="eventgrid-deadletter"
PREFIX="sub-order-automation-prod/"

az storage blob list --account-name "$STORAGE_ACCOUNT" --container-name "$CONTAINER" --prefix "$PREFIX" --auth-mode login --query "[0:20].{name:name,lastModified:properties.lastModified,size:properties.contentLength}" --output table

# Ne telecharger qu'un petit echantillon d'abord.
az storage blob download-batch --account-name "$STORAGE_ACCOUNT" --destination ./deadletter-sample --source "$CONTAINER" --pattern "${PREFIX}*" --auth-mode login

Pour chaque exemple, conservez l’event ID, l’event type, le subject, l’event time, la raison de dead-letter si disponible, les métadonnées de tentative de livraison et tout identifiant de corrélation porté par le payload. Ne supposez pas que tous les blobs relèvent du même mode de panne.

Corréler échecs de livraison et logs endpoint

La bonne question n’est pas “Event Grid est-il cassé ?”. C’est “quel contrôle refuse la livraison ?”. Séparez erreurs endpoint, authentification, reachability réseau, throttling et validation côté consommateur.

kusto 03-event-grid-delivery-evidence.kql
let StartTime = datetime(2026-07-05T06:00:00Z);
let EndTime = datetime(2026-07-05T07:00:00Z);
let SubscriptionName = "sub-order-automation-prod";
AzureDiagnostics
| where TimeGenerated between (StartTime .. EndTime)
| where ResourceProvider has "MICROSOFT.EVENTGRID"
| where tostring(eventSubscriptionName_s) == SubscriptionName
| project TimeGenerated,
        eventSubscriptionName_s,
        eventType_s,
        subject_s,
        deliveryStatus_s,
        deliveryResponseCode_s,
        deliveryResponseMessage_s,
        deadLetterReason_s,
        endpointUrl_s
| order by TimeGenerated asc

Corrélez ensuite avec les logs du consommateur via event ID, request ID, operation ID ou corrélation payload. Si Event Grid remonte 401 ou 403, rejouer avant de corriger l’identité ne fera qu’ajouter des dead letters. Si l’endpoint répond 400, le contrat consommateur a peut-être changé. Si aucune requête n’atteint le consommateur, inspectez DNS, firewall, APIM, networking de Function ou dépendances Private Endpoint selon l’architecture de l’endpoint.

Prouver l’idempotence avant rejeu

Le rejeu n’est sûr que si le consommateur tolère les doublons ou si le périmètre exclut les événements déjà traités. C’est ici que beaucoup de corrections d’incident deviennent des incidents de données.

text idempotency-checklist.txt
Controles de securite du rejeu
Le consommateur stocke les event IDs ou cles metier deja traites
Les effets de bord sont idempotents ou une compensation est documentee
L'event time reste dans la fenetre de validite metier
La dependance aval accepte des evenements historiques
Un traitement partiel est detectable depuis les logs ou l'etat
Le batch de rejeu peut etre limite par event type, subject et fenetre temporelle
Les operateurs savent arreter le rejeu rapidement

Bloquer le rejeu quand
Les evenements declenchent des actions externes irreversibles
Le consommateur n'a pas de detection de doublons
Le schema payload a change et les anciens evenements ne sont pas compatibles
L'etat cible a deja ete reconstruit par un autre processus
Les echantillons dead-letter melangent plusieurs causes d'echec

Pour l’automatisation événementielle, l’idempotence n’est pas un confort. C’est la condition qui transforme le rejeu en outil d’exploitation plutôt qu’en deuxième incident.

Construire un plan de rejeu borné

Le rejeu doit être un batch contrôlé, pas une boucle sur tout un container. Définissez la règle d’inclusion, la taille maximale, l’endpoint cible, les logs attendus et la condition d’arrêt.

yaml event-grid-replay-plan.yml
replay:
incident_id: eg-deadletter-20260705-01
subscription: sub-order-automation-prod
source_container: eventgrid-deadletter
include:
  event_types:
    - OrderAccepted
  subject_prefix: /orders/prod/
  event_time:
    from: 2026-07-05T06:00:00Z
    to: 2026-07-05T06:30:00Z
exclude:
  already_processed_event_ids: true
  failed_schema_version: v1-preview
batch:
  max_events: 100
  pause_between_batches: 5m
validation:
  - endpoint returns expected 2xx
  - consumer logs processed event IDs
  - downstream state changes match event count
  - no new dead-letter spike appears
stop:
  - duplicate side effect detected
  - endpoint errors exceed threshold
  - unknown event type appears
  - operator cannot correlate replayed events

Si la plateforme n’a pas encore d’outil de rejeu, commencez par générer un rapport dry-run depuis les blobs dead-letter. Le rapport doit lister les événements qui seraient rejoués et la raison d’inclusion de chacun.

Décider rejeu, discard, attente ou rollback

Gardez la décision explicite. Un backlog dead-letter peut représenter un travail valide à récupérer, des événements poison à écarter ou un changement de configuration à rollbacker.

text event-grid-decision.txt
Rejouer
La cause racine est corrigee
Les evenements sont encore valides
L'idempotence du consommateur est prouvee
Le perimetre de rejeu est etroit et observable
La condition d'arret est assignee

Discard ou archivage
Les evenements sont obsoletes
Le consommateur a deja reconstruit l'etat cible
Le schema payload n'est plus accepte
Le proprietaire metier confirme qu'aucune action n'est necessaire

Mettre en attente
Les causes d'echec sont melangees
L'endpoint retourne encore des erreurs
La detection de doublons manque
La validite des evenements ne peut pas etre confirmee

Rollbacker
Les dead letters commencent apres changement de subscription, endpoint, auth ou filtre
La configuration precedente restaure la livraison en test
Le nouveau filtre exclut des evenements de production requis
Le rejeu compenserait un changement toujours actif

La récupération la plus propre peut être de rollbacker d’abord, puis de rejouer uniquement les événements encore valides après stabilisation de la route, de l’identité, du filtre ou du comportement endpoint.

Valider après le rejeu

Un rejeu n’est pas terminé quand le script se termine. Il est terminé quand la subscription reste saine, que l’état consommateur est cohérent et que le container dead-letter ne grossit plus pour la même raison.

text post-replay-validation.txt
Validation post-rejeu
La livraison de nouveaux evenements reussit
Le volume dead-letter n'augmente plus pour la meme raison
Les event IDs rejoues sont visibles dans les logs consommateur
L'etat aval correspond au volume metier attendu
Les enregistrements de detection de doublons sont alimentes
L'identite ou l'acces temporaire de rejeu est retire
La note d'incident conserve echantillons, KQL, perimetre et decision

Rollbacker l'outillage de rejeu quand
Il utilise des droits Storage trop larges
Il peut publier des evenements arbitraires sans revue
Il contourne l'authentification normale
Il n'a ni dry-run ni limite de batch

Conservez le dossier de preuves. Les décisions de rejeu sont difficiles à relire après coup si l’équipe ne garde que le message final de succès.

Conclusion

Les événements Event Grid en dead-letter ne deviennent une opportunité de récupération qu’après diagnostic. Avant de rejouer, prouvez le contrat de subscription, la cause de livraison, le comportement endpoint, la validité des événements et l’idempotence du consommateur.

La décision de production devient alors concrète : rejouer un batch étroit, écarter des événements obsolètes, mettre en attente des causes mélangées ou rollbacker le changement de subscription. C’est cette discipline qui rend l’automatisation événementielle récupérable sans transformer le rejeu en deuxième chemin d’écriture incontrôlé.