Pide tu presupuesto ya!

Dominar la API Graph de PowerShell: información fácil de seguir

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.

Requisitos previos

Si desea seguirme en este artículo, asegúrese primero de cumplir con los siguientes criterios:

  • Ejecutando Windows PowerShell 5.1 (Esta es la versión con la que probé. Otras versiones puede funcionan pero no están garantizados)
  • Un inquilino de Azure
  • Autenticado en Azure con una cuenta con permisos de administrador global o permisos de registro de aplicaciones en la suscripción y un administrador global para aceptar sus solicitudes de registro de aplicaciones.

Creación de una identidad de aplicación para la API de Microsoft Graph

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:

  • Dirígete al Portal Azul y vaya a Azure Active Directory.
  • Haga clic en Registros de aplicaciones bajo Administrar en el menú de la izquierda y haga clic en el Nuevo registro botón.
Autenticación antes de crear la API Graph de PowerShell
  • Ingrese un nombre para su aplicación y haga clic Registro.
  • Copie la guía de identificación de la aplicación para usarla más adelante.
Crear un registro de aplicación en Azure Portal
Registrar una aplicación

Creación de secretos para la API de Microsoft Graph

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.

ID de aplicación/secreto

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.

Crear un secreto de cliente de aplicación en Azure
Crear un 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.

Certificado

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.

Carga de un certificado a la aplicación Azure
Cargando un certificado

Agregar permisos a la aplicación

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.

Permiso de aplicación que necesita consentimiento del administrador
Aprobación del permiso del administrador de inquilinos

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?”

Conceder permiso a la aplicación
Conceder el consentimiento del administrador

Ahora ya está todo listo: ¡iniciemos sesión y obtengamos algunos datos!

Adquirir un token de acceso (ID de aplicación y secreto)

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 codificada
  • client_secret – Su secreto de aplicación – URL codificada
  • scope – Una URL codificada en URL que especifica a qué desea acceder
  • grant_type – ¿Qué método de autenticación estás utilizando?

La URL del punto final es https://login.microsoftonline.com//oauth2/v2.0/token. Puede solicitar un token de acceso con PowerShell y Graph API utilizando el fragmento de código a continuación.

# 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

Adquirir un token de acceso (mediante un certificado)

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:

  1. Cree un encabezado de token web Java (JWT).
  2. Cree una carga útil JWT.
  3. Firme el encabezado JWT Y la carga útil con el certificado autofirmado creado previamente. Esto creará un token de acceso de creación propia que se utilizará para solicitar un token de acceso de Microsoft Graph.
  4. Cree un cuerpo de solicitud que contenga:
  • client_id=<application id>
  • client_assertion=<the JWT>
  • client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  • scope=<URLEncoded scope>
  • grant_type=client_credentials
  1. Realice una solicitud de publicación con cuerpo en el punto final de oauth con Authorization=<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

Comprender la salida del token de acceso de solicitud

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álido
  • ext_expires_in – Como expires_in pero para mayor resiliencia en caso de una interrupción del servicio simbólico
  • access_token – Para qué vinimos

A continuación creará un encabezado usando token_type y access_token y comience a realizar solicitudes con PowerShell a la API de Microsoft Graph.

Realizar solicitudes a la API Graph de Microsoft Powershell

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

                            ]
}

Comprensión y gestión de la paginación de salida de API

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

Conclusión

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.

Written by

Leave a comment