Cloud
Azure WAF : diagnostiquer une policy appliquée de façon incohérente après déploiement
Un runbook de production pour séparer propagation ARM, associations WAF, priorité des règles et variations de requêtes avant un rollback global de policy Application Gateway.
Une policy Azure WAF vient d’être déployée sur Application Gateway. Le test de l’équipe plateforme passe, mais certains utilisateurs reçoivent encore un 403. Une autre route semble ignorer la nouvelle règle. La conclusion tombe vite : « la policy ne s’est pas propagée partout ». Un rollback global paraît alors plus sûr que de laisser un comportement incohérent en production.
Ce diagnostic est souvent prématuré. Deux requêtes peuvent traverser des listeners, des path rules ou des gateways différents. Une custom rule prioritaire peut terminer l’évaluation avant les règles managées. Le déploiement ARM peut aussi être terminé alors que le test rejoue un hostname, un chemin ou un payload différent. Ce runbook vise une décision précise : attendre dans une fenêtre bornée, corriger une association ou une règle, rollbacker la version de policy, ou maintenir le blocage parce qu’il est légitime.
Figer le changement et le symptôme
Commencez par une seule requête acceptée et une seule requête bloquée. Conservez leur heure UTC, hostname, chemin, méthode, IP source, identifiant de corrélation et résultat applicatif. Sans ce couple, « parfois » mélange des trafics qui n’ont peut-être jamais suivi la même configuration.
change:
gateway: agw-api-prod
policy: waf-api-prod
intended_policy_version: git-8d2c1f4
deployment_operation: dep-waf-20261003-1605
started_utc: 2026-10-03T16:05:00Z
expected_scope:
hostnames: [api.example.com]
paths: [/partner/*]
symptom_pair:
accepted:
utc: 2026-10-03T16:18:11Z
method: POST
path: /partner/orders
correlation_id: waf-canary-accepted-01
blocked:
utc: 2026-10-03T16:18:43Z
method: POST
path: /partner/orders
correlation_id: waf-canary-blocked-01
decision_deadline_utc: 2026-10-03T16:35:00Z
rollback_artifact: last-known-good-policy-json Exportez aussi le diff IaC approuvé et la dernière policy saine. Le rollback doit restaurer un objet connu, pas reconstruire une ancienne règle à la main pendant l’incident.
Prouver l’état ARM avant d’attendre
Un déploiement accepté par le pipeline ne prouve pas que la ressource a terminé sa mise à jour. Lisez la policy et la gateway directement dans Azure, puis reliez-les à l’opération de changement.
RG="rg-edge-prod"
GATEWAY="agw-api-prod"
POLICY="waf-api-prod"
POLICY_ID=$(az network application-gateway waf-policy show --resource-group "$RG" --name "$POLICY" --query id -o tsv)
az network application-gateway waf-policy show --resource-group "$RG" --name "$POLICY" --query '{id:id,state:provisioningState,settings:policySettings,customRules:customRules[].{name:name,priority:priority,state:state,action:action}}' --output jsonc
az network application-gateway show --resource-group "$RG" --name "$GATEWAY" --query '{state:provisioningState,policy:firewallPolicy.id}' --output jsonc
az monitor activity-log list --resource-id "$POLICY_ID" --start-time "2026-10-03T15:45:00Z" --end-time "2026-10-03T16:35:00Z" --query '[].{time:eventTimestamp,status:status.localizedValue,operation:operationName.localizedValue,correlationId:correlationId}' --output table Si provisioningState n’est pas Succeeded, n’empilez pas une seconde modification. Figez la fenêtre, suivez l’opération et préparez le rollback. Si Azure indique Succeeded, continuer à parler de propagation sans comparer les associations et les requêtes laisse le diagnostic sans preuve.
Inventorier les associations réellement effectives
Une policy peut être associée à la gateway, à un listener ou à une path rule. Une association plus spécifique change le périmètre observé. Listez chaque point d’attache et comparez son identifiant complet à la policy attendue.
az network application-gateway show --resource-group "$RG" --name "$GATEWAY" --query '{
gatewayPolicy:firewallPolicy.id,
listeners:httpListeners[].{name:name,hostNames:hostNames,policy:firewallPolicy.id},
pathMaps:urlPathMaps[].{
name:name,
rules:pathRules[].{name:name,paths:paths,policy:firewallPolicy.id}
}
}' --output jsonc Vérifiez ensuite la table de routage de la gateway : listener sélectionné, hostnames, URL path map, ordre des règles de chemin et backend. Une requête envoyée à l’IP avec un mauvais en-tête Host n’est pas un canari du trafic réel. De même, deux gateways derrière Traffic Manager ou Front Door ne partagent pas forcément la même policy ni le même instant de déploiement.
Ne modifiez pas toutes les associations pour les rendre visuellement identiques. L’intention peut être légitime : policy globale, contrôle renforcé sur un listener administratif, exception bornée sur une route. Corrigez seulement l’écart entre l’architecture approuvée et l’état déployé.
Comparer le contenu déployé au commit approuvé
Un nom de policy stable ne garantit pas un contenu stable. Exportez la ressource, retirez les champs volatils, puis comparez règles, priorités, actions, conditions, exclusions et version des règles managées au commit attendu.
az network application-gateway waf-policy show --resource-group "$RG" --name "$POLICY" --query '{
mode:policySettings.mode,
state:policySettings.state,
requestBodyCheck:policySettings.requestBodyCheck,
maxRequestBodySizeInKb:policySettings.maxRequestBodySizeInKb,
customRules:customRules,
managedRules:managedRules
}' --output json > waf-policy-effective.json
jq -S . waf-policy-effective.json > waf-policy-effective.sorted.json
jq -S . waf-policy-approved.json > waf-policy-approved.sorted.json
diff -u waf-policy-approved.sorted.json waf-policy-effective.sorted.json Une custom rule avec une priorité plus faible numériquement s’exécute avant les suivantes. Une action terminale peut donc expliquer pourquoi une requête n’atteint jamais la règle que l’équipe surveille. Vérifiez aussi les règles désactivées, les match variables, les transformations, les négations et les exclusions : une seule différence peut ressembler à une propagation partielle.
Lire les événements WAF comme une preuve de décision
Les logs WAF prouvent qu’une règle a évalué une requête à un instant donné. Ils ne constituent pas à eux seuls un numéro de version de policy. Utilisez-les pour comparer le couple de requêtes et identifier la règle, l’action, le hostname et le chemin réellement observés.
let StartTime = datetime(2026-10-03T16:05:00Z);
let EndTime = datetime(2026-10-03T16:35:00Z);
AGWFirewallLogs
| where TimeGenerated between (StartTime .. EndTime)
| where Hostname =~ "api.example.com"
| where RequestUri startswith "/partner/"
| summarize
matches=count(),
blocked=countif(Action =~ "Blocked"),
transactions=dcount(TransactionId),
rules=make_set(RuleId, 20),
messages=make_set(Message, 10)
by bin(TimeGenerated, 2m), Hostname, RequestUri, ClientIp
| order by TimeGenerated asc Si le workspace reçoit encore AzureDiagnostics, adaptez les noms de champs à hostname_s, requestUri_s, clientIp_s, action_s, ruleId_s et transactionId_g. Corrélez ensuite les mêmes identifiants avec les access logs et les logs applicatifs. L’absence de ligne WAF peut signifier que la requête n’a pas traversé cette gateway, qu’aucune règle n’a journalisé l’événement ou que l’ingestion est en retard ; elle ne prouve pas automatiquement une policy inactive.
Rejouer une matrice qui ne change qu’une dimension
Un canari fiable garde le même DNS public, le même hostname TLS, la même méthode, le même chemin et le même corps. Il ne change qu’une dimension contrôlée : en-tête attendu, valeur qui déclenche la règle ou identité client.
Canari A - contrôle autorisé
Même hostname, route, méthode et payload de référence
Résultat attendu: réponse applicative non bloquée
Canari B - règle ciblée
Une seule valeur modifiée pour matcher la custom rule
Résultat attendu: blocage et ruleId attendue dans les logs
Canari C - contrôle hors périmètre
Autre route du même listener
Résultat attendu: comportement inchangé
Canari D - refus de sécurité
Payload de test approuvé qui doit rester bloqué par règle managée
Résultat attendu: blocage toujours visible, sans exclusion globale Exécutez la matrice depuis une source connue, à faible volume, avec un identifiant unique par requête. Répétez-la pendant une fenêtre courte seulement si l’état ARM vient de passer à Succeeded. Des retries continus créent du bruit et peuvent activer une règle de rate limiting sans apporter de preuve sur la policy.
Décider correction, attente ou rollback
Attendez uniquement si une opération Azure est encore active, si la fenêtre de changement autorise cette attente et si aucun parcours critique n’est exposé. Corrigez une association lorsque l’identifiant de policy diffère du contrat sur le listener ou la path rule touchée. Corrigez la règle lorsque le scope, la priorité ou la condition déployée diffère du commit approuvé.
Rollbackez la policy si Azure annonce le déploiement terminé mais que le canari autorisé régresse, si la règle touche un hostname ou un chemin hors périmètre, si le contrôle de refus ne bloque plus, ou si l’état effectif ne peut pas être expliqué avant la deadline. Restaurez la dernière version connue par le pipeline propriétaire, puis relisez provisioningState, les associations, le diff exporté et la même matrice.
Un rollback WAF ne retire pas une requête déjà acceptée par le backend. Si la mauvaise policy a ouvert un flux, conservez les transaction IDs, examinez les actions applicatives et déclenchez le runbook de sécurité approprié. Si elle a seulement bloqué du trafic légitime, mesurez la récupération dans les access logs et côté application avant de fermer l’incident.
Conclusion
Une application WAF apparemment incohérente n’est pas une invitation à modifier plusieurs règles jusqu’à ce que les tests passent. Il faut relier une paire de requêtes à l’état ARM, aux associations effectives, au contenu exact de la policy et aux événements WAF.
La décision de production devient alors défendable : attendre une opération encore active, corriger un périmètre précis, conserver un blocage légitime ou restaurer la dernière policy saine. La validation finale doit prouver à la fois le parcours autorisé et le refus qui protège encore l’application.