Cloud

Azure APIM : diagnostiquer une régression de policy avant rollback backend

Un runbook de production pour qualifier une régression Azure API Management avec trace APIM, diff de policy, headers, cache, backend, identité, logs, validation et rollback avant de modifier l'API ou le service cible.

27 juil. 2026 azureapi-managementapimpolicybackendheaderscacheobservabilitykqldevopsrunbookrollbackproduction

Une régression APIM ressemble rarement à une panne APIM au premier regard. Un client reçoit un 401, un 403, un 429 ou un 502. Le backend dit ne rien avoir changé. L’équipe API voit une policy récemment modifiée, mais aussi un déploiement applicatif dans la même fenêtre. La tentation est forte : rollbacker le backend, désactiver une règle, élargir un header, ou contourner APIM pour prouver que “le service marche”.

Le cas d’usage est une API interne exposée par Azure API Management vers un backend HTTP privé. Une PR a modifié une policy d’inbound : normalisation d’un header, ajout d’un validate-jwt, réécriture d’URI, cache de réponse ou règle de rate limit. Après mise en production, certains consommateurs échouent seulement sur un parcours. L’objectif du runbook est de décider si l’incident vient de la policy APIM, du backend, de l’identité, du routage privé, du cache ou d’un changement applicatif, puis de choisir une correction bornée ou un rollback propre.

Cadrer le changement comme une hypothèse d’exploitation

Commencez par écrire ce qui a changé et ce qui ne doit pas changer. Une policy APIM peut modifier la requête avant le backend, modifier la réponse après le backend, refuser avant tout appel, ou masquer l’erreur réelle derrière une réponse standardisée.

text apim-policy-change-scope.txt
Surface
APIM service: apim-prod-shared
API: orders-internal
Operation: POST /orders/{id}/confirm
Backend: https://orders-api.prod.internal
Changed policy section: inbound, backend, outbound or on-error
Deployment window: 2026-07-27 08:30-09:00 UTC

Hypotheses to separate
APIM refuses before backend call
APIM calls the wrong backend URL
APIM drops or rewrites a required header
APIM cache returns a stale response
Backend changed behavior independently
Private path, DNS or TLS failed
Consumer sends a payload no longer accepted

Minimum evidence
APIM trace or gateway logs with request ID
Policy diff linked to the failing operation
Backend logs with the same correlation ID
Consumer, subscription and identity observed
Replay request with controlled headers
Rollback commit or named policy version

Sans ce cadrage, l’équipe compare des symptômes hétérogènes. Un 502 peut venir d’un backend indisponible, mais aussi d’un set-backend-service trop large, d’un nom privé mal résolu ou d’un certificat présenté avec un hostname différent.

Relier chaque requête à une trace APIM

La première preuve utile est le chemin de la requête dans APIM. Il faut savoir si APIM a reçu l’appel, quelle operation a matché, quelle policy a exécuté une décision, si le backend a été appelé, et quelle réponse est revenue.

kusto 01-apim-request-timeline.kql
let Window = 2h;
let TargetApiId = "orders-internal";
let CorrelationId = "incident-apim-20260727-001";
ApiManagementGatewayLogs
| where TimeGenerated > ago(Window)
| where ApiId == TargetApiId
| where RequestHeaders has CorrelationId
 or BackendRequestHeaders has CorrelationId
 or ResponseHeaders has CorrelationId
| project TimeGenerated,
        ApiId,
        OperationId,
        Method,
        Url,
        ResponseCode,
        BackendUrl,
        BackendResponseCode,
        CallerIpAddress,
        SubscriptionId,
        RequestId,
        TotalTime,
        BackendTime,
        ErrorMessage
| order by TimeGenerated asc

Adaptez les champs à votre configuration de diagnostics. Le point important est de ne pas conclure sur le seul code HTTP vu par le client. Si BackendResponseCode est vide, le backend n’a peut-être jamais été appelé. Si BackendTime est stable mais que le code change, la réponse peut être transformée en outbound ou on-error.

Lire le diff de policy comme du code de production

Une policy APIM est du code d’exécution. Le diff doit être relu au niveau de l’operation touchée, pas seulement au niveau du fichier XML complet.

xml policy-diff-risk-example.xml
<inbound>
<base />
<set-header name="x-tenant-id" exists-action="override">
  <value>@(context.Subscription?.Name)</value>
</set-header>
<rewrite-uri template="/v2/orders/{id}/confirm" />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" />
<rate-limit-by-key calls="60" renewal-period="60"
  counter-key="@(context.Subscription?.Id)" />
</inbound>

Classez chaque ligne par effet.

text apim-policy-diff-review.txt
Refus avant backend
validate-jwt
check-header
ip-filter
quota, rate-limit, rate-limit-by-key

Mutation de requete
set-header, rewrite-uri, set-query-parameter
set-method, set-body, set-backend-service

Mutation de reponse
set-status, set-header, set-body
return-response

Effet cache
cache-lookup, cache-store
vary-by-header absent ou trop large

Risque de diagnostic
on-error qui remplace le message
choose conditionnel sans trace
expression qui depend d'un claim ou d'une subscription optionnelle

Un changement de header peut suffire à casser une autorisation backend. Une réécriture d’URI peut appeler une route valide mais différente. Un rate-limit-by-key peut regrouper trop de consommateurs si la clé choisie est vide ou partagée.

Rejouer avec une requête contrôlée

Le replay doit conserver les éléments qui comptent : hostname, operation, abonnement APIM, identité, headers métier et payload. Un test direct contre le backend aide à isoler, mais il ne remplace pas le test via APIM.

bash 02-apim-replay-with-correlation.sh
APIM_HOST="api.internal.example.com"
SUBSCRIPTION_KEY="00000000000000000000000000000000"
TOKEN="$(cat token.jwt)"
CORRELATION_ID="incident-apim-$(date +%Y%m%d%H%M%S)"

curl -sk -o /tmp/apim-response.json -w "%{http_code}\n" "https://$APIM_HOST/orders/42/confirm" -H "ocp-apim-subscription-key: $SUBSCRIPTION_KEY" -H "authorization: Bearer $TOKEN" -H "x-correlation-id: $CORRELATION_ID" -H "x-tenant-id: tenant-a" -H "content-type: application/json" --data @confirm-order.json

echo "correlation_id=$CORRELATION_ID"

Si le backend est privé, gardez le même nom logique et le même chemin réseau que la production. Un appel depuis une VM d’administration peut utiliser un DNS, une route ou une identité différents de ceux d’APIM.

Séparer refus APIM, backend et cache

Le diagnostic doit produire une décision lisible. Pour chaque requête échouée, classez le point d’arrêt.

text apim-failure-classification.txt
APIM refuse avant backend
BackendResponseCode absent
validate-jwt, check-header, quota ou rate-limit visible
Correction: policy ciblee, claim attendu, cle de rate limit, scope operation

APIM appelle le mauvais backend
BackendUrl different du design
rewrite-uri ou set-backend-service recent
Correction: rollback policy ou condition plus explicite

Backend refuse apres APIM
BackendResponseCode 401 ou 403
Header, token, mTLS ou managed identity different
Correction: restaurer le header attendu ou corriger authentification backend

Cache masque la realite
BackendTime nul ou faible
cache-lookup hit sur parcours dynamique
Correction: vary-by-header, vary-by-query, purge ciblee, ou cache retire

Chemin prive casse
BackendResponseCode 502 ou timeout
DNS, TLS, NSG, route ou Private Endpoint a verifier
Correction: reseau ou DNS, pas policy applicative

Cette classification évite deux erreurs coûteuses : rollbacker le backend quand APIM n’a jamais appelé le service, ou modifier APIM quand le backend refuse une identité réellement invalide.

Vérifier les headers et l’identité effective

APIM est souvent l’endroit où les identités changent de forme. Un JWT consommateur devient des claims transmis au backend. Une subscription key devient un nom d’application. Un certificat client est validé, puis un header interne est ajouté. Chaque transformation doit rester explicable.

text identity-and-header-checks.txt
Verifier
Le consumer et la subscription APIM observes
Le subject, audience et issuer du JWT
Les claims utilises dans les expressions de policy
Les headers ajoutes, supprimes ou remplaces
La cle de rate limit ou quota effective
Le certificat client ou backend si mTLS est actif
L'identite managée APIM si le backend attend Entra ID

Bloquer la reprise quand
Un header métier est reconstruit depuis une valeur optionnelle
Plusieurs tenants partagent la meme cle de rate limit
Le backend ne journalise pas le meme correlation ID
Le token valide cote APIM n'est pas celui attendu cote backend
Une condition choose renvoie vers un backend de fallback non documente

L’objectif n’est pas d’ajouter des logs partout en urgence. Il est de prouver l’identité effective au moment de l’appel et d’empêcher une correction qui élargit trop les droits ou mélange les tenants.

Décider correction, purge, rollback ou maintien

Une fois la cause classée, la réponse doit être bornée. Couper toute la policy ou bypasser APIM est rarement la bonne première action en production.

text apim-policy-decision.txt
Corriger la policy
Operation touchee identifiee
Diff minimal possible
Replay reproduit l'erreur
Trace APIM et backend confirment la cause
Rollback de la policy reste disponible

Purger ou desactiver temporairement le cache
cache-lookup prouve
Reponse dynamique ou tenant-dependante
vary-by insuffisant
Purge ciblee possible

Rollbacker la policy
Plusieurs mutations melangees
Refus legitimes en production
Trace incomplete ou effet de bord non borne
Version precedente connue et deployable

Ne pas toucher APIM
APIM appelle le bon backend
Headers et identite conformes
BackendResponseCode prouve une regression applicative
Rollback backend ou correction service a prioriser

Le critère sain est simple : ne changez que la couche qui a produit la preuve. Si la preuve manque, revenez à l’état connu le plus sûr plutôt que d’empiler des exceptions.

Valider après action

Après correction ou rollback, rejouez le scénario initial et un contrôle négatif. Une API corrigée mais non observable reste fragile.

text apim-post-action-validation.txt
Validation fonctionnelle
Requete initiale rejouee via APIM
Backend appele avec le bon hostname
Correlation ID visible dans APIM et backend
Headers attendus presents, headers interdits absents
Pas de hausse 401, 403, 429 ou 502 sur l'operation

Validation de controle
JWT invalide refuse avant backend
Tenant non autorise refuse proprement
Rate limit borne par consumer attendu
Cache ne repond pas a une donnee tenant-dependante
on-error conserve un identifiant de diagnostic

Rollback pret
Commit ou revision de policy precedente identifie
Commande de restauration connue
Fenetre de surveillance definie
Ticket conserve avec diff, traces et decision

Si le replay réussit mais que la trace ne permet toujours pas de suivre la requête, la reprise n’est pas complète. La prochaine régression reposera sur les mêmes suppositions.

Conclusion

Une régression APIM doit être traitée comme une panne de contrat entre consommateur, gateway et backend. Le bon diagnostic sépare refus avant backend, mutation de requête, cache, identité, chemin privé et comportement applicatif.

La décision utile n’est pas “rollbacker ou pas”. C’est : corriger une policy minimale, purger un cache, restaurer une version connue, ou laisser APIM intact parce que le backend est bien la couche fautive. Ce choix doit tenir sur des traces corrélées, un replay reproductible et un rollback prêt.