Introducción a Microsoft Graph PowerShell — la gestión de Microsoft 365 tras la baja de AzureAD y MSOnline

· Actualizado el: · · PowerShell, Microsoft 365, Microsoft Entra ID, Microsoft Graph, Sistemas de información, Automatización, Mejora operativa, Seguridad

Para los equipos que automatizan la gestión de Microsoft 365, el cambio más importante entre 2024 y 2025 fue la baja de los módulos AzureAD y MSOnline. No son pocos los responsables de sistemas de información que habían escrito procesos rutinarios como «el proceso de baja de empleados», «el inventario de licencias» o «la creación de cuentas para nuevas incorporaciones» con Get-MsolUser o Get-AzureADUser, y todos ellos dejarán de funcionar progresivamente.

La migración lleva a Microsoft Graph PowerShell SDK. Sin embargo, no se trata de un simple cambio de nombres de comando. Cambian las premisas de diseño: la forma de pensar la autenticación (ámbitos y consentimiento), la manera de obtener datos (filtros OData y paginación) e incluso cómo construir la ejecución desatendida (registro de aplicación y certificado). Si se sustituye mecánicamente sin entender esto, se acaba arrastrando otros problemas, como «funciona, pero con permisos excesivos» o «en un inquilino con muchos usuarios solo se obtiene una parte».

Este artículo está dirigido a los responsables de sistemas de información que automatizan con PowerShell la gestión interna de Microsoft 365, y reúne de forma práctica el repaso del proceso de baja, el diseño de la conexión y los permisos, la configuración de la ejecución desatendida, y las tres recetas habituales: inventario, recuento de licencias y proceso de baja de empleados.

1. Conclusión general

  • MSOnline y AzureAD se marcaron como obsoletos el 30 de marzo de 2024; MSOnline dejó de estar disponible el 30 de mayo de 2025, y AzureAD se dio de baja tras terminar su soporte el 30 de marzo de 2025.1
  • La migración lleva a Microsoft Graph PowerShell SDK, o a Microsoft Entra PowerShell construido sobre él (disponibilidad general en marzo de 2025).Este último está orientado a escenarios y también ofrece una opción de compatibilidad que facilita la migración desde AzureAD.2
  • Microsoft.Graph es un metamódulo.Instalarlo completo resulta pesado, así que en la práctica basta con instalar Microsoft.Graph.Authentication más los submódulos de las cargas de trabajo que va a usar.3
  • La conexión empieza con Connect-MgGraph -Scopes.Use siempre el permiso mínimo necesario. Puede averiguar qué permiso hace falta con Find-MgGraphPermission, y a qué módulo pertenece un comando con Find-MgGraphCommand.45
  • La ejecución desatendida se basa en un registro de aplicación y autenticación de aplicación exclusiva con certificado.Se conecta sin interacción con -ClientId, -TenantId y -CertificateThumbprint. Se recomienda el certificado antes que un secreto de cliente.46
  • Los permisos necesarios son distintos entre delegado (Delegated) y de aplicación exclusiva (Application).Aunque la operación sea la misma, el ámbito exigido cambia, así que al pasar a ejecución desatendida hay que volver a asignar los permisos.6
  • Para listar todo use -All, para filtrar -Filter, y para los campos -Property.Filtrar en el cliente con Where-Object provoca obtenciones innecesarias y ajuste (throttling).7
  • El acceso masivo queda sujeto a ajuste (throttling).Ante una respuesta 429, la recomendación oficial es esperar según el valor de Retry-After.8
  • Separe el registro de aplicaciones según el uso.Una única aplicación «con todo incluido» acumula permisos excesivos y amplía el alcance de un incidente.

2. El proceso de baja y qué elegir ahora

Antes de nada, repasemos los hechos. El contenido de este artículo corresponde a julio de 2026.Todas las fechas siguientes forman parte del calendario de baja publicado por Microsoft y ya han pasado.1

Módulo Estado
MSOnline (Get-MsolUser, etc.) Obsoleto desde el 30 de marzo de 2024. Dado de baja el 30 de mayo de 2025
AzureAD (Get-AzureADUser, etc.) Obsoleto desde el 30 de marzo de 2024. Fin del soporte el 30 de marzo de 2025 y baja posterior
Microsoft Graph PowerShell SDK Vigente. Convierte la API de Graph directamente en cmdlets
Microsoft Entra PowerShell Disponibilidad general en marzo de 2025. Módulo orientado a escenarios construido sobre el SDK de Graph2

El criterio para elegir es el siguiente. Si quiere trabajar directamente con la estructura de la API de Graph o tocar cargas de trabajo amplias (Exchange, Teams, Intune, etc.), use Graph PowerShell SDK.Si el centro de su trabajo es la gestión de identidades en Entra ID (antes Azure AD) y quiere que la migración desde el módulo AzureAD sea lo más sencilla posible, use Microsoft Entra PowerShell. Este último interopera con Graph PowerShell SDK y también ofrece una opción de compatibilidad hacia atrás que facilita la migración desde el módulo AzureAD.2

En este artículo nos centramos en Graph PowerShell SDK, por su versatilidad y la abundancia de documentación disponible.

3. Instalación — no instale el metamódulo completo

Microsoft.Graph es un metamódulo que agrupa numerosos submódulos. Instalarlo completo resulta pesado tanto para instalar como para cargar, y en algunos entornos la sola carga puede tardar decenas de segundos.3

# 【Pesado】instala todas las cargas de trabajo
Install-Module Microsoft.Graph -Scope CurrentUser

# 【Práctico】solo autenticación + las cargas de trabajo que va a usar
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser   # obligatorio
Install-Module Microsoft.Graph.Users          -Scope CurrentUser   # usuarios
Install-Module Microsoft.Graph.Groups         -Scope CurrentUser   # grupos
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser  # licencias, etc.
Install-Module Microsoft.Graph.Users.Actions  -Scope CurrentUser   # operaciones sobre usuarios
                                                                   # (Revoke-MgUserSignInSession, etc.)

# Averiguar en qué módulo está cada comando y qué permiso necesita
Find-MgGraphCommand -Command Get-MgUser | Select-Object Module, Permissions -First 1
Find-MgGraphPermission user.read -PermissionType Delegated

Se recomienda usarlo con PowerShell 7. También funciona con Windows PowerShell 5.1, pero este es un terreno donde hay buenas razones —tanto de rendimiento como de continuidad futura— para elegir la versión 7 («Diferencias entre Windows PowerShell 5.1 y PowerShell 7»).3

4. Conexión y permisos — abandone el «ReadWrite.All por si acaso»

4.1 Antes que nada, un vocabulario común

Antes de continuar, definamos brevemente los términos que aparecerán repetidamente.

Término Significado
Delegado (Delegated) Modalidad en la que la ejecución corre a cargo del usuario que inició sesión, actuando como él mismo. Lo que realmente se puede hacer depende a la vez del «ámbito consentido a la aplicación» y del «rol que tiene ese usuario»6
Aplicación exclusiva (Application) Modalidad en la que la ejecución corre a cargo de la propia aplicación, sin pasar por un usuario. Es la que usan los procesos por lotes desatendidos. Se autentica con un certificado o un secreto de cliente6
Registro de aplicación / entidad de servicio El registro de aplicación define la aplicación. La entidad de servicio es su materialización en el inquilino, y es a ella a la que se vinculan los permisos y roles
Ámbito (permiso de acceso) Nombre de permiso como User.Read.All. Los ámbitos delegados y los de aplicación están en categorías separadas: aunque el nombre coincida, hay que volver a asignarlo6
OData Open Data Protocol. Es la base de la sintaxis de consultas de Graph: -Filter, -Property, etc. se traducen a las opciones de consulta OData correspondientes y se procesan en el servidor7
Ajuste (throttling) Mecanismo con el que el servicio rechaza deliberadamente las solicitudes cuando son demasiadas. Se devuelve como HTTP 429 con la cabecera Retry-After8
Evaluación de acceso continuo (CAE) Mecanismo por el que el recurso recibe eventos como una revocación y corta el acceso sin esperar a que caduque el token. Solo funciona en aplicaciones y recursos compatibles9

La diferencia entre estas dos modalidades es, en este artículo, el punto que más incidentes provoca. Representada como diagrama, queda así.

Aplicación exclusiva (Application) — ejecución desatendidaDelegado (Delegated) — inicio de sesión interactivoAl pasar a desatendido,hay que reasignar los permisosPermiso de aplicaciónrequiere consentimiento del administradorAutenticación con certificadoLo que se puede hacer =exactamente el permiso concedidoÁmbito delegado consentidoEl administrador inicia sesiónRol de administrador que tiene esa personaLo que se puede hacer =intersección de ámbito y rol

Basta con recordar una regla. Delegado = se ejecuta como el propio usuario; aplicación exclusiva = proceso por lotes desatendido.Con delegado, «lo que la persona no puede hacer, la aplicación tampoco puede»; con aplicación exclusiva, «la aplicación siempre puede hacer exactamente lo que se le concedió».

4.2 Conexión interactiva

La conexión interactiva se hace con Connect-MgGraph -Scopes. Aparece una pantalla de consentimiento para los ámbitos indicados, y el resultado del consentimiento queda registrado en el inquilino.4

# Uso de solo lectura para inventario. No se solicitan permisos de escritura
Connect-MgGraph -Scopes 'User.Read.All', 'Organization.Read.All' -NoWelcome

Get-MgContext | Format-List Account, TenantId, Scopes, AuthType   # comprobar la conexión actual
Disconnect-MgGraph

Aquí es donde más se nota el cambio al migrar. En la época de Get-MsolUser, «iniciar sesión con una cuenta de administrador permitía hacerlo todo», pero en Graph cada operación tiene definido el ámbito que necesita, y solo funciona dentro de lo consentido. Esto no es una limitación, sino un mecanismo de seguridad. Un incidente en el que un script de inventario escribe por error no puede ocurrir si solo se ha consentido .Read.All.

Los principios son tres.

  • No solicite ámbitos de escritura para un uso de solo lectura (si User.Read.All basta, no pida User.ReadWrite.All)
  • Separe el registro de aplicaciones según el uso (una para inventario, otra para creación de cuentas, otra para gestión de licencias)
  • Que el consentimiento lo dé el administrador de forma consciente (el consentimiento concedido una vez permanece en el inquilino)

Cuando no sepa qué ámbito necesita, busque candidatos con Find-MgGraphPermission y confirme el permiso que exige un comando con Find-MgGraphCommand.5

5. Ejecución desatendida — registro de aplicación y certificado

Si va a ejecutarlo cada noche desde el Programador de tareas, el inicio de sesión interactivo no sirve. Debe cambiar a autenticación de aplicación exclusiva mediante registro de aplicación (entidad de servicio) y certificado.6

El procedimiento tiene cuatro pasos. Como es donde más se atasca la gente en la práctica, se detalla la ubicación exacta en la pantalla y los comandos.6

5.1 Registrar la aplicación

En el Centro de administración de Microsoft Entra (https://entra.microsoft.com), siga esta ruta.

Identidad > Aplicaciones > Registros de aplicaciones > Nuevo registro

Escriba un nombre (por ejemplo, M365-Inventory-Batch), elija «Cuentas en este directorio organizativo únicamente (solo esta organización - Monoinquilino)» como tipos de cuenta admitidos y pulse «Registrar». El URI de redirección no es necesario si solo va a usarlo para ejecución desatendida desde un script.

Anote los dos valores siguientes que aparecen en la página «Información general» tras el registro. Son los que usará para conectarse.

Nombre mostrado en «Información general» Parámetro de Connect-MgGraph
Id. de aplicación (cliente) -ClientId
Id. de directorio (inquilino) -TenantId

Tenga en cuenta que los nombres de navegación del Centro de administración de Microsoft Entra pueden cambiar. Aunque el texto del menú izquierdo sea distinto, el nombre de la página a la que debe llegar es «Registros de aplicaciones». Use ese nombre como referencia.

5.2 Añadir permisos de aplicación y conceder el consentimiento del administrador

En la misma pantalla de la aplicación, siga esta ruta.

Administrar > Permisos de API > Agregar un permiso > Microsoft Graph > Permisos de aplicación

El punto clave aquí es elegir «Permisos de aplicación». Si elige la opción vecina, «Permisos delegados», no funcionará en la ejecución desatendida. Marque los permisos necesarios (para inventario, por ejemplo, User.Read.All) y confirme con «Agregar permisos».

En este punto todavía no se puede usar. En la parte superior de la misma pantalla, pulse el botón

«Conceder consentimiento de administrador para (nombre del inquilino)»

para confirmarlo. Tras pulsarlo, la operación está completa cuando la columna «Estado» de la lista cambia a «Concedido para (nombre del inquilino)». Olvidar este paso es la causa clásica de «añadí el permiso pero sigue fallando con un 403».

5.3 Crear, cargar y ubicar el certificado

Un certificado autofirmado es suficiente. Se crea con New-SelfSignedCertificate de PowerShell.

# 【1】crear el certificado (validez de 2 años; ajústelo a su operativa)
$cert = New-SelfSignedCertificate `
    -Subject           'CN=M365-Inventory-Batch' `
    -CertStoreLocation 'Cert:\CurrentUser\My' `
    -KeySpec           Signature `
    -KeyExportPolicy   Exportable `
    -KeyAlgorithm      RSA `
    -KeyLength         2048 `
    -HashAlgorithm     SHA256 `
    -NotAfter          (Get-Date).AddYears(2)

# 【2】anotar la huella digital (thumbprint) que usará para conectarse
$cert.Thumbprint

# 【3】exportar solo la clave pública a .cer para cargarla (no incluye la clave privada)
Export-Certificate -Cert $cert -FilePath 'C:\temp\M365-Inventory-Batch.cer'

Cargue el archivo .cer exportado desde la pantalla de la aplicación en el Centro de administración de Microsoft Entra, siguiendo la ruta

Administrar > Certificados y secretos > pestaña Certificados > Cargar certificado

Solo debe cargar el .cer (clave pública).Nunca cargue un .pfx, que contiene la clave privada.

Dónde colocar la clave privada depende de la cuenta con la que se ejecute la tarea en el Programador de tareas.

Cuenta de ejecución Almacén de certificados Forma de pasarlo en la conexión Nota
Usuario específico o cuenta de servicio Cert:\CurrentUser\My (creado o importado con esa cuenta) -CertificateThumbprint No es visible desde ninguna cuenta distinta de la que lo creó
SYSTEM, o usado desde varias cuentas Cert:\LocalMachine\My -Certificate (cargado manualmente y pasado así) Crearlo requiere privilegios de administrador. Debe concederse a la cuenta de ejecución permiso de lectura sobre la clave privada

Lo que suele pasarse por alto es que -CertificateThumbprint y -CertificateSubjectName solo buscan en el almacén de certificados del usuario actual.4 Si el certificado está en Cert:\LocalMachine\My, pasar la huella digital no lo encontrará. Si va a usar el almacén del equipo local, cárguelo usted mismo y páselo con -Certificate.

# Si está en LocalMachine, hágalo así
$cert = Get-ChildItem -Path 'Cert:\LocalMachine\My\A1B2C3D4E5F6...'
Connect-MgGraph -ClientId $clientId -TenantId $tenantId -Certificate $cert -NoWelcome

El problema de «en mi equipo funcionaba, pero solo desde el Programador de tareas no encuentra el certificado» casi siempre se debe a este desajuste entre dónde está guardado y cómo se pasa.

5.4 Conectarse desde el script

# Conexión para ejecución desatendida (sin interacción)
$connect = @{
    ClientId              = '11111111-2222-3333-4444-555555555555'
    TenantId              = '66666666-7777-8888-9999-000000000000'
    CertificateThumbprint = 'A1B2C3D4E5F6...'    # certificado en el almacén CurrentUser de la cuenta de ejecución
    NoWelcome             = $true
}
Connect-MgGraph @connect

# Comprobar el tipo de conexión: si AuthType es AppOnly, se conectó como aplicación exclusiva
Get-MgContext | Format-List AppName, ClientId, TenantId, AuthType, Scopes

try {
    # procesamiento de negocio
}
finally {
    Disconnect-MgGraph
}

La primera vez, antes de registrar realmente la tarea, ejecute el script anterior con la cuenta de ejecución del Programador de tareas para confirmar que funciona. Las trampas propias del Programador de tareas (cuenta de ejecución, la opción «Ejecutar tanto si el usuario inició sesión como si no», el directorio de trabajo, etc.) están recopiladas en «Las tareas del Programador de tareas no se ejecutan o terminan con 0x1».

5.5 Puntos de atención

Hay dos puntos de atención.

(1) Los permisos necesarios son distintos entre delegado y aplicación exclusiva.Al desatender un script que funcionaba de forma interactiva con -Scopes 'User.Read.All', hay que volver a asignar en el registro de aplicación el permiso equivalente como permiso de aplicación, y requiere consentimiento del administrador.6

(2) Los certificados tienen fecha de caducidad.Que todos los procesos por lotes nocturnos fallen justo el día en que caduca el certificado es un incidente frecuente en la práctica. Anote la fecha de caducidad en un calendario y documente el procedimiento de renovación. Sobre el almacenamiento de credenciales, consulte también «Manejo seguro de credenciales en PowerShell». También es posible conectarse con un secreto de cliente, pero se recomienda el certificado por el riesgo de manejarlo en texto plano y por lo engorroso de gestionar su caducidad.

6. Cómo obtener datos — -All / -Filter / -Property

Graph es una API que da por sentada la paginación. Por defecto solo devuelve una página, así que si necesita todos los elementos, añada -All.7

# 【Mal】ignora la paginación y filtra en el cliente (lento, trae de más, causa throttling)
Get-MgUser | Where-Object { $_.Department -eq 'Ventas' }

# 【Bien】filtra en el servidor, solo los campos necesarios, todas las páginas
Get-MgUser -All -Filter "department eq 'Ventas'" `
           -Property Id, DisplayName, UserPrincipalName, AccountEnabled, Department |
    Select-Object DisplayName, UserPrincipalName, AccountEnabled

Los puntos clave son tres.

  • -Filter es una expresión OData que filtra en el servidor. Where-Object filtra en local, así que termina obteniendo todos los elementos para luego descartarlos
  • Limitar las columnas con -Property aligera la respuesta. Algunas propiedades que no se devuelven por defecto sí se devuelven si se solicitan explícitamente
  • Los campos obtenidos con -Property también hay que escribirlos en Select-Object.Obtención y visualización son cosas distintas: si solo se hace una de las dos, el campo aparece vacío

Para consultas avanzadas como startsWith o endsWith, o cuando solo necesita el número de elementos, combine -ConsistencyLevel eventual con -CountVariable.7

# Contar solo el número de usuarios habilitados (obtiene el recuento sin traer todos los elementos)
Get-MgUser -Filter 'accountEnabled eq true' -ConsistencyLevel eventual -CountVariable total -Top 1 | Out-Null
"Usuarios habilitados: $total"

Como -Top 1 | Out-Null es una expresión poco habitual, conviene aclararla. Lo que se asigna a -CountVariable es el número total de elementos que cumplen la condición, y no depende del valor de -Top. Lo que determina -Top es solo cuántos objetos vienen en una única respuesta. Es decir, -Top 1 no significa «contar solo un elemento»: es una indicación para minimizar los datos reales que llegan, dado que de todos modos hay que enviar la solicitud al menos una vez para obtener el recuento. Si se omite, se devuelve la página de objetos de usuario por defecto, que luego habría que descartar con Out-Null. Cabe señalar que -ConsistencyLevel eventual es obligatorio para usar -CountVariable o consultas avanzadas como startsWith.7

El acceso masivo activa el ajuste (throttling) y devuelve HTTP 429. La recomendación oficial es «esperar el número de segundos indicado en la cabecera Retry-After y volver a intentarlo».8 Los cmdlets del SDK reintentan internamente hasta cierto punto, pero cuando va a recorrer un bucle de miles de elementos, es más fiable reducir directamente el número de obtenciones (filtrar con -Filter y -Property, obtener toda la información necesaria de una sola vez). Para el diseño de reintentos en sí, consulte «Manejo de errores y diseño de reintentos en PowerShell».

7. Tres recetas habituales

(1) Exportar el inventario de usuarios a CSV

# SignInActivity (fecha del último inicio de sesión) no se puede obtener solo con
# User.Read.All; además se necesita AuditLog.Read.All. Si no la va a usar, quítela de -Property
Connect-MgGraph -Scopes 'User.Read.All', 'AuditLog.Read.All' -NoWelcome

$users = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, AccountEnabled,
                              Department, JobTitle, CreatedDateTime, SignInActivity |
    Select-Object DisplayName, UserPrincipalName, Department, JobTitle, AccountEnabled,
                  @{ n = 'Fecha de creación'; e = { $_.CreatedDateTime } },
                  # Para detectar cuentas inactivas, use el "inicio de sesión exitoso".
                  # LastSignInDateTime es un "intento" de inicio interactivo: incluye fallos y no cubre lo no interactivo
                  @{ n = 'Último inicio de sesión exitoso'; e = { $_.SignInActivity.LastSuccessfulSignInDateTime } },
                  @{ n = 'Último intento de inicio de sesión interactivo'; e = { $_.SignInActivity.LastSignInDateTime } },
                  @{ n = 'Último inicio de sesión no interactivo'; e = { $_.SignInActivity.LastNonInteractiveSignInDateTime } }

# Un CSV con caracteres no ASCII se abre bien en Excel si se guarda en UTF-8 (con BOM).
# El nombre de la codificación cambia según la versión: utf8BOM desde 7, UTF8 (con BOM) en 5.1
$enc = if ($PSVersionTable.PSVersion.Major -ge 6) { 'utf8BOM' } else { 'UTF8' }
$users | Export-Csv -Path "D:\Inventario\users_$(Get-Date -f yyyyMMdd).csv" -Encoding $enc -NoTypeInformation

La estructura de columnas del CSV resultante sigue el orden y los nombres escritos en Select-Object, que se convierten directamente en la fila de encabezado. Como Export-Csv entrecomilla todos los campos por defecto, la primera línea tiene esta forma.

"DisplayName","UserPrincipalName","Department","JobTitle","AccountEnabled","Fecha de creación","Último inicio de sesión exitoso","Último intento de inicio de sesión interactivo","Último inicio de sesión no interactivo"

A partir de la segunda línea aparecen los valores de cada usuario, en el mismo orden. Si las columnas esperadas aparecen vacías, es porque falta algo en -Property o en Select-Object, o porque faltan permisos.En particular, si las tres columnas a partir de «Último inicio de sesión exitoso» están todas vacías, sospeche de la falta de AuditLog.Read.All, como se explica a continuación.

SignInActivity es útil para detectar cuentas inactivas. Sin embargo, qué campo se mira es lo importante. LastSignInDateTime registra intentos de inicio de sesión interactivo (incluidos los fallidos) y no incluye los inicios de sesión no interactivos de aplicaciones o servicios. Si se juzga solo con este campo, un intento fallido de un atacante puede parecer «uso real», y una cuenta de servicio que realmente está activa puede parecer «inactiva». Para detectar cuentas inactivas, use LastSuccessfulSignInDateTime, que refleja los inicios de sesión interactivos y no interactivos que tuvieron éxito.10 Ahora bien, esta propiedad en concreto no se puede obtener solo con User.Read.All: requiere además AuditLog.Read.All (y también hay requisitos de licencia a nivel de inquilino). Si faltan permisos, se produce un error o el valor queda vacío, así que si no la va a usar, quítela de -Property. Puede confirmar el permiso necesario con Find-MgGraphPermission.10 El manejo de la codificación de caracteres en CSV está recopilado en «Automatizar procesos de Excel y CSV con PowerShell».

(2) Contabilizar el consumo de licencias

Connect-MgGraph -Scopes 'Organization.Read.All' -NoWelcome

Get-MgSubscribedSku | Select-Object `
    SkuPartNumber,
    @{ n = 'Compradas';   e = { $_.PrepaidUnits.Enabled } },
    @{ n = 'Asignadas'; e = { $_.ConsumedUnits } },
    @{ n = 'Disponibles';     e = { $_.PrepaidUnits.Enabled - $_.ConsumedUnits } } |
    Sort-Object Disponibles

Ejecutar esto mensualmente basta para evitar situaciones como «se compraron licencias adicionales pese a tener sobrantes» o «la licencia de un empleado que se fue no se liberó».

(3) Proceso de baja de empleados (bloqueo de inicio de sesión y revocación de sesión)

El siguiente código es un ejemplo de ejecución en modalidad delegada (inicio de sesión interactivo).La señal de ello es que se indica -Scopes; al ejecutarlo, se le pedirá iniciar sesión en el navegador. Para convertirlo en ejecución desatendida, hay que reestructurarlo como aplicación exclusiva, tal como se explicó en el capítulo 5. Antes, repasemos la diferencia entre ambas.

  Delegado (código de abajo) Aplicación exclusiva (ejecución desatendida)
¿Quién actúa? La propia persona que inició sesión La propia aplicación (entidad de servicio)
Indicaciones en Connect-MgGraph -ClientId + -Scopes -ClientId + -TenantId + -CertificateThumbprint
Certificado No hace falta Obligatorio (o un secreto de cliente)
Tipo de permiso Permiso delegado Permiso de aplicación (requiere consentimiento del administrador)6
Requisito adicional Rol de administrador de Entra de la propia persona que inició sesión (se explica más adelante)11 Si el objetivo es un administrador, asignar un rol superior a la propia aplicación11
Situación adecuada Atención puntual e individual, comprobación previa con -WhatIf Procesos por lotes nocturnos, integración con el sistema de RR. HH.
# ── Ejemplo de ejecución delegada (inicio de sesión interactivo) ──
# Al ejecutarlo aparece la pantalla de inicio de sesión. No hace falta certificado
#
# Como implica escritura, ejecútelo con un registro de aplicación y un ámbito dedicados.
# Si se omite -ClientId, se conecta con la aplicación compartida por defecto del SDK,
# y los permisos consentidos se acumulan en esa aplicación (compartida por toda la
# organización). Precisamente en operaciones destructivas como el proceso de baja,
# aísle los permisos en un registro de aplicación dedicado
#
# Cada operación exige un permiso distinto, así que se solicitan ambos a la vez
#   cambiar accountEnabled → User.EnableDisableAccount.All (permiso mínimo)
#   revocar la sesión de inicio de sesión → User.RevokeSessions.All (permiso mínimo)
# Ambas también se pueden hacer con User.ReadWrite.All, pero el permiso es más amplio
$connect = @{
    ClientId  = '99999999-aaaa-bbbb-cccc-dddddddddddd'   # registro de aplicación dedicado al proceso de baja
    TenantId  = '66666666-7777-8888-9999-000000000000'
    Scopes    = 'User.Read.All', 'User.EnableDisableAccount.All', 'User.RevokeSessions.All'
    NoWelcome = $true
}
Connect-MgGraph @connect

# Revoke-MgUserSignInSession necesita Microsoft.Graph.Users.Actions
Import-Module Microsoft.Graph.Users.Actions

$upn = 'taro.yamada@example.co.jp'
$user = Get-MgUser -UserId $upn -Property Id, DisplayName, AccountEnabled

# 1. Bloquear el inicio de sesión (elimine la cuenta solo tras un período de gracia)
Update-MgUser -UserId $user.Id -AccountEnabled:$false

# 2. Revocar el token de actualización y la cookie de sesión del navegador
#    Aviso: un token de acceso ya emitido puede seguir siendo válido hasta que caduque (se explica más adelante)
Revoke-MgUserSignInSession -UserId $user.Id

Write-Host "Se detuvo el inicio de sesión de $($user.DisplayName)"

Hay tres puntos que conviene tener claros aquí. Uno es el registro de aplicación al que se conecta. Si se omite -ClientId, la conexión se hace con la aplicación por defecto del SDK de Microsoft Graph PowerShell, y los permisos consentidos quedan registrados en esa aplicación compartida por toda la organización. Para cumplir el principio del capítulo 4 de «separar el registro de aplicaciones según el uso», es necesario indicar explícitamente el registro de aplicación propio de la empresa con -ClientId.4

Otro es el ámbito. En Graph, cada operación tiene definido el permiso que necesita: no es que «poder escribir sobre usuarios» habilite cualquier cosa.Como cambiar accountEnabled y revocar la sesión de inicio de sesión tienen cada uno su propio permiso mínimo, si se consiente solo uno de los dos, la conexión se establecerá con éxito pero un comando intermedio fallará por falta de permisos. Confirme el permiso necesario para cada operación con Find-MgGraphPermission y con la tabla de permisos de la referencia de la API de Graph correspondiente.119

Y el tercer punto es que, en la ejecución delegada, el ámbito por sí solo no basta. Cuando se ejecuta iniciando sesión como la propia persona usuaria, como en el ejemplo anterior, cambiar accountEnabled también requiere un rol de administrador de Microsoft Entra. Esto se debe a que el permiso delegado solo determina «qué puede hacer la aplicación en nombre de ese usuario», y no eleva el propio nivel de permisos del usuario que inició sesión. Aunque se haya consentido el permiso, si falta el rol, la ejecución fallará con un 403. La documentación establece que el rol mínimo capaz de actualizar esta propiedad para todos los administradores del inquilino es Privileged Authentication Administrator, y que, en general, «se necesita un rol de administrador de nivel superior al del objetivo».11 Como el rol necesario cambia según si el objetivo del proceso de baja es un usuario normal o un administrador, decida de antemano qué se asigna a la cuenta encargada de la operación. Incluso en la ejecución de aplicación exclusiva con certificado, si el objetivo es un administrador, hay que asignar un rol superior a la propia aplicación.11

Si va a convertir este proceso en un proceso por lotes nocturno, lo único que cambia es la parte de la conexión.

# ── Al reestructurarlo como aplicación exclusiva (ejecución desatendida) ──
# Preparación previa: en «Permisos de API > Permisos de aplicación» del registro de
#   aplicación, agregue User.Read.All / User.EnableDisableAccount.All /
#   User.RevokeSessions.All y conceda el consentimiento del administrador (5.2).
#   Use el certificado creado en 5.3
$connect = @{
    ClientId              = '99999999-aaaa-bbbb-cccc-dddddddddddd'
    TenantId              = '66666666-7777-8888-9999-000000000000'
    CertificateThumbprint = 'A1B2C3D4E5F6...'   # no se indica -Scopes
    NoWelcome             = $true
}
Connect-MgGraph @connect

# De aquí en adelante (Import-Module hasta Revoke-MgUserSignInSession), es exactamente igual que en la versión delegada

También conviene entender con precisión el alcance de Revoke-MgUserSignInSession. Lo que invalida este comando es el token de actualización y la cookie de sesión del navegador, y un token de acceso ya emitido puede seguir siendo utilizable hasta que caduque.9 Si necesita un corte inmediato, es un requisito previo que la aplicación y el recurso sean compatibles con la evaluación de acceso continuo (CAE). No dé por hecho que «al revocar, todo el acceso se detiene al instante»: en los casos importantes, combínelo con la deshabilitación de la cuenta y tenga en cuenta el margen de tiempo hasta que surta efecto.

La práctica habitual es evitar la eliminación inmediata de la cuenta y detener primero el inicio de sesión. Si se elimina antes de terminar de traspasar el buzón de correo o OneDrive, la recuperación resulta costosa. Este tipo de «operación irreversible» es más seguro envolverlo en una función propia compatible con -WhatIf, de forma que primero muestre la lista de objetivos para confirmarla y solo después se ejecute («Diseño de parámetros y modularización en PowerShell»).

8. Reglas prácticas (tabla de decisión)

Aspecto Opciones Criterio de decisión
Módulo Graph SDK / Entra PowerShell Entra si el centro es la gestión de identidades y migra desde AzureAD; Graph SDK si abarca cargas de trabajo amplias2
Instalación Todo incluido / solo submódulos Por submódulos, para reducir el tiempo de arranque y las dependencias3
Autenticación (interactiva) Confiar en la cuenta de administrador / ámbito mínimo Para inventario, solo la familia .Read.. El consentimiento queda en el inquilino4
Autenticación (desatendida) Secreto de cliente / certificado Se recomienda el certificado. Decida antes la gestión de caducidad y el procedimiento de renovación6
Granularidad de permisos Una aplicación con todo incluido / registro de aplicación según el uso Permite limitar el alcance de un incidente
Obtención de listas Where-Object / -Filter + -All + -Property Filtre en el servidor. El volumen obtenido determina directamente la velocidad y la estabilidad7
Frente al 429 Reintento inmediato / seguir Retry-After Es la recomendación oficial. Antes que nada, reduzca el número de obtenciones8
Operaciones peligrosas Ejecución directa / -WhatIf + confirmación previa de la lista de objetivos En el proceso de baja o las eliminaciones masivas, siempre debe poder verse la lista de objetivos

9. Resumen

  • MSOnline y AzureAD ya están dados de baja. La migración lleva a Microsoft Graph PowerShell SDK, o a Microsoft Entra PowerShell construido sobre él.
  • Como Microsoft.Graph es un metamódulo, en la práctica basta con instalar la autenticación más los submódulos de las cargas de trabajo que se van a usar.
  • El principio de la conexión es minimizar el ámbito. No solicite permisos de escritura para el inventario y separe el registro de aplicaciones según el uso.
  • La ejecución desatendida se basa en un registro de aplicación y un certificado. Los incidentes suelen originarse en que los permisos necesarios difieren entre delegado y aplicación exclusiva, y en que los certificados caducan.
  • La obtención de datos se resume en tres elementos: -All (paginación), -Filter (filtrado en el servidor) y -Property (limitación de campos). Ante un 429, siga Retry-After.
  • Con solo ejecutar mensualmente el inventario, el recuento de licencias y el proceso de baja, se reducen considerablemente los dos grandes riesgos habituales: el desperdicio de licencias y las cuentas abandonadas.

Descarga del código de ejemplo

El código tratado en este artículo se distribuye ya preparado para ejecutarse directamente. Incluye conexión, extracción de cuentas inactivas, recuento de licencias y proceso de baja.

Descargar el código de ejemplo (zip)

Los ejemplos de este artículo dependen de Windows y del inquilino, por lo que no se han verificado en ejecución. Se ha aplicado análisis sintáctico y análisis estático con PSScriptAnalyzer a todos los archivos, pero compruebe siempre el funcionamiento en su propio equipo de verificación.

# Análisis sintáctico + análisis estático (se puede ejecutar también fuera de Windows)
./Invoke-SampleTests.ps1

Los valores de configuración (rutas, nombres de servidor, id. de inquilino, etc.) son solo ejemplos. No los ejecute tal cual en producción: adáptelos al entorno de su propia organización.

Artículos relacionados

Áreas de consultoría relacionadas

KomuraSoft LLC se ocupa de la migración a Graph de scripts de gestión de Microsoft 365, la revisión del registro de aplicaciones y el diseño de permisos, y la automatización de procesos rutinarios como el inventario y el proceso de baja de empleados.

Referencias

  1. Microsoft Community Hub (Microsoft Entra Blog), Action required: MSOnline and AzureAD PowerShell retirement - 2025 info and resources. Sobre que ambos módulos de PowerShell, MSOnline y AzureAD, se marcaron como obsoletos el 30 de marzo de 2024; que la baja de MSOnline se llevó a cabo en la primavera de 2025 y dejó de estar disponible el 30 de mayo de 2025; que AzureAD terminó su soporte el 30 de marzo de 2025 y se dio de baja después; y que la migración lleva a Microsoft Graph PowerShell SDK y a Microsoft Entra PowerShell.  2

  2. Microsoft Learn, What is Microsoft Entra PowerShell?. Sobre que Microsoft Entra PowerShell es un módulo orientado a escenarios construido sobre Microsoft Graph PowerShell SDK, que interopera con los cmdlets de Graph PowerShell SDK, y que ofrece una opción de compatibilidad hacia atrás que facilita la migración desde el módulo AzureAD. El anuncio de disponibilidad general (GA) es Microsoft Entra PowerShell module now generally available (marzo de 2025).  2 3 4

  3. Microsoft Learn, Install the Microsoft Graph PowerShell SDK. Sobre que Microsoft.Graph es un metamódulo que incluye un conjunto de submódulos, que se pueden instalar individualmente solo los submódulos necesarios, que Microsoft.Graph.Authentication es imprescindible para la autenticación, y las versiones de PowerShell compatibles.  2 3 4

  4. Microsoft Learn, Connect-MgGraph. Sobre la solicitud de permisos delegados mediante -Scopes, la autenticación de aplicación exclusiva mediante -ClientId / -TenantId / -CertificateThumbprint, que -CertificateThumbprint y -CertificateSubjectName obtienen el certificado del almacén de certificados del usuario actual (y que, para usar el almacén del equipo local, hay que cargarlo uno mismo y pasarlo con -Certificate), la comprobación de la información de conexión actual con Get-MgContext, y la desconexión con Disconnect-MgGraph.  2 3 4 5 6

  5. Microsoft Learn, Find Microsoft Graph PowerShell commands and permissions. Sobre la búsqueda con Find-MgGraphCommand del módulo al que pertenece un comando, el permiso necesario y la API correspondiente, y la búsqueda de nombres de permiso con Find-MgGraphPermission.  2

  6. Microsoft Learn, Use app-only authentication with the Microsoft Graph PowerShell SDK. Sobre el procedimiento de registro de aplicación, permiso de aplicación y consentimiento del administrador, la configuración de la autenticación desatendida con certificado, y la diferencia entre permisos delegados y permisos de aplicación.  2 3 4 5 6 7 8 9 10

  7. Microsoft Learn, Paging Microsoft Graph data in your app. Sobre que las respuestas de Microsoft Graph están paginadas, que con -All en el SDK de PowerShell se pueden obtener todas las páginas, el filtrado en el servidor mediante -Filter / -Property (equivalentes a $filter y $select), y -ConsistencyLevel eventual junto con la obtención de recuentos en consultas avanzadas.  2 3 4 5 6

  8. Microsoft Learn, Microsoft Graph throttling guidance. Sobre que el ajuste (throttling) se aplica por recurso y devuelve HTTP 429, que conviene esperar el número de segundos indicado en la cabecera Retry-After de la respuesta antes de reintentar, y que se recomienda un diseño que reduzca directamente el número de solicitudes.  2 3 4

  9. Microsoft Learn, user: revokeSignInSessions (Microsoft Graph API). Sobre que User.RevokeSessions.All se indica como permiso mínimo para revocar la sesión de inicio de sesión, que se requiere consentimiento del administrador, que lo que se invalida es el token de actualización y la cookie de sesión del navegador, que un token de acceso ya emitido puede usarse hasta que caduque (y que la evaluación de acceso continuo interviene en la aplicación inmediata).  2 3

  10. Microsoft Learn, signInActivity resource type. Sobre que obtener la propiedad signInActivity de un usuario requiere tanto AuditLog.Read.All como User.Read.All, que existen requisitos de licencia a nivel de inquilino, y que lastSignInDateTime representa los intentos de inicio de sesión interactivo (incluidos éxitos y fallos), mientras que lastSuccessfulSignInDateTime representa los inicios de sesión interactivos y no interactivos exitosos, y lastNonInteractiveSignInDateTime los inicios de sesión no interactivos.  2

  11. Microsoft Learn, Update user (Microsoft Graph API). Sobre que los permisos necesarios para actualizar un usuario están definidos por propiedad, que para cambiar accountEnabled se indica User.EnableDisableAccount.All como permiso mínimo, y que también se puede ejecutar con el más amplio User.ReadWrite.All. Sobre que, en el escenario delegado, además del ámbito adecuado se necesita un rol de administrador de Microsoft Entra; que el rol mínimo capaz de actualizar accountEnabled para todos los administradores del inquilino es Privileged Authentication Administrator; que, en general, se necesita un rol de administrador de nivel superior al del objetivo; y que, incluso en el escenario de aplicación exclusiva, si el objetivo es un administrador, hay que asignar a la aplicación un rol de administrador de nivel superior.  2 3 4 5

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Los módulos AzureAD y MSOnline ya no se pueden usar?
Así es, ambos están dados de baja. Los módulos MSOnline y AzureAD se marcaron como obsoletos (deprecated) el 30 de marzo de 2024; MSOnline dejó de estar disponible el 30 de mayo de 2025, y AzureAD terminó su soporte el 30 de marzo de 2025, tras lo cual también se dio de baja. Aunque un script parezca seguir funcionando, puede dejar de hacerlo en cualquier momento. La migración lleva a Microsoft Graph PowerShell SDK, o al módulo Microsoft Entra PowerShell construido sobre él (disponibilidad general en marzo de 2025).
Instalar el módulo Microsoft.Graph es pesado. ¿Se puede aligerar?
Sí. Microsoft.Graph es un metamódulo que agrupa numerosos submódulos, así que instalarlo completo tarda tanto en instalarse como en cargarse. En la práctica, lo razonable es instalar solo los submódulos de las cargas de trabajo que va a usar: Microsoft.Graph.Users para gestión de usuarios, Microsoft.Graph.Groups para grupos, y el imprescindible Microsoft.Graph.Authentication para la autenticación, por ejemplo. Puede averiguar a qué módulo pertenece cada comando con Find-MgGraphCommand.
Quiero ejecutar el script de forma desatendida desde el Programador de tareas, pero aparece un inicio de sesión interactivo.
Cambie a autenticación de aplicación exclusiva (app-only) mediante un registro de aplicación (entidad de servicio) y un certificado. Registre la aplicación en Microsoft Entra ID, conceda el consentimiento del administrador a los permisos de aplicación (Application permissions) necesarios y, a continuación, pase -ClientId, -TenantId y -CertificateThumbprint a Connect-MgGraph para conectarse sin interacción. El certificado es más seguro que un secreto de cliente y facilita la gestión de la caducidad. Coloque el certificado en el almacén de certificados de la cuenta de ejecución y establezca siempre un procedimiento para renovarlo antes de que caduque.
¿Qué debo indicar en -Scopes de Connect-MgGraph?
Indique solo el permiso mínimo que exige el comando que va a ejecutar. Puede confirmar qué necesita con Find-MgGraphPermission o en la documentación del comando; si solo va a leer, basta con un permiso de la familia .Read., como User.Read.All. Si el objetivo es un inventario o una auditoría, no solicite permisos de escritura. Como los permisos consentidos quedan registrados en el inquilino, dar el consentimiento a Directory.ReadWrite.All «por si acaso» se convierte en un riesgo futuro. Lo más seguro es separar el registro de aplicaciones y los permisos según el uso.
Al usar Get-MgUser en un inquilino con muchos usuarios, solo obtengo una parte de los resultados.
Es porque las respuestas de Microsoft Graph están paginadas. Con -All se recorren automáticamente todas las páginas. Además, en obtenciones masivas puede aplicarse un ajuste (throttling) que devuelva respuestas 429, así que la base es limitar los campos con -Property y delegar el filtrado al servidor mediante OData (-Filter) en lugar de hacerlo en el cliente con Where-Object. Si solo necesita el número de elementos, puede obtener únicamente el recuento combinando -ConsistencyLevel eventual con -CountVariable.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog