LAPS Prozessautomatisierung

Willkommen in meinem nächsten Blogartikel.
Ihr habt LAPS (Local Administrator Password Solution) erfolgreich in eurer Umgebung eingeführt – ein wichtiger Schritt für mehr Sicherheit und Compliance. Doch wie geht es nun weiter? Viele Unternehmen möchten den nächsten Komfortschritt gehen: die Integration der Passwortausgabe in ihren Self-Service.
In diesem Artikel zeige ich euch, wie ihr genau das umsetzen könnt – und zwar mithilfe der Microsoft Graph API in Kombination mit einer Logic App. Damit könnt ihr den Prozess automatisieren und euren Anwendern eine einfache, sichere Möglichkeit bieten, benötigte Administratorpasswörter selbstständig abzurufen.

Alles was ihr für dieses Szenario benötigt sind ein Microsoft Forms Formular und eine Logic App.

Inhalt

    Schritt 1 – Trigger

    Jede Logic App benötigt einen Trigger. In meinem Szenario verwende ich dafür ein Microsoft Forms-Formular, über das der antragstellende Benutzer die erforderlichen Informationen einträgt. Besonders wichtig ist dabei der Name des Geräts, für das das LAPS-Passwort benötigt wird.

    Darüber hinaus können zusätzliche, hilfreiche Angaben abgefragt werden – beispielsweise Informationen, die für die Erstellung eines Tickets im IT-System relevant sind. Ebenso kann es sinnvoll sein, eine Begründung (Justification) zu erfassen, die anschließend im Ticket hinterlegt wird.

    Nachdem wir das Formular erstellt haben, binden wir es nun in unsere Logic App ein. Der erste Schritt besteht darin, den Trigger so zu konfigurieren, dass er auf neue Formularübermittlungen reagiert. Sobald ein Benutzer das Formular ausfüllt, wird die Logic App automatisch gestartet.

    Im nächsten Schritt rufen wir die übermittelten Antwortinformationen ab. Diese Daten sind die Grundlage für alle weiteren Aktionen in unserem Workflow. Typischerweise handelt es sich dabei um den Gerätenamen, für den das LAPS-Passwort benötigt wird, sowie zusätzliche Angaben wie Ticketnummer oder Begründung.

    Diese Informationen können wir anschließend in den folgenden Schritten flexibel weiterverarbeiten.

    Schritt 2 – Managed Identity berechtigen

    Bevor wir die Graph API-Aufrufe in unsere Logic App integrieren, müssen wir sicherstellen, dass die notwendigen Berechtigungen vorhanden sind. Für die Authentifizierung verwenden wir die Managed Identity der Logic App.

    Wie ihr eine Managed Identity einrichtet und die entsprechenden Berechtigungen zuweist, findet ihr in der offiziellen Microsoft-Dokumentation. Wichtig ist, dass die Identity der Logic App folgende Berechtigungen in Microsoft Graph erhält:

    • Device.Read.All – um Geräteinformationen wie die Device-ID abzurufen
    • DeviceLocalCredential.Read.All – um die LAPS-Passwörter auslesen zu können

    Diese Berechtigungen müssen als Application Permissions in der App-Registrierung hinterlegt und von einem Administrator genehmigt werden. Erst danach kann die Logic App erfolgreich auf die benötigten Daten zugreifen.

    Folgendes PowerShell Skript hilft euch dabei.

    #Connect to AzureAD mit einem entsprechenden Admin Account
    Connect-AzureAD
    
    #New Service Principal Permissions using Azure AD module
    $ServicePrincipalId = <ID eurer managed identity>
    # App ID der notwendigen Graph API
    $GraphResource = Get-AzureADServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
    
    #Set role DeviceLocalCredential.Read.All
    $Permission = $GraphResource.AppRoles | Where-Object {$_.value -eq 'DeviceLocalCredential.Read.All'}
    New-AzureADServiceAppRoleAssignment -ObjectId $ServicePrincipalId -PrincipalId $ServicePrincipalId -Id $Permission.Id -ResourceId $GraphResource.ObjectId
    
    #Set role Device.Read.All
    $Permission = $GraphResource.AppRoles | Where-Object {$_.value -eq 'Device.Read.All'}
    New-AzureADServiceAppRoleAssignment -ObjectId $ServicePrincipalId -PrincipalId $ServicePrincipalId -Id $Permission.Id -ResourceId $GraphResource.ObjectId

    Bevor wir nun in der Logic App die Calls ausführen, kann es sinnvoll sein ein paar Minuten oder Stunden zu warten, bis die Berechtigungen vollends greifen.

    Schritt 3 – Device ID ermitteln

    Damit wir das LAPS-Passwort für ein bestimmtes Gerät über die Microsoft Graph API abrufen können, benötigen wir zunächst die Device-ID. Diese ID ist der eindeutige Schlüssel, den die API für den Zugriff auf die lokalen Administrator-Anmeldeinformationen erwartet.

    Da uns aktuell nur der Gerätename vorliegt, müssen wir diesen zunächst in die entsprechende Device-ID auflösen. Dazu führen wir einen GET-Request an den Graph-Endpunkt aus und geben den dynamischen Wert aus dem Forms mit:

    https://graph.microsoft.com/v1.0/devices?$filter=displayName+eq+'<dynamic_content_forms_Device Name>'

    Achtet darauf, dass ihr sowohl die korrekte URI als auch die richtige HTTP-Methode verwendet. Um den dynamischen Wert aus dem Microsoft Forms-Formular einzufügen, könnt ihr den Shortcut „/“ nutzen. Dadurch öffnet sich ein Auswahlmenü, in dem alle verfügbaren dynamischen Inhalte angezeigt werden. So könnt ihr den entsprechenden Wert bequem in eure HTTP-Anfrage einfügen.

    Zum Abschluss ist es entscheidend, die korrekte Authentifizierung zu konfigurieren. Hierfür verwenden wir die zu Beginn erstellte Managed Identity.

    In der HTTP-Aktion wählt ihr als Authentifizierungstyp Managed Identity aus. Unter dem Feld Audience muss der Wert gesetzt werden auf:

    00000003-0000-0000-c000-000000000000

    Dieser Wert steht für Microsoft Graph und ist erforderlich, damit die Logic App die API-Aufrufe erfolgreich ausführen kann.

    Der API-Aufruf liefert uns eine JSON-Antwort, die wir für die weitere Verarbeitung zunächst aufbereiten müssen. Dafür nutzen wir in der Logic App die Aktion „Parse JSON“.

    • Im Feld Content geben wir den dynamischen Wert Body des vorherigen HTTP-Calls an.
    • Anschließend hinterlegen wir das passende Schema, damit die Logic App die Struktur der Antwort kennt und wir die einzelnen Werte (z. B. das Passwort) in den nächsten Schritten einfach referenzieren können.
    {
      "type": "object",
      "properties": {
        "@@odata.context": {
          "type": "string"
        },
        "value": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "deletedDateTime": {},
              "accountEnabled": {
                "type": "boolean"
              },
              "approximateLastSignInDateTime": {
                "type": "string"
              },
              "complianceExpirationDateTime": {},
              "createdDateTime": {
                "type": "string"
              },
              "deviceCategory": {},
              "deviceId": {
                "type": "string"
              },
              "deviceMetadata": {},
              "deviceOwnership": {
                "type": "string"
              },
              "deviceVersion": {
                "type": "integer"
              },
              "displayName": {
                "type": "string"
              },
              "domainName": {},
              "enrollmentProfileName": {
                "type": "string"
              },
              "enrollmentType": {
                "type": "string"
              },
              "externalSourceName": {},
              "isCompliant": {
                "type": "boolean"
              },
              "isManaged": {
                "type": "boolean"
              },
              "isRooted": {
                "type": "boolean"
              },
              "managementType": {
                "type": "string"
              },
              "manufacturer": {
                "type": "string"
              },
              "mdmAppId": {
                "type": "string"
              },
              "model": {
                "type": "string"
              },
              "onPremisesLastSyncDateTime": {},
              "onPremisesSyncEnabled": {},
              "operatingSystem": {
                "type": "string"
              },
              "operatingSystemVersion": {
                "type": "string"
              },
              "physicalIds": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "profileType": {
                "type": "string"
              },
              "registrationDateTime": {
                "type": "string"
              },
              "sourceType": {},
              "systemLabels": {
                "type": "array"
              },
              "trustType": {
                "type": "string"
              },
              "extensionAttributes": {
                "type": "object",
                "properties": {
                  "extensionAttribute1": {},
                  "extensionAttribute2": {},
                  "extensionAttribute3": {},
                  "extensionAttribute4": {},
                  "extensionAttribute5": {},
                  "extensionAttribute6": {},
                  "extensionAttribute7": {},
                  "extensionAttribute8": {},
                  "extensionAttribute9": {},
                  "extensionAttribute10": {},
                  "extensionAttribute11": {},
                  "extensionAttribute12": {},
                  "extensionAttribute13": {},
                  "extensionAttribute14": {},
                  "extensionAttribute15": {}
                }
              },
              "alternativeSecurityIds": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "integer"
                    },
                    "identityProvider": {},
                    "key": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "type",
                    "identityProvider",
                    "key"
                  ]
                }
              }
            },
            "required": [
              "id",
              "deletedDateTime",
              "accountEnabled",
              "approximateLastSignInDateTime",
              "complianceExpirationDateTime",
              "createdDateTime",
              "deviceCategory",
              "deviceId",
              "deviceMetadata",
              "deviceOwnership",
              "deviceVersion",
              "displayName",
              "domainName",
              "enrollmentProfileName",
              "enrollmentType",
              "externalSourceName",
              "isCompliant",
              "isManaged",
              "isRooted",
              "managementType",
              "manufacturer",
              "mdmAppId",
              "model",
              "onPremisesLastSyncDateTime",
              "onPremisesSyncEnabled",
              "operatingSystem",
              "operatingSystemVersion",
              "physicalIds",
              "profileType",
              "registrationDateTime",
              "sourceType",
              "systemLabels",
              "trustType",
              "extensionAttributes",
              "alternativeSecurityIds"
            ]
          }
        }
      }
    }

    Nun haben wir aus dem angegebenen Gerätenamen die erforderliche Device-ID ermittelt. Beachte, dass die Logic App an dieser Stelle automatisch eine „For Each“-Schleife erstellt. Der Grund: Der API-Aufruf könnte theoretisch mehrere Ergebnisse zurückliefern – zum Beispiel, wenn mehrere Geräte denselben Namen haben, was aber nicht sein kann.

    Schritt 4 – LAPS Passwort erhalten

    Mit der ermittelten Device-ID können wir nun das LAPS-Passwort abrufen. Dazu führen wir einen weiteren Graph API-Aufruf aus, der die Device-ID als Parameter verwendet.

    Der entsprechende Endpunkt lautet:

    https://graph.microsoft.com/beta/directory/deviceLocalCredentials<dynamische Device ID>?$select=credentials

    Zusätzlich müssen wir in diesem Schritt bestimmte Header-Informationen mitgeben, damit der API-Aufruf korrekt verarbeitet wird. Dazu gehören:

    • User-Agent = Dsreg/10.0 (Windows 10.0.19043.1466)
    • ocp-client-name = My Friendly Client
    • ocp-client-version = 1.2

    Vergesst außerdem nicht, am Ende wieder die richtige Authentifizierung zu konfigurieren – wie zuvor beschrieben über die Managed Identity mit dem entsprechenden Audience-Wert für Microsoft Graph.

    Und wieder die JSON- Antwort auflösen mit folgendem Schema

    {
      "type": "object",
      "properties": {
        "@@odata.context": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "deviceName": {
          "type": "string"
        },
        "lastBackupDateTime": {
          "type": "string"
        },
        "refreshDateTime": {
          "type": "string"
        },
        "credentials": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "accountName": {
                "type": "string"
              },
              "accountSid": {
                "type": "string"
              },
              "backupDateTime": {
                "type": "string"
              },
              "passwordBase64": {
                "type": "string"
              }
            },
            "required": [
              "accountName",
              "accountSid",
              "backupDateTime",
              "passwordBase64"
            ]
          }
        }
      }
    }

    Je nach Konfiguration von LAPS und der eingestellten Gültigkeitsdauer der Passwörter kann es vorkommen, dass der API-Aufruf mehrere gültige Passwörter zurückliefert. Aus diesem Grund erstellt die Logic App an dieser Stelle automatisch eine „For Each“-Schleife, um alle zurückgegebenen Einträge zu verarbeiten.

    Schritt 5 – Passwort an User ausgeben

    Jetzt, da wir das Passwort erfolgreich abgerufen haben, müssen wir es natürlich noch an den Benutzer übermitteln. In meinem Beispiel nutze ich dafür Microsoft Teams und sende über den Flow Bot eine Chat-Nachricht an den Benutzer, der das Formular eingereicht hat. Dazu muss zusätzlich ein Account vorhanden sein, um eine Teams Connection aufzubauen. Beispielsweise kann aber auch Ticketsystem oder anderes dafür genutzt werden.

    Den Namen des lokalen Admins können wir wieder über eine dynamische Abfrage aus dem Graph Call bekommen.

    Das von der Graph API zurückgegebene Passwort ist Base64-codiert. Bevor wir es in der Nachricht anzeigen, müssen wir es also dekodieren. In der Logic App lässt sich das ganz einfach mit der Funktion decodeBase64() umsetzen:

    decodeBase64(item()?['passwordBase64'])

    So stellen wir sicher, dass der Benutzer das Passwort in Klartext erhält, ohne zusätzliche Schritte durchführen zu müssen.

    Conclusio

    Mit der Kombination aus Microsoft Forms, Logic Apps und der Microsoft Graph API lässt sich die Ausgabe von LAPS-Passwörtern effizient und sicher automatisieren. Der Prozess bietet nicht nur Komfort für Anwender, sondern sorgt auch für Nachvollziehbarkeit und reduziert manuelle Eingriffe. Durch den Einsatz von Managed Identities, klar definierten Berechtigungen und Best Practices wie Logging und Justification bleibt die Lösung sicher und compliant. Der Prozess kann weiterhin beliebig angepasst werden und beispielsweise durch einen Freigabeprozess erweitert werden.

    Schreibe einen Kommentar

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert