Automation

Azure API Management : valider un policy fragment partagé avant son déploiement

Un runbook de production pour inventorier les références, versionner un policy fragment APIM, le tester sur une révision canari, corréler les traces puis décider promotion ou rollback.

06 sept. 2026 azureapi-managementapimpolicy-fragmentsdevopsautomationobservabilitysecuritycanaryrunbookrollbackproduction

Une équipe veut durcir l’authentification de plusieurs API derrière Azure API Management. La logique existe déjà dans un policy fragment partagé : validation du jeton, normalisation de quelques headers et ajout d’un contexte vers le backend. Modifier ce fragment en place paraît plus propre que toucher chaque API. C’est aussi le moyen le plus rapide de propager une erreur à tous ses consommateurs.

Le problème n’est pas seulement de valider un XML. Un fragment est inséré tel quel dans chaque policy qui le référence. Son comportement dépend donc de la section, de l’ordre des instructions, des variables déjà définies, des named values et du gateway qui exécute la policy. Ce runbook vise une décision exploitable : promouvoir une nouvelle version du fragment, limiter son périmètre, ou revenir à la référence précédente sans réécrire toutes les policies.

Figer le contrat avant de toucher au fragment

Décrivez d’abord ce que le changement doit produire et ce qu’il ne doit jamais changer. Le diff XML n’est qu’une implémentation de ce contrat.

yaml apim-fragment-change.yml
change:
id: CHG-2941
fragment_current: auth-context-v1
fragment_candidate: auth-context-v2
intent: reject expired tokens and preserve backend context

must_preserve:
- valid-client-request-is-forwarded
- backend-receives-correlation-id
- existing-product-subscriptions-still-work
- error-body-does-not-leak-token-details

must_reject:
- expired-token
- wrong-audience
- missing-required-claim

rollback:
reference: auth-context-v1
owner: api-platform
trigger: unexplained-401-403-5xx-or-latency-regression

Figez aussi le scope exact : instance APIM, workspace éventuel, produits, API, opérations et gateways concernés. Un test sur une API ne prouve rien pour une autre policy dont l’ordre de <base />, des fragments ou des variables diffère.

Inventorier les consommateurs réels

La liste des équipes qui pensent utiliser le fragment n’est pas l’inventaire. Il faut lire les références présentes dans les policies déployées, puis rapprocher chaque référence de son scope et de son trafic.

bash 01-export-apim-policy-state.sh
APIM_ID="/subscriptions/<subscription>/resourceGroups/rg-api-prod/providers/Microsoft.ApiManagement/service/apim-prod"
API_VERSION="<supported-management-api-version>"

az rest --method get \
--url "https://management.azure.com/$APIM_ID/policyFragments?api-version=$API_VERSION" \
--output json > policy-fragments-before.json

# Exporter aussi les policies globales, produit, API et operation depuis IaC
# ou l'API de management, puis rechercher la reference exacte.
rg -n 'include-fragment fragment-id="auth-context-v1"' exported-policies/ \
> auth-context-v1-references.txt

test -s auth-context-v1-references.txt

Conservez le contenu du fragment, les références, les named values utilisées, leur portée, la révision courante de chaque API et le commit IaC attendu. Une référence indirecte ne doit pas être supposée : un fragment ne peut pas en imbriquer un autre, et il ne porte ni section de policy ni élément <base />.

Versionner au lieu d’éditer en place

Une mise à jour du fragment partagé affecte ses références existantes. Pour un changement sensible, créez un nouvel identifiant immuable pendant le canari, par exemple auth-context-v2, puis modifiez une seule policy de test pour le référencer. Cette duplication temporaire est un contrôle de rollout, pas une dette permanente.

text fragment-review-gates.txt
Structure
XML valide avec une ou plusieurs instructions de policy
Aucune section inbound, backend, outbound ou on-error
Aucun element base
Aucun include-fragment imbrique

Dependances
Named values declarees dans le bon scope
Variables lues apres leur definition
Secrets jamais imprimes dans les traces ou reponses
Instruction compatible avec la section et le gateway cibles

Changement
Nouvel ID de fragment pour le canari
Diff relu comme un comportement, pas seulement comme du XML
Reference v1 conservee pour rollback
Aucun autre changement de policy dans le meme lot

Le point dur est l’ordre d’exécution. Déplacer une validation avant l’initialisation d’une variable, mettre une transformation après le routage backend ou changer la position relative à <base /> peut modifier le résultat sans rendre le document invalide.

Construire une révision canari représentative

Créez une révision non courante d’une API représentative et remplacez uniquement la référence v1 par v2. Gardez les mêmes paramètres, backend, produit et abonnement de test. Une révision permet d’appeler explicitement la candidate avec ;rev=<numéro> sans déplacer le trafic normal.

Le canari doit couvrir au moins un chemin heureux, les refus attendus et un contrôle de non-régression backend.

bash 02-probe-fragment-canary.sh
BASE_URL="https://api.example.net/orders"
REVISION="<candidate-revision>"

# Controle : revision courante.
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer <valid-test-token>" \
-H "x-correlation-id: apim-fragment-current-001" \
"$BASE_URL/health"

# Candidate : revision explicite.
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer <valid-test-token>" \
-H "x-correlation-id: apim-fragment-v2-001" \
"https://api.example.net/orders;rev=$REVISION/health"

# Refus attendu : verifier le code et un corps d'erreur non sensible.
curl --silent --show-error --output candidate-deny.json --write-out '%{http_code}
' \
-H "Authorization: Bearer <expired-test-token>" \
-H "x-correlation-id: apim-fragment-v2-deny-001" \
"https://api.example.net/orders;rev=$REVISION/health"

N’utilisez pas un vrai jeton expiré copié depuis la production. Préparez des identités et tokens de test dont les claims représentent les cas attendus. Le test doit vérifier le code HTTP, le corps, les headers, l’appel backend et l’absence d’informations sensibles.

Lire les traces comme une preuve de chaîne

Un 401 attendu ne suffit pas : il peut venir du fragment, d’une autre policy ou du backend. Activez les diagnostics approuvés sur le canari et corrélez chaque requête avec son identifiant. Adaptez les colonnes à la table utilisée par votre mode de diagnostic APIM.

kusto 03-correlate-apim-canary.kql
ApiManagementGatewayLogs
| where TimeGenerated > ago(30m)
| where CorrelationId startswith "apim-fragment-v2-"
| project TimeGenerated,
        CorrelationId,
        ApiId,
        OperationId,
        ResponseCode,
        BackendResponseCode,
        TotalTime,
        BackendTime,
        LastErrorReason
| order by TimeGenerated asc

La preuve attendue relie la requête, la décision de policy et le backend. Pour un jeton valide, APIM transmet une seule requête avec le contexte attendu. Pour un jeton invalide, le backend ne doit recevoir aucun appel. Une hausse de latence doit être séparée entre temps gateway et temps backend avant d’accuser la nouvelle validation.

Tester le blast radius avant la promotion

Le premier canari prouve la logique. Il ne prouve pas la compatibilité de tous les consommateurs. Classez les références par forme de policy : scope global ou produit, section d’inclusion, présence de <base />, variables préexistantes, type de gateway et dépendances de named values. Testez un représentant de chaque classe.

text fragment-rollout-matrix.txt
Classe A: API standard sur gateway manage
Chemin heureux, mauvais token, claim absent, timeout backend

Classe B: policy produit avec base et quotas
Auth valide, subscription key, quota conserve, erreur stable

Classe C: gateway self-hosted ou workspace
Compatibilite instruction, acces named values, diagnostic disponible

Controles transverses
Aucun secret dans logs ou reponses
Correlation ID preserve
Taux 401/403/5xx compare a la reference
Latence gateway comparee sur charge representative
Reference v1 toujours deployable

Si une classe ne peut pas être testée, ne généralisez pas le rollout. Gardez v2 limité aux scopes prouvés ou corrigez le fragment pour réduire ses dépendances implicites.

Promouvoir sans perdre le rollback

La promotion doit être un changement de références, par lots bornés, vers un fragment candidat qui ne bouge plus. Ne modifiez pas v2 pendant que les API migrent : une cible mobile rend les résultats impossibles à comparer.

yaml fragment-rollout-decision.yml
promote:
when:
- every-policy-class-has-a-passing-canary
- valid-and-denied-paths-are-correlated
- gateway-latency-remains-within-change-budget
- backend-context-is-preserved
action: migrate-references-in-bounded-batches

hold:
when:
- consumer-inventory-is-incomplete
- named-value-scope-is-ambiguous
- one-gateway-class-is-untested
action: keep-v1-current-and-fix-evidence

rollback:
when:
- unexplained-authentication-regression
- backend-call-occurs-after-expected-deny
- sensitive-data-appears-in-output-or-traces
- gateway-error-or-latency-budget-is-breached
action:
- restore-v1-reference-for-last-batch
- redeploy-policy-only
- replay-positive-and-negative-probes
- reconcile-rejected-or-duplicated-requests

Le rollback ne consiste pas à réécrire v2 en urgence. Restaurez la référence v1 connue, republiez seulement les policies du lot touché et rejouez exactement les mêmes probes. Gardez v2 pour l’analyse, puis créez une v3 corrigée si nécessaire.

Conclusion

Un policy fragment APIM réduit la duplication, mais concentre aussi le risque. La bonne unité de changement n’est pas le fichier XML seul : c’est le fragment, ses références, ses named values, son ordre d’exécution, ses gateways et les requêtes qui prouvent son comportement.

La décision devient alors simple à expliquer. Promouvez une version figée après un canari par classe de policy, maintenez le déploiement si l’inventaire ou les traces sont incomplets, et rollbackez en restaurant la référence précédente. La réutilisation reste un avantage uniquement si son blast radius est observable et réversible.