Infrastructure
Azure Key Vault : migrer des access policies vers Azure RBAC sans couper les workloads
Un runbook de production pour inventorier les accès Key Vault, préparer les rôles data plane, basculer vers Azure RBAC avec un canari, valider les workloads et rollbacker sans élargir les droits.
Un coffre Azure Key Vault fonctionne depuis des années avec des access policies. Une équipe sécurité demande de passer à Azure RBAC pour mieux séparer administration du coffre et accès aux secrets. Les role assignments sont créés, le modèle d’autorisation est basculé, puis plusieurs applications retournent des 403. La réaction la plus rapide serait d’ajouter Key Vault Administrator aux identités en échec. Elle rétablit parfois le service, mais elle masque les consommateurs oubliés et élargit durablement le data plane.
Le cas fil rouge est un coffre partagé par une Function App qui lit des secrets, une pipeline qui renouvelle un certificat et une équipe d’astreinte qui consulte les métadonnées. Ce runbook prépare la migration, bascule dans une fenêtre bornée, observe les accès réels et produit une décision : valider Azure RBAC, corriger un rôle précis ou revenir temporairement aux access policies.
Figer le contrat avant la migration
Commencez par capturer l’état du coffre, pas seulement la liste visible dans le portail. Il faut conserver le modèle d’autorisation, les access policies, les paramètres réseau, les diagnostic settings, les role assignments directs et hérités, ainsi que les identités attendues. Le réseau ne change pas pendant cette opération : un 403 après la bascule ne doit pas conduire à ouvrir le firewall ou l’accès public.
set -euo pipefail
RG="rg-platform-prod"
VAULT="kv-orders-prod"
VAULT_ID=$(az keyvault show -g "$RG" -n "$VAULT" --query id -o tsv)
az keyvault show -g "$RG" -n "$VAULT" --query '{id:id,rbac:properties.enableRbacAuthorization,tenant:properties.tenantId,accessPolicies:properties.accessPolicies,networkAcls:properties.networkAcls}' -o json > key-vault-before.json
az role assignment list --scope "$VAULT_ID" --include-inherited --all -o json > role-assignments-before.json
az monitor diagnostic-settings list --resource "$VAULT_ID" -o json > diagnostic-settings-before.json Associez le snapshot à un identifiant de changement et à une heure UTC. Vérifiez aussi que l’opérateur dispose séparément de Microsoft.Authorization/roleAssignments/write et Microsoft.KeyVault/vaults/write. Un droit de modifier le coffre ne prouve pas le droit de créer les role assignments nécessaires.
Inventorier les consommateurs réels
Une access policy décrit un principal et des permissions, mais pas le service qui l’utilise ni sa criticité. Résolvez chaque objectId vers un utilisateur, un groupe, un service principal ou une identité managée. Recherchez aussi les identités absentes des policies qui héritent déjà de droits RBAC depuis le resource group ou la subscription.
vault: kv-orders-prod
consumers:
- name: orders-function-prod
principal_id: 11111111-1111-1111-1111-111111111111
principal_type: managed_identity
operations: [secrets/get]
probe: read-secret-version
criticality: critical
- name: certificate-renewal-pipeline
principal_id: 22222222-2222-2222-2222-222222222222
principal_type: service_principal
operations: [certificates/get, certificates/import]
probe: import-test-certificate
criticality: scheduled
- name: platform-oncall
principal_id: 33333333-3333-3333-3333-333333333333
principal_type: group
operations: [secrets/list-metadata]
probe: list-secret-metadata
criticality: interactive
unknown_policies: []
unowned_principals: []
validation_owner: platform-team Ne migrez pas un principal inconnu en lui attribuant un rôle large « par prudence ». Marquez-le comme bloquant, retrouvez son propriétaire et observez les logs historiques. Une policy inutilisée peut être retirée plus tard, dans un changement distinct.
Traduire des opérations en rôles data plane
Ne faites pas une correspondance par intitulé. Partez des opérations réellement requises. Une application qui lit la valeur d’un secret relève généralement de Key Vault Secrets User; une pipeline qui gère des secrets peut nécessiter Key Vault Secrets Officer; les clés et certificats ont leurs propres rôles. Key Vault Reader lit les métadonnées, pas la valeur des secrets. Key Vault Contributor administre le coffre au control plane mais n’accorde pas automatiquement l’accès aux données.
Les rôles intégrés ne reproduisent pas toutes les combinaisons fines des access policies. Si aucun rôle ne couvre le besoin sans excès, créez un rôle custom revu et versionné. Gardez le scope au niveau du coffre par défaut. Un scope au secret peut être pertinent, mais il augmente le nombre d’objets à exploiter et ne doit pas servir à compenser un coffre qui mélange trop d’applications.
VAULT_ID=$(az keyvault show -g "$RG" -n "$VAULT" --query id -o tsv)
az role assignment create --assignee-object-id "11111111-1111-1111-1111-111111111111" --assignee-principal-type ServicePrincipal --role "Key Vault Secrets User" --scope "$VAULT_ID"
az role assignment create --assignee-object-id "22222222-2222-2222-2222-222222222222" --assignee-principal-type ServicePrincipal --role "Key Vault Certificates Officer" --scope "$VAULT_ID" Créez les assignments avant la bascule et attendez leur propagation. Ils n’autorisent pas encore le data plane tant que le coffre utilise les access policies, mais ils doivent être présents, résolus vers les bons principals et relisibles avant la fenêtre de changement.
Construire une matrice de validation
La bascule concerne tout le coffre. Il n’existe pas de canari par identité sur le même coffre : dès que enableRbacAuthorization passe à true, les workloads dépendent du modèle RBAC. Testez d’abord la correspondance sur un coffre non productif représentatif, puis préparez une fenêtre courte avec des probes exécutées par les identités réelles.
Principal Test positif Test negatif
orders-function-prod lire secret canari ecrire une nouvelle version
certificate-renewal-pipeline importer certificat test lire un secret applicatif
platform-oncall lister les metadonnees lire la valeur d'un secret
Preuves exigees
- resultat fonctionnel depuis le chemin reseau habituel
- objectId attendu dans la trace
- operation et scope attendus
- aucun role administrateur ajoute en urgence
- test negatif refuse
- commande de rollback relue et operateur disponible Un test avec l’identité personnelle de l’opérateur ne valide pas une identité managée. Exécutez chaque probe depuis son runtime normal : Function App, runner de pipeline ou poste d’administration contrôlé.
Basculer avec des conditions d’arrêt
Annoncez l’heure exacte, suspendez les déploiements qui modifient identités ou secrets, puis vérifiez une dernière fois le snapshot et les role assignments. La commande de bascule est simple; la discipline autour de la commande fait la sécurité du changement.
az keyvault update --resource-group "$RG" --name "$VAULT" --enable-rbac-authorization true
az keyvault show -g "$RG" -n "$VAULT" --query properties.enableRbacAuthorization -o tsv
# Lancer ensuite les probes avec chaque identite reelle.
# Ne pas utiliser le compte de l'operateur comme preuve applicative. Arrêtez la migration si un workload critique échoue, si l’identité observée diffère de l’inventaire, si les logs sont absents ou si la correction proposée exige un rôle plus large que le contrat. Un délai de propagation peut justifier une courte attente avec retries bornés; il ne justifie pas une succession d’assignments improvisés.
Lire les refus sans confondre identité et réseau
Key Vault peut envoyer la catégorie AuditEvent vers Log Analytics. Selon le mode de destination, les événements se trouvent dans une table resource-specific ou dans AzureDiagnostics. Adaptez les colonnes au schéma réellement activé et cherchez l’opération, le code, le principal, l’adresse appelante et la corrélation temporelle.
let Cutover = datetime(2026-09-19T16:15:00Z);
union isfuzzy=true AZKVAuditLogs, AzureDiagnostics
| where TimeGenerated between (Cutover - 15m .. Cutover + 45m)
| extend Vault = tostring(column_ifexists("Resource", Resource)),
Operation = tostring(column_ifexists("OperationName", OperationName)),
Status = tostring(column_ifexists("HttpStatusCode", ResultSignature)),
Principal = tostring(column_ifexists("Identity", identity_claim_oid_g)),
CallerIp = tostring(column_ifexists("CallerIpAddress", CallerIPAddress))
| where Vault has "kv-orders-prod" or _ResourceId has "/vaults/kv-orders-prod"
| summarize Calls=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated)
by Principal, Operation, Status, CallerIp
| order by Status desc, Calls desc Un 403 avec le bon principal et une opération précise pointe vers rôle, scope ou propagation. L’absence totale de trace renvoie plutôt vers DNS, réseau, mauvais nom de coffre ou instrumentation. Un succès depuis le portail mais un refus applicatif indique souvent que deux identités différentes ont été testées.
Décider validation, correction ou rollback
Validez si tous les tests positifs passent, tous les tests négatifs restent refusés, les workloads critiques sont stables et aucun rôle temporaire trop large n’a été ajouté. Corrigez en place si un seul principal identifié manque d’une opération prévue et si un rôle minimal existe. Conservez une observation renforcée après la fenêtre, car certains jobs ne s’exécutent qu’à intervalle long.
Rollbackez si plusieurs consommateurs critiques sont absents, si le principal réel n’est pas maîtrisé, si les assignments hérités rendent le périmètre illisible ou si les preuves d’audit manquent. Le retour au modèle précédent doit avoir été testé hors production. Avant de l’exécuter, confirmez que les access policies du snapshot sont toujours présentes et qu’aucun changement concurrent ne les a modifiées.
az keyvault update --resource-group "$RG" --name "$VAULT" --enable-rbac-authorization false
az keyvault show -g "$RG" -n "$VAULT" --query '{rbac:properties.enableRbacAuthorization,accessPolicies:properties.accessPolicies}' -o json
# Rejouer toute la matrice. Un toggle reussi ne prouve pas le service retabli. Ne supprimez pas immédiatement les role assignments préparés : ils facilitent l’analyse et une reprise ultérieure. Marquez-les comme non actifs pour ce coffre, corrigez l’inventaire, puis programmez une nouvelle migration plutôt que de prolonger un état hybride incompris.
Conclusion
Migrer Key Vault vers Azure RBAC n’est pas un changement de bouton. C’est une migration du contrat d’autorisation du data plane. La réussite dépend de l’inventaire des identités, de la traduction des opérations en rôles minimaux, de probes exécutées par les vrais runtimes et de traces disponibles au moment de la bascule.
La décision finale doit rester binaire et prouvable : conserver Azure RBAC parce que chaque accès attendu et chaque refus attendu ont été validés, ou revenir temporairement aux access policies avec le snapshot intact. Ajouter un rôle administrateur pour faire disparaître les 403 n’est ni une validation ni un rollback.