Entra Credential Monitor
Anleitung

API, erste Schritte

Alles, was die Oberfläche kann, geht auch über /api/v1: Kunden anlegen, Laufzeiten auslesen, Prüfungen auslösen, Sensor-URLs abholen. Gedacht für ein übergeordnetes Cloud-Portal oder ein Onboarding-Script.

Arbeitsteilung: Die Oberfläche ist für Administratoren, die einen Einzelfall geradeziehen. Die API ist für Techniker und angeschlossene Systeme, die dasselbe im Durchlauf erledigen. Beide sprechen dieselbe Datenbank, es gibt keinen zweiten Zustand.

Alle Beispiele auf dieser Seite stammen aus einer laufenden Instanz mit Beispieldaten, erzeugt von tools/demo_pages.py. Sie sind nicht abgetippt.

1. Schlüssel ausstellen

Im Portal unter API. Ein Schlüssel hat einen Bereich, und der entscheidet, was mit ihm geht.

BereichErlaubtFür wen
readAlle GET-AufrufeSysteme, die nur Zahlen abholen
writeZusätzlich POST, PATCH, DELETEOnboarding, Cloud-Portal
API-Seite des Portals mit Formular zum Ausstellen eines Schlüssels, Liste zweier bestehender Schlüssel mit Präfix und Bereich, sowie der Tabelle aller Endpunkte.
Gespeichert wird nur der Hash. Der Schlüssel selbst erscheint genau einmal, direkt nach dem Ausstellen. Wer ihn verliert, stellt einen neuen aus.

Übergeben wird er als Bearer-Token:

curl -H "Authorization: Bearer esm_xxxxxxxx_..." \
  https://entra-portal.example.com/api/v1/

Die Wurzel listet Version, Instanz und alle Endpunkte mit ihrem Bereich. Sie eignet sich als erster Test, ob der Schlüssel funktioniert:

{
  "version": "1.0",
  "instance": "Entra Credential Portal",
  "scope": "write",
  "endpoints": [
    { "method": "GET",  "path": "/api/v1/customers",       "scope": "read",  "purpose": "..." },
    { "method": "POST", "path": "/api/v1/customers",       "scope": "write", "purpose": "..." },
    { "method": "GET",  "path": "/api/v1/problems",        "scope": "read",  "purpose": "..." }
  ]
}

Die maschinenlesbare Beschreibung liegt unter /api/v1/openapi.json und lässt sich direkt in einen Client-Generator geben.

2. Kunde anlegen

Ein POST mit Tenant-ID, Client-ID und dem Zugangsdatum. Der Schlüssel muss kleingeschrieben sein.

curl -X POST https://entra-portal.example.com/api/v1/customers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "key": "musterag",
        "display_name": "Muster AG",
        "tenant_id": "99999999-1111-2222-3333-444444444444",
        "client_id": "88888888-5555-6666-7777-888888888888",
        "auth_type": "secret",
        "client_secret": "beispiel-secret-aus-der-app"
      }'

Antwort 201 Created, gekürzt auf das Wesentliche:

{
  "key": "musterag",
  "display_name": "Muster AG",
  "auth_type": "secret",
  "has_credential": true,
  "is_active": true,
  "state": "unknown",
  "scan": {
    "status": "pending",
    "last_check_at": null,
    "slot_minute": 1200
  },
  "summary": { "count_total": 0, "count_critical": 0, "min_days": null },
  "problems": [
    {
      "code": "never_checked",
      "severity": "warn",
      "message": "Dieser Kunde wurde noch nie erfolgreich geprüft.",
      "action": "Eine Prüfung auslösen oder den nächsten Tageslauf abwarten."
    }
  ]
}

Der Kunde ist angelegt, aber noch nie geprüft. slot_minute ist die Tagesminute, zu der er künftig automatisch geprüft wird; das Portal verteilt neue Kunden in die grösste Lücke.

Mit Zertifikat statt Secret

{
  "key": "contoso",
  "display_name": "Contoso AG",
  "tenant_id": "8f3a1c22-9b41-4d7e-a512-6ce0f9b27d44",
  "client_id": "2d9f4b18-77ac-4e63-9f1a-b58d3c07e912",
  "auth_type": "certificate",
  "cert_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n",
  "key_pem": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
}

Sofort prüfen statt warten

curl -X POST https://entra-portal.example.com/api/v1/customers/musterag/check \
  -H "Authorization: Bearer $TOKEN"

Die Antwort enthält das Ergebnis des Laufs, nicht nur eine Bestätigung. Ein Onboarding-Script kann damit direkt melden, ob die Zugangsdaten stimmen.

3. Laufzeiten auslesen

Secret-Werte gibt es nirgends. Weder die API noch Microsoft Graph geben den Wert eines Client Secrets heraus. Ausgelesen werden Metadaten: welche Anwendung, welcher Typ, wann läuft es ab. Genau das ist der Zweck.

curl -H "Authorization: Bearer $TOKEN" \
  https://entra-portal.example.com/api/v1/customers/contoso/credentials
{
  "customer": "contoso",
  "count": 3,
  "last_check_at": "2026-09-08T17:05:28+00:00",
  "credentials": [
    {
      "app_name": "SVC-Backup Veeam",
      "app_id": "00000001-aaaa-bbbb-cccc-000000000000",
      "type": "secret",
      "credential_name": "prod",
      "days_left": 5,
      "end_date": "2026-09-13T18:05:28+00:00",
      "object_type": "application",
      "sibling_count": 1
    },
    {
      "app_name": "ConnectSyncProvisioning",
      "type": "cert",
      "days_left": 86,
      "end_date": "2026-12-03T18:05:28+00:00",
      "sibling_count": 2
    }
  ]
}

Sortiert ist nach kürzester Restlaufzeit. sibling_count sagt, wie viele Credentials derselben Anwendung und desselben Typs zusammengefasst wurden; gezählt wird immer die längste Laufzeit, weil ein frisch gerolltes Secret das alte irrelevant macht.

Nur das, was Aufmerksamkeit braucht

Wer nicht alle Kunden durchgehen und selbst bewerten will, nimmt /problems. Die Reihenfolge ist die der Dringlichkeit.

curl -H "Authorization: Bearer $TOKEN" \
  https://entra-portal.example.com/api/v1/problems
{
  "checked_customers": 5,
  "count": 3,
  "customers": [
    {
      "key": "contoso",
      "display_name": "Contoso AG",
      "state": "error",
      "min_days": 5,
      "problems": [
        {
          "code": "credential_critical",
          "severity": "error",
          "message": "SVC-Backup Veeam (prod) läuft in weniger als 14 Tagen ab.",
          "action": "Erneuerung einplanen.",
          "applications": ["SVC-Backup Veeam (prod)"],
          "min_days_left": 5,
          "count": 1
        }
      ]
    },
    {
      "key": "seewerk",
      "display_name": "Seewerk Industrie",
      "state": "error",
      "problems": [{ "code": "scan_failed", "severity": "error", "message": "..." }]
    }
  ]
}

Von fünf Kunden tauchen hier nur die drei auf, bei denen etwas ist. Jeder Befund hat einen code zum Auswerten und einen Satz Klartext zum Anzeigen.

CodeBedeutung
credential_criticalRestlaufzeit unter der Fehlerschwelle
credential_warningRestlaufzeit unter der Warnschwelle
credential_expiredbereits abgelaufen
scan_failedletzter Lauf ist gescheitert, Details in scan.error
never_checkednoch nie erfolgreich geprüft
no_credentialkein Zugangsdatum der gewählten Anmeldeart hinterlegt
inactiveÜberwachung für diesen Kunden abgeschaltet

4. XML auslesen

Das PRTG-XML hängt nicht am API-Schlüssel, sondern an einem eigenen Token pro Kunde. So kann eine PRTG-Instanz die Sensordaten holen, ohne einen Schlüssel zu besitzen, der Kunden anlegen könnte.

curl -H "Authorization: Bearer $TOKEN" \
  https://entra-portal.example.com/api/v1/customers/contoso/urls
{
  "prtg_xml": "https://entra-portal.example.com/prtg/SENSOR-TOKEN-AUS-DER-ANTWORT",
  "json":     "https://entra-portal.example.com/json/SENSOR-TOKEN-AUS-DER-ANTWORT",
  "hinweis":  "Anhängbar sind ?app=, ?filter=, ?exclude=, ?type=secret|cert, ?warn= und ?error= für eigene Schwellen."
}

Die prtg_xml-URL kommt in einen Sensor vom Typ HTTP Data Advanced. Sie braucht keinen weiteren Kopf, der Token steht im Pfad:

curl https://entra-portal.example.com/prtg/SENSOR-TOKEN-AUS-DER-ANTWORT
<?xml version="1.0" encoding="UTF-8" ?>
<prtg>
  <result>
    <channel>Minimale Restlaufzeit</channel>
    <value>5</value>
    <unit>Custom</unit>
    <customunit>Tage</customunit>
    <limitmode>1</limitmode>
    <limitminwarning>30</limitminwarning>
    <limitminerror>14</limitminerror>
  </result>
  <result>
    <channel>SVC-Backup Veeam (Secret)</channel>
    <value>5</value>
    ...
  </result>
</prtg>

Ein Abruf dieser URL löst keine Graph-Anfrage aus. Geliefert wird der gespeicherte Stand des letzten Laufs. PRTG darf also häufig fragen, ohne dass Microsoft häufiger gefragt wird.

Ein kompromittierter Token wird durch einen neuen ersetzt. Die bisherige URL liefert danach nichts mehr:

curl -X POST https://entra-portal.example.com/api/v1/customers/contoso/token \
  -H "Authorization: Bearer $TOKEN"

5. Fehler auslesen

Fehler kommen immer im selben Umschlag: ein Objekt error mit code, message und bei Eingabefehlern zusätzlich fields.

Ungültige Eingabe, 422

Ein Schlüssel mit Grossbuchstaben, der häufigste Fall beim Onboarding:

{
  "error": {
    "code": "validation_failed",
    "message": "Eingaben unvollständig.",
    "fields": {
      "key": "Grossbuchstaben sind im Schlüssel nicht erlaubt. Verwende musterag."
    }
  }
}

fields ist nach Feldnamen aufgeschlüsselt und lässt sich direkt an ein Formular zurückgeben. Die Meldung ist wörtlich dieselbe wie in der Oberfläche, damit Techniker und Administrator dieselbe Anweisung lesen.

Zugangsdatum passt nicht zur Anmeldeart, 422

Wichtig für ein Rotations-Script: ein client_secret für einen Kunden, der auf Zertifikat steht, wird abgelehnt statt stillschweigend verworfen.

{
  "error": {
    "code": "validation_failed",
    "message": "Eingaben unvollständig.",
    "fields": {
      "client_secret": "Gehört nicht zu auth_type 'certificate'. Feld weglassen oder leer senden."
    }
  }
}

Ein echter Wechsel der Anmeldeart geht weiterhin. Dabei muss auth_type mitkommen und das Feld der alten Methode weggelassen oder leer gesendet werden:

curl -X PATCH https://entra-portal.example.com/api/v1/customers/contoso   -H "Authorization: Bearer $TOKEN"   -H "Content-Type: application/json"   -d '{"auth_type": "secret", "client_secret": "neuer-wert"}'

Unbekannter Kunde, 404

{
  "error": {
    "code": "not_found",
    "message": "Kein Kunde mit diesem Schlüssel."
  }
}

Statuscodes

Statuserror.codeWannWas tun
200-Abfrage erfolgreich-
201-Kunde angelegt-
401unauthenticatedKopf Authorization fehltBearer-Token mitgeben
401invalid_keySchlüssel falsch oder widerrufenNeuen Schlüssel ausstellen
403read_onlySchlüssel ist read, Aufruf braucht writeSchreibenden Schlüssel verwenden
404not_foundKunde existiert nichtKundenschlüssel prüfen
409duplicateKundenschlüssel bereits vergebenAnderen Schlüssel wählen oder PATCH benutzen
422validation_failedEingabe unvollständig oder ungültigfields auswerten
429rate_limitedZu viele AufrufeKopf Retry-After abwarten

Die Codes in dieser Tabelle sind keine Behauptung: tools/demo_pages.py löst jeden davon bei jedem Lauf einmal aus und schreibt die Antwort nach api-statuscodes.json.

Fehler eines Prüflaufs

Ein gescheiterter Lauf ist kein HTTP-Fehler. Der Kunde antwortet mit 200, der Fehler steht im Objekt scan:

{
  "key": "seewerk",
  "state": "error",
  "scan": {
    "status": "error",
    "last_check_at": "2026-09-08T12:05:28+00:00",
    "last_success_at": null,
    "error": "AADSTS7000215: Ungültiges Client Secret. Secret abgelaufen oder falsch hinterlegt."
  }
}

Der AADSTS-Code von Entra bleibt erhalten. Damit steht die Ursache im überwachenden System, ohne dass jemand ins Portal schauen muss.

Rate Limits

Schreibende Aufrufe und Prüfungen sind begrenzt, damit ein fehlerhaftes Script weder die Instanz noch Microsoft Graph flutet. Bei Überschreitung kommt 429 mit der Wartezeit in der Antwort. Ein Onboarding, das Kunden nacheinander anlegt, läuft nie hinein.

Weiter