Beheersing van PowerShell Graph API: Makkelijk te volgen inzichten

De Microsoft Graph API is een dienst waarmee je bijna elk aspect van Azure AD en Office 365 onder één enkel REST API-eindpunt kunt lezen, aanpassen en beheren. In dit artikel leer je hoe je je API kunt transformeren naar de PowerShell Graph API.

Vereisten

Als je met me mee wilt doen in dit artikel, zorg er dan voor dat je eerst aan de volgende criteria voldoet:

  • Windows PowerShell 5.1 uitvoeren (Dit is de versie waarmee ik heb getest. Andere versies kunnen werken maar worden niet gegarandeerd)
  • Een Azure-tenant
  • Geauthenticeerd voor Azure met een account met globale beheerdersrechten of toestemmingen voor app-registratie op het abonnement en een globale beheerder om je app-registratieverzoeken te accepteren.

Het maken van een toepassingsidentiteit voor de Microsoft Graph API

Om toegang te krijgen tot de Microsoft Graph API heb je eerst een identiteit nodig om een OAuth-token te krijgen. Dit wordt voornamelijk gedaan met een toepassingsidentiteit die je kunt maken in het Azure-portal. Je kunt een toepassingsidentiteit maken via het Azure-portal. Zo doe je dat:

  • Ga naar het Azure-portal en ga naar Azure Active Directory.
  • Klik op App-registraties onder Beheren in het linker menu en klik op de knop Nieuwe registratie.
Authenticating before creating the PowerShell Graph API
  • Voer een naam in voor je toepassing en klik op Registreren.
  • Kopieer de Application Id-guid voor later gebruik.
Registering an application

Het maken van geheimen voor de Microsoft Graph API

Je kunt authenticeren bij de Graph API met twee primaire methoden: AppId/Secret en certificaatgebaseerde authenticatie. Je moet authenticeren bij het verbinden met de Graph API via PowerShell.

Laten we bespreken hoe je kunt authenticeren met beide methoden.

AppId/Secret

Een toepassings-ID/geheim is vergelijkbaar met een gewone gebruikersnaam/wachtwoord. De toepassings-ID bestaat uit een GUID in plaats van een gebruikersnaam en het wachtwoord is gewoon een willekeurige reeks tekens.

Om een geheim te maken, klik op Certificaten en geheimen in het linker menu en druk op Nieuw cliëntgeheim.

Creating a client secret

Voer een beschrijving in voor het geheim en selecteer wanneer je wilt dat het verloopt. Nu is het slechts een kwestie van toestemming vragen, zodat je toegang hebt tot de gegevens die je nodig hebt.

Certificaat

Het is mogelijk om een zelfondertekend certificaat te maken en de openbare sleutel ervan naar Azure te uploaden. Dit is de voorkeurs- en veiligere manier van authenticatie.

Je moet eerst een zelfondertekend certificaat genereren. Gelukkig is dit eenvoudig te doen met PowerShell.

# Uw huurdersnaam (kan ook iets meer beschrijvend zijn)
$TenantName        = "contoso.onmicrosoft.com"

# Waar het certificaat te exporteren zonder de privésleutel
$CerOutputPath     = "C:\Temp\PowerShellGraphCert.cer"

# In welke certificaatopslag wilt u het hebben
$StoreLocation     = "Cert:\CurrentUser\My"

# Vervaldatum van het nieuwe certificaat
$ExpirationDate    = (Get-Date).AddYears(2)


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

# Certificaat maken
$Certificate = New-SelfSignedCertificate @CreateCertificateSplat

# Pad van het certificaat ophalen
$CertificatePath = Join-Path -Path $StoreLocation -ChildPath $Certificate.Thumbprint

# Certificaat exporteren zonder privésleutel
Export-Certificate -Cert $CertificatePath -FilePath $CerOutputPath | Out-Null

Upload nu het zelfondertekende certificaat dat u hebt geëxporteerd naar $CerOutputPath naar uw Azure-toepassing door te klikken op Certificaten en geheimen in het linker menu en te klikken op Certificaat uploaden.

Uploading a certificate

Permissies toevoegen aan de applicatie

Het toekennen van de juiste permissies aan de applicatie is belangrijk – niet alleen voor de functionaliteit van uw app, maar ook voor de beveiliging. Kennis hiervan en (bijna) alles in de Microsoft Graph API is te vinden in de documentatie.

Zodra ik dit heb opgezet, ga ik alle beveiligingsevenementen van mijn huurder verzamelen. Om dat te mogen doen, heb ik minimaal de toestemming SecurityEvents.Read.All nodig. Hiermee kan ik actie ondernemen bij gebeurtenissen zoals onmogelijke reizen, gebruikers die verbinding maken via VPN/TOR, en dergelijke.

Om de toestemming SecurityEvents.Read.All toe te voegen aan je applicatie, klik je op API-machtigingen en vervolgens op Machtiging toevoegen. Dit geeft je niet alleen toegang tot de Graph API, maar ook tot tal van andere applicaties in Azure. De meeste van deze applicaties zijn eenvoudig te verbinden zodra je weet hoe je verbinding kunt maken met de Microsoft Graph API.

Klik op Microsoft Graph > Applicatiemachtigingen > Beveiligingsevenementen en vink SecurityEvents.Read.All aan. Druk daarna op de knop Machtiging toevoegen.

Zag je dat de kolom Beheerdersgoedkeuring vereist was ingesteld op Ja voor die machtiging? Dit betekent dat een beheerder van de huurder de toestemming moet goedkeuren voordat deze aan de applicatie wordt toegevoegd.

Tenant admin permission approval

Als je een globale beheerder bent, druk dan op de knop Beheerdersgoedkeuring verlenen voor of vraag een Globale Beheerder om het goed te keuren. Het vragen van toestemming aan de gebruiker in plaats van een beheerder die lees-/schrijftoestemming instelt, is een groot onderdeel van OAuth-authenticatie. Maar dit stelt ons in staat om dit te omzeilen voor de meeste machtigingen in Microsoft Graph.

Je hebt dit waarschijnlijk al gezien op Facebook of Google: “Sta je applicatie X toe om toegang te krijgen tot je profiel?”

Granting admin consent

Nu ben je er helemaal klaar voor – laten we inloggen en wat gegevens ophalen!

Verkrijg een Toegangstoken (Applicatie-id en Geheim)

Hiervoor moeten we een verzoek indienen om een toegangstoken te verkrijgen vanaf een Microsoft Graph OAuth-eindpunt. En in de body van dat verzoek moeten we het volgende verstrekken:

  • client_id – Jouw Applicatie-id – URL-gecodeerd
  • client_secret – Jouw applicatiegeheim – URL-gecodeerd
  • scope – Een URL-gecodeerde URL die aangeeft waartoe je toegang wilt
  • grant_type – Welke authenticatiemethode je gebruikt

De URL voor het eindpunt is https://login.microsoftonline.com/<tenantnaam>/oauth2/v2.0/token. Je kunt een toegangstoken aanvragen met PowerShell en de Graph API met behulp van de onderstaande code.

# Definieer AppId, geheim en scope, jouw tenantnaam en eindpunt-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"

# Voeg System.Web toe voor URL-codering
Add-Type -AssemblyName System.Web

# Creëer body
$Body = @{
    client_id = $AppId
	client_secret = $AppSecret
	scope = $Scope
	grant_type = 'client_credentials'
}

# Splat de parameters voor Invoke-Restmethod voor schonere code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    # Creëer string door bodylist te verbinden met '&'
    Body = $Body
    Uri = $Url
}

# Vraag het token aan!
$Request = Invoke-RestMethod @PostSplat

Verkrijg een Toegangstoken (Gebruikmakend van een Certificaat)

Het authenticeren bij de Microsoft Graph API met een certificaat verschilt enigszins van de normale AppId/Secret-flow. Om een toegangstoken te verkrijgen met een certificaat moet je:

  1. Creëer een Java Web Token (JWT)-header.
  2. Creëer een JWT-payload.
  3. Onderteken de JWT-header EN payload met het eerder gemaakte zelfondertekende certificaat. Hierdoor wordt een zelfgemaakt toegangstoken gecreëerd dat wordt gebruikt voor het aanvragen van een Microsoft Graph-toegangstoken.
  4. Creëer een verzoeklichaam dat het volgende bevat:
  • client_id=<toepassings-id>
  • client_assertion=<de JWT>
  • client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  • scope=<URLGecodeerde scope>
  • grant_type=client_credentials
  1. Voer een postverzoek uit met het lichaam naar het OAuth-eindpunt met Authorization=<JWT> in de kop.

Hoe dit te doen was niet duidelijk in de documentatie van Microsoft, maar hier is het PowerShell-script om dit te laten gebeuren:

$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"

# Maak base64-hash van het certificaat
$CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash())

# Maak JWT-tijdstempel voor vervaldatum
$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)

# Maak JWT-geldigheidsstarttijdstempel
$NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds
$NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0)

# Maak JWT-header
$JWTHeader = @{
    alg = "RS256"
    typ = "JWT"
    # Gebruik CertificateBase64Hash en vervang/strook om overeen te komen met webcodering van base64
    x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
}

# Maak JWT-payload
$JWTPayLoad = @{
    # Welk eindpunt mag deze JWT gebruiken
    aud = "https://login.microsoftonline.com/$TenantName/oauth2/token"

    # Vervaltijdstempel
    exp = $JWTExpiration

    # Uitgever = jouw toepassing
    iss = $AppId

    # JWT ID: willekeurige GUID
    jti = [guid]::NewGuid()

    # Niet te gebruiken voor
    nbf = $NotBefore

    # JWT-onderwerp
    sub = $AppId
}

# Converteer header en payload naar base64
$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)

# Voeg header en Payload samen met "." om een geldige (niet-ondertekende) JWT te maken
$JWT = $EncodedHeader + "." + $EncodedPayload

# Haal het privésleutelobject van je certificaat op
$PrivateKey = $Certificate.PrivateKey

# Definieer RSA-handtekening en hashingsalgoritme
$RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
$HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256

# Maak een handtekening van de JWT
$Signature = [Convert]::ToBase64String(
    $PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)
) -replace '\+','-' -replace '/','_' -replace '='

# Voeg de handtekening toe aan de JWT met "."
$JWT = $JWT + "." + $Signature

# Maak een hash met lichaamsparameters
$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"

# Gebruik de zelf gegenereerde JWT als Autorisatie
$Header = @{
    Authorization = "Bearer $JWT"
}

# Verspreid de parameters voor Invoke-Restmethod voor schonere code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    Body = $Body
    Uri = $Url
    Headers = $Header
}

$Request = Invoke-RestMethod @PostSplat

Begrijpen van het Uitvoer van Aanvraagtoegangstoken

Zodra je een toegangstoken hebt verkregen via de toepassings-ID/-geheim of via een certificaat, zou je een object met vier eigenschappen moeten zien.

  • token_type – Wat voor soort token het is
  • expires_in – Tijd in seconden dat het toegangstoken geldig is
  • ext_expires_in – Zoals expires_in maar voor veerkracht in geval van een uitval van de tokenservice
  • access_token – Waar we voor kwamen

Vervolgens zul je een kop maken met behulp van token_type en access_token en beginnen met het doen van verzoeken met PowerShell naar de Microsoft Graph API.

Verzoeken doen aan de Microsoft Powershell Graph API

Begin nu met het doen van enkele verzoeken aan de API.

Met ons voorbeeld meegaand, heb je eerst de URL nodig voor het vermelden van beveiligingsmeldingen. Onthoud om de Microsoft Graph API-documentatie te raadplegen om te zien wat er nodig is.

In dit geval heb je een kop nodig met Authorization=Bearer <access_token> en een GET-verzoek naar de Graph API Alerts-eindpunt. Hier is hoe je dat doet met PowerShell.

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

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

# Haal alle beveiligingsmeldingen op
$SecurityAlertsRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"

$SecurityAlerts = $SecurityAlertsRequest.Value

Als je nu enige beveiligingsmeldingen hebt in de $SecurityAlerts variabele, zou het er ongeveer zo uit moeten zien:

$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

Het inspecteren van een enkel beveiligingswaarschuwing als JSON ziet er zo uit:

"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":  [

                            ]
}

Understanding and Managing API Output Paging

De Microsoft Graph API heeft een limiet per functie voor het aantal items dat het zal retourneren. Deze limiet geldt per functie, maar laten we zeggen dat het 1000 items is. Dat betekent dat je slechts een maximum van 1000 items kunt verkrijgen in je verzoek.

Wanneer deze limiet is bereikt, gebruikt het paginering om de rest van de items af te leveren. Dit doet het door de @odata.nextLink-eigenschap toe te voegen aan het antwoord van je verzoek. @odata.nextLink bevat een URL die je kunt oproepen om de volgende pagina van je verzoek te verkrijgen.

Je kunt alle items doorlezen door te controleren op deze eigenschap en een lus te gebruiken:

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

# Alle beveiligingswaarschuwingen ophalen
$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"
}

Conclusie

Nadat je hebt geleerd hoe je je bij de Graph API moet authenticeren, is het vrij eenvoudig om er gegevens van te verzamelen. Het is een krachtige service die veel minder wordt gebruikt dan zou moeten.

Gelukkig zijn er al veel modules beschikbaar om de Microsoft Graph API te gebruiken, maar om aan je eigen behoeften te voldoen, moet je mogelijk je eigen module ervoor maken. Met de vaardigheden die je in dit artikel hebt geleerd, ben je goed op weg.

Voor meer informatie over het beheren van gasttoegang in Office 365, heb ik een diepgaand artikel op mijn blog geschreven dat ik je aanmoedig om te bekijken.

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