Beherrschen des PowerShell Graph API: Leicht verständliche Einblicke

Die Microsoft Graph API ist ein Dienst, der es Ihnen ermöglicht, nahezu jeden Aspekt von Azure AD und Office 365 unter einem einzigen REST-API-Endpunkt zu lesen, zu ändern und zu verwalten. In diesem Artikel erfahren Sie, wie Sie Ihre API in die PowerShell Graph API transformieren können.

Voraussetzungen

Wenn Sie dem Artikel folgen möchten, stellen Sie bitte sicher, dass Sie die folgenden Kriterien erfüllen:

  • Windows PowerShell 5.1 ausführen (Dies ist die von mir getestete Version. Andere Versionen können funktionieren, sind jedoch nicht garantiert)
  • Einen Azure-Mandanten
  • Mit einem Konto mit globalen Administratorberechtigungen oder Berechtigungen zur App-Registrierung auf dem Abonnement und einem globalen Administrator, der Ihre App-Registrierungsanfragen akzeptiert.

Erstellen einer Anwendungsidentität für die Microsoft Graph API

Um auf die Microsoft Graph API zugreifen zu können, benötigen Sie zunächst eine Identität, um ein OAuth-Token zu erhalten. Dies geschieht in erster Linie mit einer Anwendungsidentität, die Sie im Azure-Portal erstellen können. Sie können eine Anwendungsidentität über das Azure-Portal erstellen. Gehen Sie dazu wie folgt vor:

  • Wechseln Sie zum Azure-Portal und gehen Sie zu Azure Active Directory.
  • Klicken Sie im linken Menü unter Verwalten auf Anwendungsregistrierungen und klicken Sie auf die Schaltfläche Neue Registrierung.
Authenticating before creating the PowerShell Graph API
  • Geben Sie einen Namen für Ihre Anwendung ein und klicken Sie auf Registrieren.
  • Kopieren Sie die Anwendungs-ID-GUID für die spätere Verwendung.
Registering an application

Erstellen von Secrets für die Microsoft Graph API

Sie können sich mit zwei Hauptmethoden bei der Graph API authentifizieren: AppId/Secret und zertifikatsbasierte Authentifizierung. Sie müssen sich authentifizieren, wenn Sie sich mit PowerShell bei der Graph API verbinden.

Lassen Sie uns besprechen, wie Sie sich mit beiden Methoden authentifizieren können.

AppId/Secret

Eine Anwendungs-ID/Geheimnis ist ähnlich wie ein regulärer Benutzername/Kennwort. Die Anwendungs-ID besteht aus einer GUID anstelle eines Benutzernamens, und das Kennwort ist nur eine zufällige Zeichenfolge.

Um ein Geheimnis zu erstellen, klicken Sie auf Zertifikate & Geheimnisse im linken Menü und klicken Sie auf Neues Klientengeheimnis.

Creating a client secret

Geben Sie eine Beschreibung für das Geheimnis ein und wählen Sie aus, wann es ablaufen soll. Jetzt müssen Sie nur noch um Erlaubnis bitten, damit Sie auf die gewünschten Daten zugreifen können.

Zertifikat

Es besteht die Möglichkeit, ein selbstsigniertes Zertifikat zu erstellen und den öffentlichen Schlüssel in Azure hochzuladen. Dies ist die bevorzugte und sicherere Methode der Authentifizierung.

Sie müssen zuerst ein selbstsigniertes Zertifikat generieren. Zum Glück ist dies mit PowerShell einfach erledigt.

# Ihr Mandantenname (kann auch etwas beschreibender sein)
$TenantName        = "contoso.onmicrosoft.com"

# Wohin soll das Zertifikat ohne den privaten Schlüssel exportiert werden
$CerOutputPath     = "C:\Temp\PowerShellGraphCert.cer"

# In welchem Zertifikatspeicher möchten Sie es haben
$StoreLocation     = "Cert:\CurrentUser\My"

# Ablaufdatum des neuen Zertifikats
$ExpirationDate    = (Get-Date).AddYears(2)


# Splat für bessere Lesbarkeit
$CreateCertificateSplat = @{
    FriendlyName      = "AzureApp"
    DnsName           = $TenantName
    CertStoreLocation = $StoreLocation
    NotAfter          = $ExpirationDate
    KeyExportPolicy   = "Exportable"
    KeySpec           = "Signature"
    Provider          = "Microsoft Enhanced RSA and AES Cryptographic Provider"
    HashAlgorithm     = "SHA256"
}

# Zertifikat erstellen
$Certificate = New-SelfSignedCertificate @CreateCertificateSplat

# Zertifikatpfad abrufen
$CertificatePath = Join-Path -Path $StoreLocation -ChildPath $Certificate.Thumbprint

# Zertifikat ohne privaten Schlüssel exportieren
Export-Certificate -Cert $CertificatePath -FilePath $CerOutputPath | Out-Null

Jetzt laden Sie das selbstsignierte Zertifikat, das Sie nach $CerOutputPath exportiert haben, in Ihre Azure-Anwendung hoch, indem Sie auf Zertifikate & Geheimnisse im linken Menü klicken und dann auf Zertifikat hochladen.

Uploading a certificate

Hinzufügen von Berechtigungen für die Anwendung

Das Gewähren der richtigen Berechtigungen für die Anwendung ist wichtig – nicht nur für die Funktionalität Ihrer App, sondern auch für die Sicherheit. Informationen hierzu und (fast) alles andere in der Microsoft Graph API finden Sie in der Dokumentation.

Sobald ich dies eingerichtet habe, werde ich alle Sicherheitsereignisse aus meinem Mandanten sammeln. Um dies zu dürfen, benötige ich mindestens die Berechtigung SecurityEvents.Read.All. Damit kann ich Maßnahmen bei „Impossible Travel“-Ereignissen ergreifen, Benutzer, die sich über VPN/TOR verbinden, und ähnliches.

Um die Berechtigung SecurityEvents.Read.All zu Ihrer Anwendung hinzuzufügen, klicken Sie auf API-Berechtigungen und dann auf Berechtigung hinzufügen. Dadurch werden nicht nur die Graph API, sondern auch viele andere Anwendungen in Azure angezeigt. Die meisten dieser Anwendungen lassen sich leicht verbinden, wenn Sie wissen, wie Sie sich mit der Microsoft Graph API verbinden.

Klicken Sie auf Microsoft Graph > Anwendungsberechtigungen > Sicherheitsereignisse und aktivieren Sie die Berechtigung SecurityEvents.Read.All. Drücken Sie anschließend die Schaltfläche Berechtigung hinzufügen.

Haben Sie bemerkt, dass die Spalte Admin-Zustimmung erforderlich für diese Berechtigung auf Ja gesetzt ist? Dies bedeutet, dass ein Mandantenadministrator die Zustimmung erteilen muss, bevor die Berechtigung zur Anwendung hinzugefügt wird.

Tenant admin permission approval

Wenn Sie ein globaler Administrator sind, drücken Sie die Admin-Zustimmung erteilen für oder bitten Sie einen globalen Administrator, dies zu genehmigen. Die Erlaubnis des Benutzers einzuholen, anstatt dass ein Administrator Lese-/Schreibberechtigungen festlegt, ist ein wesentlicher Bestandteil der OAuth-Authentifizierung. Dies ermöglicht uns jedoch, dies für die meisten Berechtigungen in der Microsoft Graph zu umgehen.

Sie haben dies wahrscheinlich schon auf Facebook oder Google gesehen: „Erlauben Sie Anwendung X den Zugriff auf Ihr Profil?“

Granting admin consent

Jetzt sind Sie bereit – lassen Sie uns einloggen und einige Daten abrufen!

Erhalten Sie einen Zugriffstoken (Anwendungs-ID und Geheimnis)

Dafür müssen wir eine Anfrage senden, um ein Zugriffstoken von einem Microsoft Graph OAuth-Endpunkt zu erhalten. Und im Body dieser Anfrage müssen wir folgendes bereitstellen:

  • client_id – Ihre Anwendungs-ID – URL-kodiert
  • client_secret – Ihr Anwendungsgeheimnis – URL-kodiert
  • scope – Eine URL-kodierte URL, die angibt, auf welche Ressourcen zugegriffen werden soll
  • grant_type – Die Authentifizierungsmethode, die Sie verwenden

Die URL für den Endpunkt lautet https://login.microsoftonline.com/<tenantname>/oauth2/v2.0/token. Sie können ein Zugriffstoken mit PowerShell und der Graph API anfordern, indem Sie den folgenden Code-Schnipsel verwenden.

# Definieren Sie AppId, Geheimnis und Scope, Ihren Mandanten und Endpunkt-URL
$AppId = '2d10909e-0396-49f2-ba2f-854b77c1e45b'
$AppSecret = 'abcdefghijklmnopqrstuv12345'
$Scope = "https://graph.microsoft.com/.default"
$TenantName = "contoso.onmicrosoft.com"

$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"

# Fügen Sie System.Web für die URL-Codierung hinzu
Add-Type -AssemblyName System.Web

# Erstellen Sie den Body
$Body = @{
    client_id = $AppId
	client_secret = $AppSecret
	scope = $Scope
	grant_type = 'client_credentials'
}

# Splat die Parameter für Invoke-Restmethod für saubereren Code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    # Erstellen Sie einen String, indem Sie bodylist mit '&' verbinden
    Body = $Body
    Uri = $Url
}

# Fordern Sie das Token an!
$Request = Invoke-RestMethod @PostSplat

Erhalten Sie einen Zugriffstoken (Verwendung eines Zertifikats)

Die Authentifizierung bei der Microsoft Graph API mit einem Zertifikat unterscheidet sich etwas von der normalen AppId/Secret-Flow. Um ein Zugriffstoken mit einem Zertifikat zu erhalten, müssen Sie:

  1. Erstellen Sie einen Java Web Token (JWT)-Header.
  2. Erstellen Sie eine JWT-Nutzlast.
  3. Signieren Sie den JWT-Header UND die Nutzlast mit dem zuvor erstellten selbstsignierten Zertifikat. Dadurch wird ein selbstgemachter Zugriffstoken erstellt, das für die Anforderung eines Microsoft Graph Zugriffstokens verwendet wird.
  4. Erstellen Sie einen Anforderungskörper, der enthält:
  • client_id=<Anwendungs-ID>
  • client_assertion=<der JWT>
  • client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  • scope=<URL-codierter Bereich>
  • grant_type=client_credentials
  1. Führen Sie eine Post-Anforderung mit dem Körper an den OAuth-Endpunkt mit Authorization=<JWT> in seinem Header durch.

Wie dies in der Dokumentation von Microsoft zu tun ist, war nicht offensichtlich, aber hier ist das PowerShell-Skript, um dies zu erreichen:

$TenantName = "<your tenant name>.onmicrosoft.com"
$AppId = "<your application id"
$Certificate = Get-Item Cert:\CurrentUser\My\<self signed and uploaded cert thumbprint>
$Scope = "https://graph.microsoft.com/.default"

# Erstellen Sie den Base64-Hash des Zertifikats
$CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash())

# Erstellen Sie den JWT-Zeitstempel für das Ablaufdatum
$StartDate = (Get-Date "1970-01-01T00:00:00Z" ).ToUniversalTime()
$JWTExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End (Get-Date).ToUniversalTime().AddMinutes(2)).TotalSeconds
$JWTExpiration = [math]::Round($JWTExpirationTimeSpan,0)

# Erstellen Sie den JWT-Gültigkeitsstart-Zeitstempel
$NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds
$NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0)

# Erstellen Sie den JWT-Header
$JWTHeader = @{
    alg = "RS256"
    typ = "JWT"
    # Verwenden Sie den CertificateBase64Hash und ersetzen/entfernen Sie, um die Web-Codierung von Base64 anzupassen
    x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
}

# Erstellen Sie das JWT-Payload
$JWTPayLoad = @{
    # Welcher Endpunkt darf dieses JWT verwenden
    aud = "https://login.microsoftonline.com/$TenantName/oauth2/token"

    # Ablaufzeitstempel
    exp = $JWTExpiration

    # Aussteller = Ihre Anwendung
    iss = $AppId

    # JWT-ID: Zufällige GUID
    jti = [guid]::NewGuid()

    # Nicht vorher verwenden
    nbf = $NotBefore

    # JWT-Subjekt
    sub = $AppId
}

# Header und Payload in Base64 konvertieren
$JWTHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTHeader | ConvertTo-Json))
$EncodedHeader = [System.Convert]::ToBase64String($JWTHeaderToByte)

$JWTPayLoadToByte =  [System.Text.Encoding]::UTF8.GetBytes(($JWTPayload | ConvertTo-Json))
$EncodedPayload = [System.Convert]::ToBase64String($JWTPayLoadToByte)

# Verbinden Sie Header und Payload mit "." um einen gültigen (unsignierten) JWT zu erstellen
$JWT = $EncodedHeader + "." + $EncodedPayload

# Holen Sie sich das private Schlüsselobjekt Ihres Zertifikats
$PrivateKey = $Certificate.PrivateKey

# Definieren Sie den RSA-Signatur- und Hash-Algorithmus
$RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
$HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256

# Erstellen Sie eine Signatur des JWT
$Signature = [Convert]::ToBase64String(
    $PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)
) -replace '\+','-' -replace '/','_' -replace '='

# Fügen Sie die Signatur dem JWT mit "." hinzu
$JWT = $JWT + "." + $Signature

# Erstellen Sie einen Hash mit den Körperparametern
$Body = @{
    client_id = $AppId
    client_assertion = $JWT
    client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    scope = $Scope
    grant_type = "client_credentials"

}

$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"

# Verwenden Sie das selbst generierte JWT als Autorisierung
$Header = @{
    Authorization = "Bearer $JWT"
}

# Splat die Parameter für Invoke-Restmethod für saubereren Code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    Body = $Body
    Uri = $Url
    Headers = $Header
}

$Request = Invoke-RestMethod @PostSplat

Verstehen der Anforderung Zugriffstoken-Ausgabe

Nachdem Sie ein Zugriffstoken erhalten haben, entweder über die Anwendungs-ID/-Geheimnis oder über ein Zertifikat, sollten Sie ein Objekt mit vier Eigenschaften sehen.

  • token_type – Welche Art von Token es ist
  • expires_in – Zeit in Sekunden, für die das Zugriffstoken gültig ist
  • ext_expires_in – Ähnlich wie expires_in, aber für die Widerstandsfähigkeit im Falle eines Ausfalls des Token-Dienstes
  • access_token – Wofür wir gekommen sind

Als nächstes erstellen Sie einen Header mit token_type und access_token und beginnen mit PowerShell Anfragen an die Microsoft Graph API zu senden.

Anfragen an die Microsoft Powershell Graph API senden

Beginnen Sie nun damit, Anfragen an die API zu senden.

In unserem Beispiel benötigen Sie zunächst die URL zum Auflisten von Sicherheitswarnungen. Vergessen Sie nicht, die Microsoft Graph API-Dokumentation zu konsultieren, um zu sehen, was benötigt wird.

In diesem Fall benötigen Sie einen Header mit Authorization=Bearer <access_token> und eine GET-Anforderung an den Graph API Alerts-Endpunkt. So geht’s mit PowerShell.

# Header erstellen
$Header = @{
    Authorization = "$($Request.token_type) $($Request.access_token)"
}

$Uri = "https://graph.microsoft.com/v1.0/security/alerts"

# Alle Sicherheitswarnungen abrufen
$SecurityAlertsRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"

$SecurityAlerts = $SecurityAlertsRequest.Value

Wenn Sie jetzt Sicherheitswarnungen in der Variable $SecurityAlerts haben, sollte es ungefähr so aussehen:

$SecurityAlerts | select eventDateTime,Title

eventDateTime                title
-------------                -----
2019-08-05T17:59:47.6271981Z Atypical travel
2019-08-05T08:23:01.7325708Z Anonymous IP address
2019-08-05T08:23:55.5000456Z Anonymous IP address
2019-08-04T22:06:51.063797Z  Anonymous IP address
2019-08-04T21:56:10.981437Z  Anonymous IP address
2019-08-08T09:30:00Z         Creation of forwarding/redirect rule
2019-07-19T13:30:00Z         eDiscovery search started or exported
2019-07-19T08:00:00Z         eDiscovery search started or exported

Die Inspektion eines einzelnen Sicherheitsalarms als JSON wird wie folgt aussehen:

"id":  "censored",
    "azureTenantId":  "censored",
    "azureSubscriptionId":  "censored",
    "riskScore":  null,
    "tags":  [

             ],
    "activityGroupName":  null,
    "assignedTo":  null,
    "category":  "AnonymousLogin",
    "closedDateTime":  null,
    "comments":  [

                 ],
    "confidence":  null,
    "createdDateTime":  "2019-08-08T09:46:59.65722253Z",
    "description":  "Sign-in from an anonymous IP address (e.g. Tor browser, anonymizer VPNs)",
    "detectionIds":  [

                     ],
    "eventDateTime":  "2019-08-08T09:46:59.65722253Z",
    "feedback":  null,
    "lastModifiedDateTime":  "2019-08-08T09:54:30.7256251Z",
    "recommendedActions":  [

                           ],
    "severity":  "medium",
    "sourceMaterials":  [

                        ],
    "status":  "newAlert",
    "title":  "Anonymous IP address",
    "vendorInformation":  {
                              "provider":  "IPC",
                              "providerVersion":  null,
                              "subProvider":  null,
                              "vendor":  "Microsoft"
                          },
    "cloudAppStates":  [

                       ],
    "fileStates":  [

                   ],
    "hostStates":  [

                   ],
    "historyStates":  [

                      ],
    "malwareStates":  [

                      ],
    "networkConnections":  [

                           ],
    "processes":  [

                  ],
    "registryKeyStates":  [

                          ],
    "triggers":  [

                 ],
    "userStates":  [
                       {
                           "aadUserId":  "censored",
                           "accountName":  "john.doe",
                           "domainName":  "contoso.com",
                           "emailRole":  "unknown",
                           "isVpn":  null,
                           "logonDateTime":  "2019-08-08T09:45:59.6174156Z",
                           "logonId":  null,
                           "logonIp":  "censored",
                           "logonLocation":  "Denver, Colorado, US",
                           "logonType":  null,
                           "onPremisesSecurityIdentifier":  null,
                           "riskScore":  null,
                           "userAccountType":  null,
                           "userPrincipalName":  "[email protected]"
                       }
                   ],
    "vulnerabilityStates":  [

                            ]
}

Verstehen und Verwalten der API-Ausgabe-Paging

Die Microsoft Graph API hat ein Limit pro Funktion dafür, wie viele Elemente sie zurückgibt. Diese Grenze gilt pro Funktion, aber sagen wir, es sind 1000 Elemente. Das bedeutet, dass Sie maximal 1000 Elemente in Ihrer Anfrage erhalten können.

Wenn dieses Limit erreicht ist, wird Paging verwendet, um den Rest der Elemente zu liefern. Dies geschieht, indem der @odata.nextLink-Eigenschaft zur Antwort Ihrer Anfrage hinzugefügt wird. @odata.nextLink enthält eine URL, unter der Sie die nächste Seite Ihrer Anfrage abrufen können.

Sie können alle Elemente durchgehen, indem Sie diese Eigenschaft überprüfen und eine Schleife verwenden:

$Uri = "https://graph.microsoft.com/v1.0/auditLogs/signIns"

# Alle Sicherheitswarnungen abrufen
$AuditLogRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"

$AuditLogs = @()
$AuditLogs+=$AuditLogRequest.value

while($AuditLogRequest.'@odata.nextLink' -ne $null) {
    $AuditLogRequest += Invoke-RestMethod -Uri $AuditLogRequest.'@odata.nextLink' -Headers $Header -Method Get -ContentType "application/json"
}

Abschluss

Nachdem Sie gelernt haben, wie Sie sich bei der Graph API authentifizieren, ist es ziemlich einfach, Daten daraus zu sammeln. Es ist ein leistungsstarker Dienst, der viel weniger genutzt wird, als er sollte.

Zum Glück gibt es bereits viele Module, um die Microsoft Graph API zu nutzen, aber um Ihre eigenen Anforderungen zu erfüllen, müssen Sie möglicherweise Ihr eigenes Modul dafür erstellen. Mit den in diesem Artikel erlernten Fähigkeiten sollten Sie gut vorankommen.

Für weitere Informationen zur Steuerung des Gastzugriffs in Office 365 habe ich einen ausführlichen Artikel in meinem Blog geschrieben, den ich Ihnen ans Herz lege, überprüfen Sie ihn.

Source:
https://adamtheautomator.com/powershell-graph-api/