Pide tu presupuesto ya!
6th December 2023
Microsoft Graph API es un servicio que le permite leer, modificar y administrar casi todos los aspectos de Azure AD y Office 365 en un único punto final de API REST. En este artículo, aprenderá cómo transformar su API a PowerShell Graph API.
Si desea seguirme en este artículo, asegúrese primero de cumplir con los siguientes criterios:
Para acceder a la API de Microsoft Graph, primero necesita una identidad para obtener un token de OAuth. Esto se hace principalmente con una identidad de aplicación que puede crear en Azure Portal. Puede crear una identidad de aplicación a través de Azure Portal. Para hacerlo:

Puede autenticarse en Graph API con dos métodos principales: AppId/Secret y autenticación basada en certificados. Deberá autenticarse cuando se conecte a la API de gráficos con PowerShell.
Veamos cómo autenticarse con ambos métodos.
Un ID/secreto de aplicación es como un nombre de usuario/contraseña normal. La identificación de la aplicación consta de un GUID en lugar de un nombre de usuario y la contraseña es solo una cadena aleatoria.
Para crear un secreto, haga clic en Certificados y secretos en el menú de la izquierda y presione en Nuevo secreto de cliente.

Ingrese una descripción para el secreto y seleccione cuándo desea que caduque. Ahora sólo es cuestión de pedir permiso para poder acceder a los datos que desees.
Existe la posibilidad de crear un certificado autofirmado y cargar su clave pública en Azure. Esta es la forma preferida y más segura de autenticación.
Primero deberá generar un certificado autofirmado. Afortunadamente, esto se puede hacer fácilmente con PowerShell.
# Your tenant name (can something more descriptive as well)
$TenantName = "contoso.onmicrosoft.com"
# Where to export the certificate without the private key
$CerOutputPath = "C:\Temp\PowerShellGraphCert.cer"
# What cert store you want it to be in
$StoreLocation = "Cert:\CurrentUser\My"
# Expiration date of the new certificate
$ExpirationDate = (Get-Date).AddYears(2)
# Splat for readability
$CreateCertificateSplat = @{
FriendlyName = "AzureApp"
DnsName = $TenantName
CertStoreLocation = $StoreLocation
NotAfter = $ExpirationDate
KeyExportPolicy = "Exportable"
KeySpec = "Signature"
Provider = "Microsoft Enhanced RSA and AES Cryptographic Provider"
HashAlgorithm = "SHA256"
}
# Create certificate
$Certificate = New-SelfSignedCertificate @CreateCertificateSplat
# Get certificate path
$CertificatePath = Join-Path -Path $StoreLocation -ChildPath $Certificate.Thumbprint
# Export certificate without private key
Export-Certificate -Cert $CertificatePath -FilePath $CerOutputPath | Out-Null
Ahora, cargue el certificado autofirmado que exportó a $CerOutputPath a su aplicación de Azure haciendo clic en Certificados y secretos en el menú de la izquierda y pulsando en Cargar certificado.

Es importante otorgarle a la aplicación los permisos adecuados, no solo por la funcionalidad de la aplicación sino también por la seguridad. El conocimiento de esto y (casi) todo lo demás en la API de Microsoft Graph se puede encontrar en el documentación.
Una vez que tenga esto configurado, voy a reunir todos los eventos de seguridad de mi inquilino. Para que me permitan hacer eso necesito Eventos de seguridad.Leer.Todos como permiso mínimo. Con esto puedo recopilar y tomar medidas sobre eventos de Impossible Travel, usuarios que se conectan a través de VPN/TOR y demás.
Para agregar el Eventos de seguridad.Leer.Todos a su aplicación – haga clic en Permisos API y luego Agregar permiso. Esto le presentará no solo Graph API sino también muchas otras aplicaciones en Azure. Es fácil conectarse a la mayoría de estas aplicaciones una vez que sepa cómo conectarse a la API de Microsoft Graph.
Haga clic en Gráfico de Microsoft > Permisos de aplicación > Eventos de seguridad y comprobar el Eventos de seguridad.Leer.Todos. Después de eso presione el Agregar permiso botón.
¿Viste que el Se requiere consentimiento del administrador La columna se estableció en Sí con ese permiso? Esto significa que un administrador de inquilinos debe aprobar antes de agregar el permiso a la aplicación.

Si eres administrador global, presiona el Otorgar consentimiento de administrador para o pídale a un administrador global que lo apruebe. Solicitar permiso al usuario en lugar de que un administrador simplemente configure el permiso de lectura/escritura es una gran parte de la autenticación OAuth. Pero esto nos permite omitirlo para la mayoría de los permisos en Microsoft Graph.
Probablemente ya hayas visto esto en Facebook o Google: “¿Permites que la aplicación X acceda a tu perfil?”

Ahora ya está todo listo: ¡iniciemos sesión y obtengamos algunos datos!
Para ello, necesitaremos publicar una solicitud para obtener un token de acceso desde un punto final de Microsoft Graph OAuth. Y en el cuerpo de esa solicitud debemos proporcionar:
client_id – Su ID de aplicación – URL codificadaclient_secret – Su secreto de aplicación – URL codificadascope – Una URL codificada en URL que especifica a qué desea accedergrant_type – ¿Qué método de autenticación estás utilizando?La URL del punto final es https://login.microsoftonline.com/
# Define AppId, secret and scope, your tenant name and endpoint 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"
# Add System.Web for urlencode
Add-Type -AssemblyName System.Web
# Create body
$Body = @{
client_id = $AppId
client_secret = $AppSecret
scope = $Scope
grant_type="client_credentials"
}
# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = @{
ContentType="application/x-www-form-urlencoded"
Method = 'POST'
# Create string by joining bodylist with '&'
Body = $Body
Uri = $Url
}
# Request the token!
$Request = Invoke-RestMethod @PostSplat
La autenticación en la API de Microsoft Graph con un certificado es un poco diferente del flujo normal de AppId/Secret. Para obtener un token de acceso mediante un certificado tienes que:
client_id=<application id>client_assertion=<the JWT>client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearerscope=<URLEncoded scope>grant_type=client_credentialsAuthorization=<JWT> en su encabezado.Cómo hacer esto no era obvio en documentación de Microsoft pero aquí está el script de PowerShell para que esto suceda:
$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"
# Create base64 hash of certificate
$CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash())
# Create JWT timestamp for 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)
# Create JWT validity start timestamp
$NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds
$NotBefore = [math]::Round($NotBeforeExpirationTimeSpan,0)
# Create JWT header
$JWTHeader = @{
alg = "RS256"
typ = "JWT"
# Use the CertificateBase64Hash and replace/strip to match web encoding of base64
x5t = $CertificateBase64Hash -replace '\+','-' -replace '/','_' -replace '='
}
# Create JWT payload
$JWTPayLoad = @{
# What endpoint is allowed to use this JWT
aud = "https://login.microsoftonline.com/$TenantName/oauth2/token"
# Expiration timestamp
exp = $JWTExpiration
# Issuer = your application
iss = $AppId
# JWT ID: random guid
jti = https://diseño-web-barato.com/microsoft-graph-api-powershell/::NewGuid()
# Not to be used before
nbf = $NotBefore
# JWT Subject
sub = $AppId
}
# Convert header and payload to 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)
# Join header and Payload with "." to create a valid (unsigned) JWT
$JWT = $EncodedHeader + "." + $EncodedPayload
# Get the private key object of your certificate
$PrivateKey = $Certificate.PrivateKey
# Define RSA signature and hashing algorithm
$RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
$HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256
# Create a signature of the JWT
$Signature = [Convert]::ToBase64String(
$PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT),$HashAlgorithm,$RSAPadding)
) -replace '\+','-' -replace '/','_' -replace '='
# Join the signature to the JWT with "."
$JWT = $JWT + "." + $Signature
# Create a hash with body parameters
$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"
# Use the self-generated JWT as Authorization
$Header = @{
Authorization = "Bearer $JWT"
}
# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = @{
ContentType="application/x-www-form-urlencoded"
Method = 'POST'
Body = $Body
Uri = $Url
Headers = $Header
}
$Request = Invoke-RestMethod @PostSplat
Una vez que haya obtenido un token de acceso, ya sea a través del ID/secreto de la aplicación o mediante un certificado, debería ver un objeto con cuatro propiedades.
token_type – ¿Qué tipo de ficha es?expires_in – Tiempo en segundos que el token de acceso es válidoext_expires_in – Como expires_in pero para mayor resiliencia en caso de una interrupción del servicio simbólicoaccess_token – Para qué vinimosA continuación creará un encabezado usando token_type y access_token y comience a realizar solicitudes con PowerShell a la API de Microsoft Graph.
Ahora comience a realizar algunas solicitudes a la API.
Siguiendo con nuestro ejemplo, primero necesitará la URL para enumerar las alertas de seguridad. Recuerde utilizar el Documentación de la API de Microsoft Graph para ver lo que se necesita.
En este caso, necesita un encabezado con Authorization=Bearer <access_token> y una solicitud GET hacia el punto final de alertas de Graph API. Aquí se explica cómo hacerlo con PowerShell.
# Create header
$Header = @{
Authorization = "$($Request.token_type) $($Request.access_token)"
}
$Uri = "https://graph.microsoft.com/v1.0/security/alerts"
# Fetch all security alerts
$SecurityAlertsRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"
$SecurityAlerts = $SecurityAlertsRequest.Value
Ahora, si tiene alguna alerta de seguridad en el $SecurityAlerts variable, debería verse así:
$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
La inspección de una sola alerta de seguridad como JSON se verá así:
"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": [
]
}
La API de Microsoft Graph tiene un límite por función sobre la cantidad de elementos que devolverá. Este límite es por función, pero digamos que son 1000 elementos. Eso significa que sólo puedes obtener un máximo de 1000 artículos en tu solicitud.
Cuando se alcance este límite, utilizará paginación para entregar el resto de los artículos. Lo hace añadiendo el @odata.nextLink propiedad para la respuesta de su solicitud. @odata.nextLink contiene una URL a la que puede llamar para obtener la siguiente página de su solicitud.
Puede leer todos los elementos comprobando esta propiedad y utilizando un bucle:
$Uri = "https://graph.microsoft.com/v1.0/auditLogs/signIns"
# Fetch all security alerts
$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"
}
Después de aprender cómo autenticarse en Graph API, será bastante fácil recopilar datos de ella. Es un servicio poderoso que se usa mucho menos de lo que debería.
Afortunadamente, ya existen muchos módulos para utilizar la API de Microsoft Graph, pero para satisfacer sus propias necesidades es posible que deba crear su propio módulo. Utilizando las habilidades que ha aprendido en este artículo, debería estar en el buen camino.
Para obtener más información sobre cómo controlar el acceso de invitados en Office 365, escribí un artículo detallado en mi blog. Te invito a que lo consultes. Échale un vistazo.
Leave a comment