Azure Cost Monitoring im Admin-Portal¶
Das Admin-Portal zeigt auf /costs eine Uebersicht der aktuellen und
prognostizierten Azure-Kosten. Datenquelle ist Azure Cost Management
Query/Forecast API, abgefragt mit einem Service Principal (Rolle
Cost Management Reader) aus dem Admin-Portal-Backend.
Datenfrische: Azure aggregiert Usage asynchron. Die Werte liegen typischerweise 8–24 h hinter der Realtime-Nutzung. Das Dashboard ist kein Live-Monitor, sondern „gestriger Stand + Forecast auf Monatsende".
Was das Dashboard anzeigt¶
- Subscription Overview: Month-to-Date, Forecast Monatsende, Letzter Monat, Delta zum Vormonat — alles in der Sub-Default-Waehrung (EUR).
- Pro Resource Group: MTD-Tabelle mit Vergleich zum Vormonat.
- 30-Tage-Trend: Tagesgenauer Verlauf als SVG-Linienchart.
- Service-Breakdown: MTD pro Azure-Service (App Service, Postgres, Storage, …), mit prozentualem Anteil.
- CSV-Export: Download der letzten 30 Tage mit allen Spalten (Datum, RG, Service, Kosten, Waehrung).
Budget & Alerts¶
In der Subscription targetshot-core-0 ist seit 2026-04-23 ein
Monatsbudget targetshot-monthly-200eur eingerichtet:
| Threshold | Art | Empfaenger |
|---|---|---|
| 50 % | Actual | [email protected] |
| 75 % | Actual | [email protected] |
| 90 % | Actual | [email protected] |
| 100 % | Forecasted | [email protected] |
Die Emails kommen direkt aus Azure — kein Umweg ueber Postmark, die Azure-eigenen Alerts waren ausreichend und sparen die zweite Integration. Aendern laesst sich das im Portal unter Cost Management + Billing → Budgets.
Setup (einmalig)¶
1. Service Principal anlegen (Portal)¶
az ad sp create-for-rbacbraucht in Entra ID die RolleApplication Developeroder hoeher. Wenn dein CLI-Login nur Contributor auf der Subscription hat, geht der Weg ueber's Portal.
- Azure Portal → Microsoft Entra ID → App registrations → New registration.
- Name:
targetshot-cost-reader, Supported account types: Single tenant, Redirect URI leer. → Register. - In der neuen App: Certificates & secrets → New client secret → 24 Monate Laufzeit → sofort den Value kopieren (wird nach Neuladen nie wieder angezeigt).
- Overview merken: Application (client) ID und Directory (tenant) ID.
2. Rolle zuweisen¶
Im Portal: Subscriptions → targetshot-core-0 → Access control
(IAM) → Add role assignment:
- Role: Cost Management Reader
- Members: die gerade angelegte App targetshot-cost-reader
Damit darf der SP lesen, aber nichts schreiben.
3. Credentials als GitHub-Variablen/Secrets eintragen¶
Im Repo targetshot-app/admin-portal:
- Variable
INTERNAL_AZURE_COST_TENANT_ID=053aaa7b-46f7-4348-b702-9b54ffcacc1f - Variable
INTERNAL_AZURE_COST_CLIENT_ID= Application (client) ID aus Schritt 1 - Secret
INTERNAL_AZURE_COST_CLIENT_SECRET= der Value aus Schritt 1 - Variable
INTERNAL_AZURE_COST_SUBSCRIPTION_ID=e4d628f6-ede4-4af9-8310-b25724a71961
Der deploy-internal.yml-Workflow injiziert sie beim naechsten Deploy
in die Runtime .env des Admin-Portal-Backends. Ohne diese Werte
zeigt das Dashboard einfach „nicht konfiguriert" — der Rest des
Admin-Portals laeuft unveraendert.
Backend-Architektur¶
backend/src/azureCosts.ts— HTTP-Client mit OAuth2 Client-Credentials-Flow gegenlogin.microsoftonline.com/{tenant}/oauth2/v2.0/token, Scopemanagement.azure.com/.default. Token wird mit 60-Sekunden-Puffer gecached.backend/src/routes/costs.ts— vier Routen hinter dem normalen Admin-Guard:GET /api/admin/v1/costs/status—{ configured: boolean }GET /api/admin/v1/costs/summaryGET /api/admin/v1/costs/breakdownGET /api/admin/v1/costs/timeseries?days=30GET /api/admin/v1/costs/export.csv?days=30- In-Memory-Cache von 30 Minuten pro Endpunkt. Das matcht die Datenfrische der Azure-API.
- Rollen-Capability
costs.read— vergeben anplatform_admin,ops_admin,billing_admin.support_adminsieht die Kosten bewusst nicht.
Frontend¶
frontend/src/features/costs/CostsPage.tsx— rendert alle vier Bloecke + eingebautes SVG-Lineplot (kein Chart-Lib-Dependency, damit die Bundlegroesse nicht wegen 30 Tagen Datenpunkten steigt).- Navigation-Eintrag
Azure Kostenwird nur angezeigt, wenn die Session die Capabilitycosts.readhat.
Kostenimpact der Integration¶
Die Cost-Management-Query- und Forecast-API sind in Azure kostenlos. Der Service Principal verursacht keine Azure-Kosten. Der Admin-Portal- Backend macht pro Cache-Window (30 Minuten) ca. 6 Requests — das ganze ist vernachlaessigbar, liegt weit unterhalb des Free-Quotas.
Bekannte Grenzen¶
- Datenlag: 8–24 h. Wer gerade ein Mini-Experiment laufen laesst, sieht es nicht sofort im Dashboard.
- Kostenhorizont: Die Forecast-API gibt den laufenden Monat vorher, kein Jahresforecast. Fuer Jahres- und Multi-Month-Analysen das Portal oder Power BI nehmen.
- Waehrung: die Sub liefert die Kosten in der bei der Subscription hinterlegten Waehrung (EUR). Multi-Waehrung ist nicht implementiert.
- Subscription-scope: Der SP und alle Queries laufen auf genau
einer Subscription. Falls wir irgendwann mehrere bekommen, muss der
subscriptionId-Parameter pro Request konfigurierbar werden.
Incident-Handling¶
Wenn das Dashboard "Azure hat die Kostenanfrage gerade nicht
akzeptiert" zeigt:
- Admin-Portal-Backend-Logs checken:
azure_costs_*_failed-Zeilen enthalten HTTP-Status und Fehlertext. - Typische Ursachen:
- 401 / expired_token — Client-Secret ist abgelaufen. Im Portal neues Secret erzeugen, GH-Secret rotieren, Admin-Portal redeployen.
- 403 / Missing subscription registration for Microsoft.CostManagement
— Resource-Provider registrieren:
az provider register --namespace Microsoft.CostManagement. - 429 — Azure Rate Limit. Der Cache fangt das ab, aber wenn du gerade das Cache-TTL herabgesetzt hast: einfach warten.