Maîtriser l’API Graph de PowerShell : Des aperçus faciles à suivre

L’API Microsoft Graph est un service qui vous permet de lire, modifier et gérer presque tous les aspects d’Azure AD et d’Office 365 sous un seul point de terminaison API REST. Dans cet article, apprenez comment transformer votre API en PowerShell Graph API.

Prérequis

Si vous souhaitez suivre avec moi dans cet article, assurez-vous d’abord de répondre aux critères suivants :

  • Exécution de Windows PowerShell 5.1 (C’est la version avec laquelle j’ai testé. D’autres versions peuvent fonctionner mais ne sont pas garanties)
  • Un locataire Azure
  • Authentifié à Azure avec un compte disposant d’autorisations d’administrateur global ou d’autorisations d’inscription d’application sur l’abonnement et un administrateur global pour accepter vos demandes d’inscription d’application.

Création d’une identité d’application pour Microsoft Graph API

Pour accéder à l’API Microsoft Graph, vous avez d’abord besoin d’une identité pour obtenir un jeton OAuth. Cela se fait principalement avec une identité d’application que vous pouvez créer dans le portail Azure. Vous pouvez créer une identité d’application via le portail Azure. Pour ce faire :

  • Rendez-vous sur le portail Azure et accédez à Azure Active Directory.
  • Cliquez sur Inscriptions d’applications sous Gérer dans le menu de gauche et cliquez sur le bouton Nouvelle inscription.
Authenticating before creating the PowerShell Graph API
  • Entrez un nom pour votre application et cliquez sur Enregistrer.
  • Copiez l’identifiant de l’application (GUID) pour une utilisation ultérieure.
Registering an application

Création de secrets pour l’API Microsoft Graph.

Vous pouvez vous authentifier auprès de l’API Graphique avec deux méthodes principales : AppId/Secret et l’authentification basée sur un certificat. Vous devrez vous authentifier lors de la connexion à l’API graphique avec PowerShell.

Explorons comment s’authentifier avec les deux méthodes.

AppId/Secret

Un ID d’application/secret est comme un nom d’utilisateur/mot de passe classique. L’ID de l’application est constitué d’un GUID au lieu d’un nom d’utilisateur et le mot de passe est simplement une chaîne aléatoire.

Pour créer un secret – cliquez sur Certificats et secrets dans le menu de gauche et appuyez sur Nouveau secret client.

Creating a client secret

Entrez une description pour le secret et sélectionnez quand vous voulez qu’il expire. Maintenant, il suffit de demander la permission pour accéder aux données que vous souhaitez.

Certificat

Il est possible de créer un certificat auto-signé et d’envoyer sa clé publique à Azure. C’est la méthode préférée et la plus sécurisée d’authentification.

Vous devrez d’abord générer un certificat auto-signé. Heureusement, cela se fait facilement avec PowerShell.

# Le nom de votre locataire (peut également être quelque chose de plus descriptif)
$TenantName        = "contoso.onmicrosoft.com"

# Où exporter le certificat sans la clé privée
$CerOutputPath     = "C:\Temp\PowerShellGraphCert.cer"

# Dans quel magasin de certificats voulez-vous le mettre
$StoreLocation     = "Cert:\CurrentUser\My"

# Date d'expiration du nouveau certificat
$ExpirationDate    = (Get-Date).AddYears(2)


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

# Créer un certificat
$Certificate = New-SelfSignedCertificate @CreateCertificateSplat

# Obtenir le chemin du certificat
$CertificatePath = Join-Path -Path $StoreLocation -ChildPath $Certificate.Thumbprint

# Exporter le certificat sans la clé privée
Export-Certificate -Cert $CertificatePath -FilePath $CerOutputPath | Out-Null

Maintenant, téléchargez le certificat auto-signé que vous avez exporté vers $CerOutputPath vers votre application Azure en cliquant sur Certificats & secrets dans le menu de gauche et en appuyant sur Télécharger le certificat.

Uploading a certificate

Ajout de permissions à l’application

Accorder les autorisations appropriées à l’application est important, non seulement pour la fonctionnalité de votre application, mais aussi pour la sécurité. La documentation sur cela et (presque) tout le reste dans l’API Microsoft Graph peut être trouvée dans la documentation.

Une fois que j’aurai configuré cela, je vais rassembler tous les événements de sécurité de mon locataire. Pour être autorisé à le faire, j’ai besoin de l’autorisation minimale SecurityEvents.Read.All. Avec cela, je peux rassembler et agir sur les événements de déplacements impossibles, les utilisateurs se connectant via VPN/TOR, etc.

Pour ajouter SecurityEvents.Read.All à votre application, cliquez sur Autorisations API puis sur Ajouter une autorisation. Cela vous présentera non seulement l’API Microsoft Graph, mais aussi de nombreuses autres applications dans Azure. La plupart de ces applications sont faciles à connecter une fois que vous savez comment vous connecter à l’API Microsoft Graph.

Cliquez sur Microsoft Graph > Autorisations d’application > Événements de sécurité et cochez SecurityEvents.Read.All. Ensuite, appuyez sur le bouton Ajouter une autorisation.

Avez-vous remarqué que la colonne Consentement administrateur requis était définie sur Oui pour cette autorisation ? Cela signifie qu’un administrateur du locataire doit approuver avant que l’autorisation ne soit ajoutée à l’application.

Tenant admin permission approval

Si vous êtes un administrateur global, appuyez sur le bouton Accorder le consentement administrateur pour ou demandez à un administrateur global de l’approuver. Demander la permission à l’utilisateur au lieu d’un administrateur définissant simplement la permission de lecture/écriture est une grande partie de l’authentification OAuth. Mais cela nous permet de contourner cela pour la plupart des autorisations dans Microsoft Graph.

Vous avez probablement déjà vu cela sur Facebook ou Google : « Autorisez-vous l’application X à accéder à votre profil ? »

Granting admin consent

Maintenant, vous êtes prêt – connectez-vous et obtenez des données !

Obtenir un jeton d’accès (ID d’application et secret)

Pour cela, nous devrons envoyer une requête pour obtenir un jeton d’accès à partir d’un point de terminaison OAuth de Microsoft Graph. Et dans le corps de cette requête, nous devons fournir :

  • client_id – Votre ID d’application – encodé en URL
  • client_secret – Votre secret d’application – encodé en URL
  • scope – Une URL encodée spécifiant ce à quoi vous souhaitez accéder
  • grant_type – La méthode d’authentification que vous utilisez

L’URL du point de terminaison est https://login.microsoftonline.com/<tenantname>/oauth2/v2.0/token. Vous pouvez demander un jeton d’accès avec PowerShell et l’API Graph en utilisant le code ci-dessous.

# Définir l'ID de l'application, le secret et l'étendue, le nom de votre locataire et l'URL du point de terminaison
$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"

# Ajouter System.Web pour l'encodage d'URL
Add-Type -AssemblyName System.Web

# Créer le corps
$Body = @{
    client_id = $AppId
	client_secret = $AppSecret
	scope = $Scope
	grant_type = 'client_credentials'
}

# Diviser les paramètres pour Invoke-Restmethod pour un code plus propre
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    # Créer une chaîne en joignant la liste du corps avec '&'
    Body = $Body
    Uri = $Url
}

# Demander le jeton !
$Request = Invoke-RestMethod @PostSplat

Obtenir un jeton d’accès (Utilisation d’un certificat)

L’authentification à l’API Microsoft Graph avec un certificat est légèrement différente de la procédure normale avec l’ID d’application/secret. Pour obtenir un jeton d’accès à l’aide d’un certificat, vous devez :

  1. Créez un en-tête de jeton Web Java (JWT).
  2. Créez une charge utile JWT.
  3. Signez l’en-tête JWT ET la charge utile avec le certificat auto-signé précédemment créé. Cela créera un jeton d’accès auto-fabriqué utilisé pour demander un jeton d’accès Microsoft Graph.
  4. Créez un corps de requête contenant :
  • client_id=<ID de l'application>
  • client_assertion=<le JWT>
  • client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  • scope=<portée encodée en URL>
  • grant_type=client_credentials
  1. Effectuez une requête POST avec le corps vers le point de terminaison OAuth avec Authorization=<JWT> dans son en-tête.

Comment faire cela n’était pas évident dans la documentation de Microsoft, mais voici le script PowerShell pour y parvenir :

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

# Créer un hash base64 du certificat
$CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash())

# Créer un horodatage JWT pour l'expiration
$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)

# Créer un horodatage de début de validité JWT
$NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds
$NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0)

# Créer l'en-tête JWT
$JWTHeader = @{
    alg = "RS256"
    typ = "JWT"
    # Utiliser CertificateBase64Hash et remplacer/retirer pour correspondre au codage web base64
    x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
}

# Créer la charge utile JWT
$JWTPayLoad = @{
    # Quel point de terminaison est autorisé à utiliser ce JWT
    aud = "https://login.microsoftonline.com/$TenantName/oauth2/token"

    # Horodatage d'expiration
    exp = $JWTExpiration

    # Émetteur = votre application
    iss = $AppId

    # ID JWT: GUID aléatoire
    jti = [guid]::NewGuid()

    # Ne pas utiliser avant
    nbf = $NotBefore

    # Sujet JWT
    sub = $AppId
}

# Convertir l'en-tête et la charge utile en 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)

# Joindre l'en-tête et la charge utile avec "." pour créer un JWT valide (non signé)
$JWT = $EncodedHeader + "." + $EncodedPayload

# Obtenir l'objet de clé privée de votre certificat
$PrivateKey = $Certificate.PrivateKey

# Définir l'algorithme de signature RSA et de hachage
$RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
$HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256

# Créer une signature du JWT
$Signature = [Convert]::ToBase64String(
    $PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)
) -replace '\+','-' -replace '/','_' -replace '='

# Joindre la signature au JWT avec "."
$JWT = $JWT + "." + $Signature

# Créer un hash avec les paramètres du corps
$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"

# Utiliser le JWT auto-généré comme Autorisation
$Header = @{
    Authorization = "Bearer $JWT"
}

# Éclater les paramètres pour Invoke-Restmethod pour un code plus propre
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    Body = $Body
    Uri = $Url
    Headers = $Header
}

$Request = Invoke-RestMethod @PostSplat

Compréhension de la sortie de jeton d’accès

Une fois que vous avez obtenu un jeton d’accès, que ce soit via l’ID/application secrète ou via un certificat, vous devriez voir un objet avec quatre propriétés.

  • token_type – Le type de jeton qu’il s’agit
  • expires_in – Temps en secondes pendant lequel le jeton d’accès est valide
  • ext_expires_in – Comme expires_in mais pour la résilience en cas de panne de service de jetons
  • access_token – Ce que nous sommes venus chercher

Ensuite, vous allez créer un en-tête en utilisant token_type et access_token et commencer à effectuer des requêtes avec PowerShell vers l’API Microsoft Graph.

Effectuer des requêtes vers l’API Graph Microsoft PowerShell

Commencez maintenant à effectuer des requêtes vers l’API.

En suivant notre exemple, vous aurez d’abord besoin de l’URL pour répertorier les alertes de sécurité. N’oubliez pas d’utiliser la documentation de l’API Microsoft Graph pour voir ce qui est nécessaire.

Dans ce cas, vous avez besoin d’un en-tête avec Authorization=Bearer <access_token> et une requête GET vers le point de terminaison des alertes de l’API Graph. Voici comment faire cela avec PowerShell.

# Créer un en-tête
$Header = @{
    Authorization = "$($Request.token_type) $($Request.access_token)"
}

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

# Récupérer toutes les alertes de sécurité
$SecurityAlertsRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"

$SecurityAlerts = $SecurityAlertsRequest.Value

Maintenant, si vous avez des alertes de sécurité dans la variable $SecurityAlerts, cela devrait ressembler à ceci :

$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

Inspecter une alerte de sécurité unique au format JSON ressemblera à ceci :

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

                            ]
}

Comprendre et gérer la pagination des résultats d’API

Le Microsoft Graph API a une limite par fonction sur le nombre d’éléments qu’il renverra. Cette limite est par fonction, mais disons qu’elle est de 1000 éléments. Cela signifie que vous ne pouvez obtenir qu’un maximum de 1000 éléments dans votre demande.

Lorsque cette limite est atteinte, la pagination est utilisée pour livrer le reste des éléments. Cela se fait en ajoutant la propriété @odata.nextLink à la réponse de votre demande. @odata.nextLink contient une URL que vous pouvez appeler pour obtenir la page suivante de votre demande.

Vous pouvez parcourir tous les éléments en vérifiant cette propriété et en utilisant une boucle :

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

# Récupérer toutes les alertes de sécurité
$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"
}

Conclusion

Une fois que vous avez appris à vous authentifier auprès de l’API Graph, il est assez facile de collecter des données. C’est un service puissant qui est utilisé beaucoup moins qu’il ne le devrait.

Heureusement, il existe déjà de nombreux modules pour utiliser le Microsoft Graph API, mais pour répondre à vos propres besoins, vous devrez peut-être créer votre propre module. En utilisant les compétences que vous avez apprises dans cet article, vous devriez être bien parti.

Pour plus d’informations sur le contrôle de l’accès des invités dans Office 365, j’ai écrit un article approfondi sur mon blog que je vous encourage à consulter.

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