Le déploiement était vert. L’application ne voyait rien.

En brefLecture 5 min
Contexte
Déploiement terminé, tous les indicateurs au vert, fonction utile inopérante
Constat
L’identité managée existait mais n’était pas sélectionnée à l’exécution
Pour qui
Architecte cloud, ingénieur plateforme, responsable de production
Preuve
Retour de mission interne, souscription Geek Guru

Le déploiement était terminé. L’infrastructure existait, le conteneur tournait et l’application répondait. Pourtant, le tableau de bord ne remontait aucune ressource Azure.

Ce type de situation est inconfortable parce que plusieurs indicateurs sont au vert. Terraform a terminé. La plateforme a accepté la révision. Le contrôle de santé répond. Pris séparément, chacun de ces signaux semble confirmer que le système fonctionne.

Le signal observé

La fonction utile du système — observer les ressources — ne fonctionnait pas.

Aucune erreur de déploiement, aucun conteneur en échec, aucun contrôle de santé rouge. Le seul signal exploitable était un résultat vide là où une liste de ressources était attendue. C’est le pire profil de défaut : il ne déclenche rien.

Pourquoi le système se comportait ainsi

L’application utilisait DefaultAzureCredential avec une identité managée attribuée par l’utilisateur. Cette identité existait et disposait des droits nécessaires. Il manquait cependant une information déterminante dans l’environnement de l’application : l’identifiant client explicite de l’identité à utiliser.

Sans AZURE_CLIENT_ID, la chaîne d’authentification ne sélectionnait pas l’identité attendue. L’infrastructure était présente, mais la relation entre l’application en cours d’exécution et son identité restait incomplète.

Ce défaut révèle une limite structurelle des revues statiques. Le Terraform peut contenir l’identité. Le rôle peut être correctement affecté. Le conteneur peut démarrer. Aucun de ces éléments ne démontre que le processus exécuté obtient effectivement un jeton avec la bonne identité et accède à la ressource attendue.

La décision

La correction technique était courte : déclarer explicitement l’identifiant client dans les variables d’environnement de la charge de travail, au niveau du code d’infrastructure et non à la main.

L’option écartée était de laisser la sélection implicite en documentant le comportement attendu. Elle coûtait moins cher immédiatement et reportait le problème sur le prochain déploiement, dans un environnement où personne ne se souviendrait du contexte. Une dépendance qui n’est vraie que si l’ordre de résolution reste inchangé n’est pas une dépendance, c’est un pari.

La vérification

Je n’ai pas considéré le défaut corrigé parce que la variable était présente. J’ai relancé un scan réel et vérifié que des ressources remontaient effectivement. C’est cette exécution — et non la lecture du plan Terraform — qui a fermé le sujet.

Un contrôle de production doit suivre le chemin réellement exécuté :

  1. Quelle identité la charge de travail tente-t-elle d’utiliser ?
  2. Comment cette identité est-elle sélectionnée à l’exécution ?
  3. Quel jeton est réellement obtenu ?
  4. Quelle ressource accepte ou refuse ce jeton ?

Le succès du pipeline ne répond à aucune de ces questions.

Ce que ce cas permet d’affirmer

Depuis cet incident, je distingue trois états au lieu de deux : « déployé », « démarré » et « opérationnel ». Un système n’entre dans le troisième que lorsque sa fonction utile a été exercée avec ses dépendances, ses identités et ses contraintes réelles.

Ce cas ne permet pas d’affirmer que la chaîne d’authentification Azure se comporte systématiquement ainsi, ni que ce défaut est fréquent. Il établit qu’une configuration d’identité complète sur le papier peut rester inopérante à l’exécution, et qu’aucun indicateur de déploiement ne le révèle.

Vérifier ce point dans votre environnement

Contrôles à rejouer vous-mêmeLe seul contrôle qui conclut est le dernier. Les quatre premiers ne font que préparer le terrain.

  1. Recenser les identités attribuées à la charge de travail. Pour une identité attribuée par l’utilisateur, transmettre explicitement client_id=$AZURE_CLIENT_ID au point de terminaison ; sans sélecteur, celui-ci tente l’identité attribuée par le système.
  2. Vérifier que AZURE_CLIENT_ID contient bien l’identifiant client de l’identité attendue dans l’environnement d’exécution, et non seulement dans le dépôt d’infrastructure.
  3. Depuis l’intérieur du conteneur, demander un jeton en transmettant cet identifiant client, puis comparer le client_id retourné à la valeur attendue. Le décodage du jeton peut aider au diagnostic, mais ne remplace ni sa validation ni l’appel à la ressource cible.
  4. Confronter le principal oid à l’attribution de rôle sur la ressource cible. Un jeton valide portant la mauvaise identité produit un refus qui peut ressembler à un problème de droits.
  5. Exercer la fonction utile, pas le contrôle de santé. Un /healthz qui répond ne teste que le serveur web.
# Depuis la charge de travail (Container Apps / App Service)
test -n "$AZURE_CLIENT_ID" || { echo "AZURE_CLIENT_ID absent" >&2; exit 1; }

curl --fail-with-body -sS \
  -H "X-IDENTITY-HEADER: $IDENTITY_HEADER" \
  --get "$IDENTITY_ENDPOINT" \
  --data-urlencode "resource=https://management.azure.com/" \
  --data-urlencode "api-version=2019-08-01" \
  --data-urlencode "client_id=$AZURE_CLIENT_ID" \
| EXPECTED_CLIENT_ID="$AZURE_CLIENT_ID" python3 -c "import os,sys,json,base64;
r=json.load(sys.stdin); part=r['access_token'].split('.')[1];
part += '=' * (-len(part) % 4); claims=json.loads(base64.urlsafe_b64decode(part));
actual=r.get('client_id') or claims.get('appid') or claims.get('azp');
expected=os.environ['EXPECTED_CLIENT_ID'];
print('client_id =', actual, '| oid =', claims.get('oid'));
raise SystemExit(0 if str(actual).lower() == expected.lower() else 1)"

Ce que cela change à l’échelle d’un grand compte

Sur un service isolé, c’est une variable oubliée. Sur un parc de plusieurs centaines de charges de travail, la sélection implicite d’identité devient une dette systémique — et elle se paie au moment le plus coûteux : pendant un incident ou pendant un audit.

  • Traçabilité des actions. Si personne ne sait quelle identité une charge de travail utilise réellement, personne ne peut répondre à « qui a effectué cette opération ». C’est la question que pose un auditeur, et l’absence de réponse coûte plus cher que le défaut lui-même.
  • Critère d’acceptation, pas de documentation. La distinction déployé / démarré / opérationnel doit figurer dans la définition de terminé. Tant qu’une mise en production est validée sur un contrôle de santé, ce défaut se reproduira sur d’autres services.
  • Moindre privilège vérifié, pas déclaré. Une identité par charge de travail et par intention — lecture d’un côté, écriture de l’autre — n’a de valeur que si l’on peut démontrer laquelle est effectivement présentée à l’exécution.
  • Le pipeline vert n’est pas un contrôle. Un déploiement réussi prouve que la plateforme a accepté une déclaration. Il ne prouve rien sur le comportement du système. Ce sont deux artefacts distincts et un dossier sérieux contient les deux.

C’est précisément ce que la méthode cherche à rendre explicite : distinguer ce qui a été déclaré de ce qui a été exercé.

Frontière de preuveRetour de mission interne. Le constat porte sur un déploiement conduit sur un environnement appartenant à Geek Guru. Il décrit une occurrence observée et le chemin de vérification suivi, non un comportement systématique du service ni un défaut de la bibliothèque d’authentification. Aucun système client n’est concerné. Les variables d’environnement citées correspondent aux plateformes Azure Container Apps et App Service ; d’autres hôtes exposent d’autres points de terminaison.

Étape suivante

Si vos mises en production sont validées sur un contrôle de santé plutôt que sur la fonction utile, l’écart mérite d’être mesuré avant le prochain incident.