Infrastructure

Azure Managed Grafana : diagnostiquer un accès API refusé avant de recréer un jeton

Un runbook de production pour séparer expiration, audience, rôle Grafana et secret déployé lorsqu'une automatisation Azure Managed Grafana reçoit un 401 ou un 403.

07 oct. 2026 azuremanaged-grafanagrafanaidentityentra-idmanaged-identityautomationobservabilitysecurityrunbookrollbackproduction

Le pipeline qui provisionne les dashboards Azure Managed Grafana échoue soudainement avec un 401 ou un 403. Les dashboards déjà publiés restent visibles et les sources de données répondent. Le réflexe consiste à générer un nouveau jeton de service account, lui donner le rôle Admin et relancer toute la synchronisation.

Cette réaction peut rétablir le job tout en masquant la cause : jeton expiré, secret périmé dans un runner, mauvaise audience Microsoft Entra, rôle attribué au mauvais principal ou appel envoyé au mauvais workspace. Elle ajoute aussi un secret sans prouver que l’ancien a été retiré. Le cas fil rouge est une automatisation CI qui lit des dossiers, compare des dashboards versionnés puis publie uniquement le diff. L’objectif est de restaurer ce flux avec le plus petit privilège, un canari et un rollback explicite.

Figer l’appel qui échoue

Conservez une exécution précise avant de modifier le credential. Relevez le runner, le commit, l’endpoint Grafana, la route API, la méthode HTTP, le statut, l’heure, le type d’identité attendu et la référence du secret. Ne copiez ni le bearer token ni les en-têtes complets dans le ticket.

yaml incident-grafana-api.yml
incident:
detected_utc: 2026-10-07T05:42:00Z
workspace: amg-observability-prod
endpoint: https://<workspace>.grafana.azure.com
runner: dashboard-sync-prod
pipeline_run: <run-id>
commit: <sha>
request: GET /api/org
response_status: 401

expected_identity:
mode: service_account_token
service_account: dashboard-publisher
token_name: ci-prod-2026-09
secret_reference: kv-observability/grafana-ci-token

containment:
publish_enabled: false
read_only_diagnostics: true
last_qualified_dashboard_bundle: dashboards-2026-10-06.3

Un 401 ou un 403 est un symptôme, pas une preuve suffisante d’expiration. Rejouez d’abord une lecture bornée depuis le même runtime. Une requête lancée depuis le poste d’un administrateur valide son identité et son chemin réseau, pas ceux du runner en panne.

Identifier la famille de jeton réellement utilisée

Azure Managed Grafana permet deux chemins adaptés à l’automatisation. Un jeton de service account Grafana est un secret opaque associé à un compte et à son rôle. Un jeton Microsoft Entra est émis pour un utilisateur, un service principal ou une identité managée disposant d’un rôle Azure Managed Grafana sur la ressource.

Ces chemins ne se dépannent pas de la même façon. Pour un service account, vérifiez le nom du compte, son état, son rôle, le nom du jeton et son expiration. Pour Microsoft Entra, vérifiez l’identité du runtime, l’audience et l’attribution RBAC. Ne déduisez pas le mécanisme depuis le nom d’une variable comme GRAFANA_TOKEN : prouvez la provenance au moment de l’exécution.

bash 01-inventorier-identite-grafana.sh
GRAFANA_NAME="amg-observability-prod"
SERVICE_ACCOUNT="dashboard-publisher"

az grafana service-account show \
--name "$GRAFANA_NAME" \
--service-account "$SERVICE_ACCOUNT" \
--output jsonc

az grafana service-account token list \
--name "$GRAFANA_NAME" \
--service-account "$SERVICE_ACCOUNT" \
--query "[].{name:name,expiration:expiration,expired:hasExpired}" \
--output table

La liste expose les métadonnées, pas la valeur du jeton. Comparez le nom attendu avec la version du secret injectée dans le job et son horodatage de déploiement. Si le jeton Azure est encore valide mais que le runner utilise une ancienne version Key Vault mise en cache, créer un troisième jeton ne corrige pas le mécanisme de distribution.

Séparer authentification, autorisation et cible

Traitez l’incident comme quatre contrats distincts :

text plans-diagnostic-grafana.txt
1. Cible
 Endpoint, workspace, tenant et route API correspondent à l'environnement attendu

2. Acquisition
 Le runtime obtient le bon type de jeton depuis la source approuvée

3. Authentification
 Le jeton est présent, non expiré et destiné à Azure Managed Grafana

4. Autorisation
 Le service account ou principal possède le rôle minimal requis par l'opération

Preuve de bout en bout
 exécution -> identité -> audience/expiration -> rôle -> endpoint -> opération

Pour Microsoft Entra, l’audience du data plane est https://dashboard.azure.com. En code, demandez le scope https://dashboard.azure.com/.default. Un jeton ARM, Microsoft Graph ou Azure Monitor peut être valide et signé, mais rester impropre à l’API Grafana.

bash 02-prober-avec-entra.sh
GRAFANA_ENDPOINT="https://<workspace>.grafana.azure.com"

TOKEN=$(az account get-access-token \
--resource https://dashboard.azure.com \
--query accessToken -o tsv)

curl --silent --show-error \
--output grafana-org-response.json \
--write-out "status=%{http_code}\n" \
--header "Authorization: Bearer $TOKEN" \
"$GRAFANA_ENDPOINT/api/org"

Cette commande est un test interactif. Dans le runner, acquérez le jeton avec l’identité du workload, pas avec la session Azure CLI d’un opérateur. Contrôlez ensuite l’attribution Grafana Viewer, Grafana Editor ou Grafana Admin au scope du workspace. Une synchronisation qui modifie des dashboards a généralement besoin d’Editor ; Admin ne doit pas devenir un correctif par défaut.

Prouver quel secret le runner a chargé

Pour un service account, la valeur n’est affichée qu’à la création. Il est donc impossible de relire le jeton côté Azure pour le comparer au secret déployé. Comparez des empreintes calculées dans des environnements contrôlés, jamais les valeurs brutes, et ne conservez pas l’empreinte dans des logs publics.

bash 03-empreinte-secret-runtime.sh
# Exécuter dans un job restreint ; ne jamais afficher GRAFANA_TOKEN.
printf '%s' "$GRAFANA_TOKEN" \
| sha256sum \
| awk '{print "token_fingerprint=" substr($1,1,12)}'

# À rapprocher de l'empreinte enregistrée lors du déploiement du secret,
# puis supprimer la sortie lorsque l'incident est clos.

Vérifiez aussi le mécanisme de résolution du secret : version épinglée ou courante, heure de rafraîchissement, redémarrage requis du runner, variable écrasée au niveau du pipeline et masquage effectif. Un secret juste dans Key Vault mais faux dans le processus reste une panne de déploiement.

Faire une rotation avec chevauchement

Si l’ancien jeton est expiré ou compromis, créez un jeton nommé et borné dans le temps sur le service account existant. Ne recréez pas le compte tant que son rôle et son scope sont corrects : vous perdriez une partie de la continuité d’audit.

bash 04-creer-jeton-canari.sh
az grafana service-account token create \
--name "amg-observability-prod" \
--service-account "dashboard-publisher" \
--token "ci-prod-2026-10-canary" \
--time-to-live 15d \
--output json

Capturez la valeur une seule fois vers le coffre approuvé sans la faire transiter dans les logs. Déployez-la sur un runner canari, puis exécutez trois probes : lecture de l’organisation, lecture d’un dossier connu et écriture idempotente d’un dashboard de test dans un dossier non critique. Comparez le diff attendu, l’identité observée et les événements d’authentification.

Pendant le chevauchement, gardez l’ancien jeton seulement s’il est encore sûr et nécessaire au rollback. Une fois tous les consommateurs inventoriés et le canari étendu, supprimez-le explicitement avec az grafana service-account token delete. Un nouveau jeton n’invalide pas automatiquement les précédents.

Préférer Microsoft Entra quand le runtime le permet

Si l’automatisation tourne sur une ressource Azure capable de porter une identité managée, testez Microsoft Entra comme chemin cible. Attribuez le rôle Grafana minimal au principal sur le workspace, acquérez un jeton pour https://dashboard.azure.com/.default, puis rejouez exactement les mêmes probes.

La migration ne doit pas mélanger identité de l’automatisation et identité utilisée par Grafana pour lire ses sources de données. Ce sont deux flux différents : le runner appelle l’API Grafana ; le workspace interroge Azure Monitor, Prometheus ou une autre source. Réparer l’un ne prouve pas l’autre.

Maintenez les deux chemins uniquement pendant une fenêtre bornée. Après validation Microsoft Entra, retirez le secret du runner, révoquez le jeton de service account devenu inutile et, si aucun autre consommateur n’en dépend, réévaluez l’activation des service accounts sur le workspace.

Décider reprise, maintien ou rollback

text gate-acces-api-grafana.txt
Reprendre la publication
Le runner de production utilise l'identité attendue
GET /api/org et les lectures bornées retournent le résultat attendu
Le rôle minimal permet l'écriture canari sans accès administratif inutile
Le diff publié correspond au commit et le second passage est idempotent
Les anciens jetons inutiles sont révoqués

Maintenir en lecture seule
L'identité ou la version du secret reste ambiguë
Le rôle nécessaire n'est pas encore borné
Certains consommateurs de l'ancien jeton ne sont pas inventoriés

Rollback
Réinjecter la dernière version de secret qualifiée si elle reste valide et sûre
Ou rétablir temporairement le chemin d'identité précédent
Suspendre les écritures et conserver le bundle de dashboards déjà qualifié
Ne jamais restaurer un jeton expiré ou suspecté compromis

Conclusion

Un accès API Azure Managed Grafana refusé n’appelle pas automatiquement un nouveau secret. Le diagnostic utile relie l’exécution, la provenance du jeton, son audience ou son expiration, le rôle effectif et le workspace ciblé.

La décision est alors contrôlable : corriger la distribution si le runner charge une mauvaise version, faire une rotation avec chevauchement si le jeton est expiré, migrer vers Microsoft Entra lorsque le runtime le permet, ou rester en lecture seule tant que l’identité n’est pas prouvée. La reprise n’est validée qu’après un canari idempotent et la révocation explicite des accès devenus inutiles.