Microsoft 365 PowerShell es la forma de administrar los usuarios, las licencias, los grupos y la configuración de Microsoft 365 con comandos, en lugar de hacerlo con clics. Con una línea se saca el inventario de todas las cuentas, se ve cuántas licencias quedan libres o se bloquea en segundos a una persona que deja la empresa. Esta guía explica qué es, por qué Azure AD PowerShell y MSOnline ya no funcionan, cómo instalar Microsoft Graph PowerShell, cómo conectarse con Connect-MgGraph y qué comandos usa un administrador cada semana. Además, incluye escenarios completos y los errores más comunes con su solución.
Está escrita para dos lectores. Si usted no es de sistemas, las primeras secciones le explican qué hace esta herramienta y por qué su equipo de TI la usa. En cambio, si administra Microsoft 365, encontrará los comandos listos para copiar, con el permiso que pide cada uno y sus trampas.
No copiamos el manual sin más. Comprobamos cada comando y cada parámetro contra el módulo vigente, Microsoft Graph PowerShell 2.41.2, y contra la documentación de Microsoft. También medimos el SDK completo en Windows PowerShell 5.1 y en PowerShell 7, y ahí apareció un problema serio que conviene conocer antes de instalar nada.
Qué es Microsoft 365 PowerShell y para qué sirve
Microsoft 365 reúne varios servicios: Microsoft Entra ID, que guarda las cuentas y los grupos, y Exchange Online, SharePoint Online y Teams. Microsoft 365 PowerShell es el nombre común para los módulos de PowerShell que administran todo eso. Mucha gente lo busca todavía como PowerShell Office 365, por el nombre antiguo del servicio. Además, durante años se usó Azure AD PowerShell para lo mismo, como se explica más abajo.
No es un solo módulo. Las cuentas, las licencias y los grupos se administran con Microsoft Graph PowerShell. En cambio, el correo, los sitios y Teams tienen cada uno su propio módulo.
Para entenderlo sin tecnicismos, piense en un edificio de oficinas. El centro de administración de Microsoft 365 es la recepción: cómoda, pero atiende de a una persona. Microsoft 365 PowerShell es el tablero de control del edificio. Desde él se revisan, se cambian o se cierran cientos de puertas a la vez, y queda escrito qué se hizo.
Sirve, sobre todo, para seis trabajos:
- Hacer inventario: quién tiene cuenta, qué licencia usa y a qué grupos pertenece.
- Dar de alta y de baja a las personas siempre igual, sin olvidar ningún paso.
- Controlar las licencias: cuántas se compraron, cuántas se usan y quién no tiene ninguna.
- Revisar quién tiene roles de administrador.
- Encontrar cuentas que nadie usa desde hace meses.
- Automatizar lo que se repite, como el informe mensual de licencias.
Qué módulo usar para cada servicio de Microsoft 365
Esta tabla resume qué módulo administra cada parte. Las cuentas y las licencias son la base, y las demás guías de esta serie parten de ellas:
| Qué administra | Módulo | Orden para conectarse | Guía |
|---|---|---|---|
| Usuarios, licencias, grupos, roles, dominios y dispositivos | Microsoft.Graph (Microsoft Graph PowerShell) | Connect-MgGraph | Esta guía |
| Buzones, permisos y seguimiento de mensajes | ExchangeOnlineManagement | Connect-ExchangeOnline | Exchange Online PowerShell |
| Sitios, uso compartido y papelera de SharePoint | Microsoft.Online.SharePoint.PowerShell o PnP.PowerShell | Connect-SPOService | SharePoint Online PowerShell |
| Equipos, canales y directivas de Teams | MicrosoftTeams | Connect-MicrosoftTeams | Microsoft Teams PowerShell |
Si es la primera vez que usa PowerShell, empiece por nuestra guía de PowerShell. Allí se explican la consola, los scripts y la directiva de ejecución.
Por qué conviene frente al centro de administración
El centro de administración sigue siendo útil para el caso suelto. Sin embargo, trabaja de a una cuenta y no deja un registro reutilizable de lo que se hizo. Por eso, los administradores combinan las dos herramientas.
| Tarea | Centro de administración | Microsoft 365 PowerShell |
|---|---|---|
| Listar todos los usuarios con su cargo y su licencia | Exportar la vista y completar a mano | Un informe en un archivo CSV |
| Encontrar las cuentas sin licencia | Ordenar y revisar la lista | Un filtro sobre todas las cuentas |
| Quitar un servicio de la licencia a 50 personas | Persona por persona | Un ciclo con el mismo cambio para todas |
| Saber quién no inicia sesión desde hace 90 días | No lo junta en una vista | Un filtro sobre la fecha del último inicio de sesión |
| Repetir la misma tarea el mes siguiente | Volver a hacer cada clic | Ejecutar el mismo script |
En resumen, el panel sirve para lo puntual. Microsoft 365 PowerShell sirve para lo masivo, para lo que se repite y para lo que hay que poder demostrar después. Si todavía no conoce el panel, lea primero nuestra guía del centro de administración de Microsoft 365.
Azure AD PowerShell y MSOnline: los módulos que ya no funcionan
Durante años, los administradores usaron dos módulos: MSOnline, con órdenes como Connect-MsolService, y Azure AD PowerShell, con Connect-AzureAD. Sin embargo, Microsoft los retiró. Por eso, muchos scripts de Azure AD PowerShell que circulan en internet ya no sirven.
Según el historial de anuncios de Microsoft Entra, los dos quedaron obsoletos el 30 de marzo de 2024. Luego, MSOnline dejó de funcionar entre comienzos de abril y finales de mayo de 2025. Por su parte, Azure AD PowerShell perdió el soporte el 30 de marzo de 2025 y su retiro quedó programado para después de julio de 2025. En consecuencia, un script de Azure AD PowerShell no tiene arreglo: hay que traducirlo.
Lo comprobamos de otra forma. Hoy, Find-Module no encuentra MSOnline, AzureAD ni AzureADPreview en la Galería de PowerShell, así que ya no se pueden instalar con Install-Module. Sin embargo, quien los instaló antes los conserva. En el equipo de prueba, por ejemplo, seguía AzureADPreview 2.0.2.183 en Windows PowerShell 5.1. Cargaba sin errores y respondía You must call the Connect-AzureAD cmdlet before calling any other cmdlets., como si nada hubiera cambiado.
Cómo traducir un script viejo a Microsoft Graph
El reemplazo oficial de Azure AD PowerShell es Microsoft Graph PowerShell. Los nombres cambian de forma bastante regular: donde decía Msol o AzureAD, ahora dice Mg. Estas son las equivalencias que más se usan, según la tabla de equivalencias de Microsoft:
| MSOnline | Azure AD PowerShell | Microsoft Graph PowerShell |
|---|---|---|
Connect-MsolService | Connect-AzureAD | Connect-MgGraph |
Get-MsolUser | Get-AzureADUser | Get-MgUser |
New-MsolUser | New-AzureADUser | New-MgUser |
Set-MsolUser | Set-AzureADUser | Update-MgUser |
Remove-MsolUser | Remove-AzureADUser | Remove-MgUser |
Get-MsolAccountSku | Get-AzureADSubscribedSku | Get-MgSubscribedSku |
Set-MsolUserLicense | Set-AzureADUserLicense | Set-MgUserLicense |
Get-MsolGroupMember | Get-AzureADGroupMember | Get-MgGroupMember |
Get-MsolRoleMember | Get-AzureADDirectoryRoleMember | Get-MgDirectoryRoleMember |
Get-MsolDomain | Get-AzureADDomain | Get-MgDomain |
Ojo: cambiar el nombre no basta. Los parámetros y los objetos que devuelve cada comando son distintos. Por ejemplo, para bloquear una cuenta ya no se usa -BlockCredential, sino -AccountEnabled.
La otra opción: Microsoft Entra PowerShell
Microsoft publica además un segundo módulo, Microsoft Entra PowerShell, construido sobre Graph. Sus comandos tienen nombres más legibles, como Get-EntraUser. Según Microsoft, es compatible en más del 98 % con Azure AD PowerShell. Además, su modo de compatibilidad permite correr scripts viejos de Azure AD PowerShell cambiando una o dos líneas.
La versión vigente es la 1.3.0. La probamos sin conectarnos y respondió Not connected to Microsoft Graph. Use 'Connect-Entra -Scopes User.Read.All' to authenticate. Es útil para migrar muchos scripts heredados rápido. Para escribir scripts nuevos, en cambio, esta guía usa Microsoft Graph PowerShell, que es lo que usa la documentación de Microsoft 365.
Requisitos de Microsoft 365 PowerShell
Para usar Microsoft 365 PowerShell hacen falta tres cosas: una cuenta con permisos, una versión de PowerShell compatible y el módulo. Ninguna exige comprar nada adicional.
Permisos: roles de administrador y ámbitos de Graph
Aquí está el cambio más grande frente a los módulos viejos. Con MSOnline, bastaba con ser administrador. Con Microsoft Graph PowerShell, además del rol, hay que pedir al conectarse cada permiso que se va a usar. Microsoft los llama ámbitos, o scopes en inglés.
Por ejemplo, para leer usuarios se pide User.Read.All y para cambiar licencias, LicenseAssignment.ReadWrite.All. La primera vez, Microsoft Entra ID muestra una ventana de consentimiento. Muchos de estos permisos exigen que lo apruebe un administrador.
Funciona como una llave maestra con candados por sala. El rol de administrador dice qué salas puede abrir esa persona. Los ámbitos dicen cuáles abre esta sesión de PowerShell. Si falta uno de los dos, la orden falla con un error 403.
Para saber qué permiso pide cada comando, use Find-MgGraphCommand. Lo mejor es que funciona sin conectarse. Así lo usamos:
Find-MgGraphCommand -Command Get-MgUser -ApiVersion v1.0 | Select-Object -First 1 -ExpandProperty Permissions
Cada permiso que devuelve trae la marca IsLeastPrivilege, que señala el de menos privilegios. Ese es el que conviene pedir. En la tabla de comandos de más abajo anotamos el que marcó la herramienta para cada uno.
Qué versión de PowerShell usar
Microsoft Graph PowerShell funciona en Windows PowerShell 5.1, con .NET Framework 4.7.2 o posterior, y en PowerShell 7. Además, corre en Windows, macOS y Linux. Microsoft recomienda PowerShell 7, y nuestras mediciones le dan la razón.
En Windows PowerShell 5.1, la directiva de ejecución debe estar en RemoteSigned o en una menos estricta. Si no la ha cambiado nunca, ejecute una vez:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Un detalle práctico: cada versión de PowerShell tiene su propia carpeta de módulos. Instalar en una no instala en la otra. En el equipo de prueba, por ejemplo, Windows PowerShell 5.1 tenía Microsoft Graph 2.28.0 y PowerShell 7 tenía la 2.35.1. Mientras tanto, la Galería ya ofrecía la 2.41.2, publicada el 14 de octubre de 2026.
Cómo instalar Microsoft Graph PowerShell
Microsoft Graph PowerShell se publica en la Galería de PowerShell. Según la guía de instalación de Microsoft, hay dos formas de instalarlo: el SDK completo o solo los submódulos que se necesitan.
# El SDK completo (todos los submódulos)
Install-Module Microsoft.Graph -Scope CurrentUser -Repository PSGallery
# O solo lo necesario para usuarios, licencias y grupos
Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Users, Microsoft.Graph.Users.Actions, Microsoft.Graph.Groups, Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser -Repository PSGallery
Para comprobar la versión y actualizarla, use estas órdenes:
Get-InstalledModule Microsoft.Graph*
Update-Module Microsoft.Graph
Existe además el módulo Microsoft.Graph.Beta, que habla con la versión preliminar de la API. Microsoft recomienda no usarlo en producción, porque sus comandos pueden cambiar sin aviso.
Lo que medimos: no cargue el SDK completo
Los números del SDK completo sorprenden. En el equipo de prueba, la versión 2.35.1 ocupaba 981,4 MB, repartidos en 38 submódulos, y sus manifiestos declaraban 9.400 comandos. Luego cargamos todo con Import-Module Microsoft.Graph, sin conectarnos a ninguna organización:
| Prueba | Windows PowerShell 5.1 | PowerShell 7.6.7 |
|---|---|---|
| Cargar el SDK completo | Falla: carga 21 de 38 submódulos | Carga los 38 |
| Tiempo | 75 a 104 segundos, hasta el error | Unos 260 segundos |
| Memoria del proceso al terminar | Más de 1 GB | 3,2 GB |
| Cargar solo Microsoft.Graph.Users, versión 2.41.2 | 4,7 segundos, sin errores | 2,9 segundos, sin errores |
Windows PowerShell 5.1 dio este error: No se puede crear la función Get-MgDomainCount porque se ha superado la capacidad de funciones (4096) para este ámbito. La causa es un límite de esa versión, que admite 4.096 funciones por sesión. El SDK completo trae más del doble. PowerShell 7 no tiene ese límite, pero tardó más de cuatro minutos.
Por lo tanto, la conclusión es simple. En scripts y en el trabajo diario, cargue solo los submódulos que usa. PowerShell los carga solos cuando se ejecuta uno de sus comandos, así que basta con no escribir Import-Module Microsoft.Graph.
Un último detalle del equipo de prueba: los módulos vivían en la carpeta Documentos, dentro de OneDrive. En la copia de Windows PowerShell 5.1, 187 de sus 468 archivos estaban solo en línea, así que la primera carga tenía además que bajarlos. Si su carpeta Documentos está en OneDrive, considere instalar para todos los usuarios, con -Scope AllUsers y permisos de administrador.
Cómo conectarse a Microsoft 365 con PowerShell
Para conectarse a Microsoft 365, se usa Connect-MgGraph con los ámbitos que va a necesitar la sesión. Así, cada sesión de Connect-MgGraph tiene solo los permisos que se pidieron. Se abre la ventana de inicio de sesión de Microsoft, con el doble factor si la cuenta lo tiene:
Connect-MgGraph -Scopes "User.Read.All", "LicenseAssignment.Read.All" -NoWelcome
Get-MgContext
Luego, Get-MgContext muestra con qué cuenta, en qué organización y con qué ámbitos quedó conectada la sesión. Además, -NoWelcome evita el mensaje de bienvenida de Connect-MgGraph, útil en los scripts.
Estas son las variantes más útiles:
| Situación | Orden |
|---|---|
| Equipo sin navegador, o servidor remoto | Connect-MgGraph -Scopes "User.Read.All" -UseDeviceCode |
| Que la sesión no se guarde para otras ventanas | Connect-MgGraph -Scopes "User.Read.All" -ContextScope Process |
| Conectarse a una organización concreta, con su identificador | Connect-MgGraph -TenantId "id-de-la-organizacion" -Scopes "User.Read.All" |
| Cerrar la sesión | Disconnect-MgGraph |
Ojo con un comportamiento que confunde. Por defecto, Microsoft Graph PowerShell guarda la sesión en caché para el usuario de Windows. Así, al abrir otra ventana sigue conectado a la misma organización hasta ejecutar Disconnect-MgGraph. Si administra varias organizaciones, use -ContextScope Process o desconéctese siempre al terminar.
Errores comunes al conectarse y cómo resolverlos
Sin conexión, todos los comandos de datos que probamos respondieron con el mismo mensaje. Esta tabla junta ese y los demás tropiezos frecuentes:
| Mensaje o síntoma | Causa | Solución |
|---|---|---|
Authentication needed. Please call Connect-MgGraph. | No hay una sesión abierta | Ejecute Connect-MgGraph con los ámbitos necesarios |
| Error 403 o falta de privilegios | La sesión no pidió el ámbito que exige el comando | Busque el permiso con Find-MgGraphCommand y reconéctese con él |
| «Se ha superado la capacidad de funciones (4096)» | Se cargó el SDK completo en Windows PowerShell 5.1 | Cargue solo los submódulos o use PowerShell 7 |
No se reconoce Connect-MsolService o Connect-AzureAD | El script es de un módulo retirado | Tradúzcalo a Microsoft Graph con la tabla de equivalencias |
Get-MgUser devuelve solo 100 cuentas | Sin -All, solo llega la primera página | Añada -All |
| Una columna sale vacía, como el cargo o la licencia | Graph no trae esa propiedad si no se pide | Añada -Property con las propiedades que necesita |
| Un filtro con conteo devuelve un error de consulta | Es una consulta avanzada | Añada -ConsistencyLevel eventual y -CountVariable |
Set-MgUserLicense no asigna la licencia | La cuenta no tiene país de uso (UsageLocation) | Fíjelo antes con Update-MgUser -UsageLocation CO |
Disconnect-MgGraph responde No application to sign out from. | No había sesión abierta | Ninguna: es solo un aviso |
Comandos más comunes de Microsoft 365 PowerShell
Estos son los comandos que un administrador usa casi todas las semanas. La columna de permisos muestra el que Find-MgGraphCommand marcó como de menos privilegios en la versión 2.41.2. Es el ámbito que conviene pedir al conectarse.
Sesión y ayuda
| Comando | Para qué sirve |
|---|---|
Connect-MgGraph | Abre la sesión con los ámbitos que se piden |
Get-MgContext | Muestra la cuenta, la organización y los ámbitos de la sesión |
Disconnect-MgGraph | Cierra la sesión y borra el token guardado |
Find-MgGraphCommand | Dice qué permiso y qué dirección de la API usa un comando, sin conectarse |
Find-MgGraphPermission | Busca permisos por nombre y explica qué permite cada uno |
Invoke-MgGraphRequest | Llama a la API directamente, para lo que no tiene comando propio |
Un aviso sobre Find-MgGraphCommand: solo conoce los comandos generados desde la API. Por ejemplo, no reconoce New-MgGroupMember, que sí existe en el módulo, y responde que no es un comando válido. En ese caso, busque su pareja New-MgGroupMemberByRef. Para New-MgGroupMember anotamos el permiso de su opuesto, Remove-MgGroupMemberByRef. Además, en Get-MgGroup y Get-MgOrganization marca como mínimos permisos de otros servicios. Para esos dos, anotamos el permiso de lectura de grupos o de la organización, que también figura en su lista.
Usuarios
| Comando | Para qué sirve | Permiso de menos privilegios |
|---|---|---|
Get-MgUser | Lista o busca usuarios | User.ReadBasic.All |
New-MgUser | Crea una cuenta | User.ReadWrite.All |
Update-MgUser | Cambia datos: cargo, país de uso, bloqueo | User.ReadWrite.All |
Remove-MgUser | Borra una cuenta (se puede restaurar 30 días) | User.DeleteRestore.All |
Revoke-MgUserSignInSession | Cierra todas las sesiones abiertas de una persona | User.RevokeSessions.All |
Get-MgUserMemberOf | Muestra los grupos y roles de una persona | Directory.Read.All |
Get-MgUserManager | Muestra el jefe directo registrado | User.Read.All |
Licencias con PowerShell
| Comando | Para qué sirve | Permiso de menos privilegios |
|---|---|---|
Get-MgSubscribedSku | Licencias compradas y usadas de cada plan | LicenseAssignment.Read.All |
Get-MgUserLicenseDetail | Licencias y servicios de una persona | LicenseAssignment.Read.All |
Set-MgUserLicense | Asigna o quita licencias, y desactiva servicios | LicenseAssignment.ReadWrite.All |
Fíjese en un matiz. Las páginas de Microsoft 365 sobre licencias con PowerShell piden User.ReadWrite.All, que también sirve. Sin embargo, Find-MgGraphCommand señala un permiso más acotado, que solo toca las licencias.
Grupos, roles y organización
| Comando | Para qué sirve | Permiso de menos privilegios |
|---|---|---|
Get-MgGroup | Lista o busca grupos | Group.Read.All |
New-MgGroup | Crea un grupo | Group.Create |
Get-MgGroupMember | Lista los miembros de un grupo | GroupMember.ReadBasic.All |
New-MgGroupMember | Añade un miembro a un grupo | GroupMember.ReadWrite.All |
Get-MgDirectoryRole y Get-MgDirectoryRoleMember | Muestran los roles de administrador y quién los tiene | RoleManagement.Read.Directory |
Get-MgDomain | Lista los dominios de la organización | Domain.Read.All |
Get-MgOrganization | Datos de la organización | Organization.Read.All |
Get-MgAuditLogSignIn | Registro de inicios de sesión | AuditLog.Read.All |
Get-MgServiceAnnouncementIssue | Incidentes abiertos en los servicios de Microsoft 365 | ServiceHealth.Read.All |
Cómo consulta Microsoft Graph: lo que cambia frente a los módulos viejos
Microsoft Graph no devuelve las cosas como MSOnline. Hay cuatro diferencias que explican la mayoría de los informes incompletos.
Sin -All, solo llegan las primeras 100 cuentas
Graph entrega los resultados por páginas. Para los usuarios, la página trae 100 de forma predeterminada y como máximo 999. Microsoft lo dice así: si usa Get-MgUser sin -All, solo se devuelven las primeras 100 cuentas. El problema es que no sale ningún aviso.
Hagamos la cuenta con una empresa de 120 personas. Sin -All, el informe trae 100 y faltan 20, el 16,7 %. Por eso, siempre que quiera la lista entera, añada -All. Además, -PageSize 999 reduce las llamadas: con 1.250 cuentas, pasan de 13 páginas de 100 a 2 de 999. Hay una excepción: si pide la actividad de inicio de sesión, la página máxima baja a 500.
Sin -Property, faltan columnas
Por defecto, Get-MgUser trae pocas propiedades, y en pantalla muestra solo cuatro columnas. Lo comprobamos con un usuario de prueba construido en la consola, sin datos reales:
DisplayName Id Mail UserPrincipalName
----------- -- ---- -----------------
Ana Pérez 00000000-0000-0000-0000-000000000001 ana@empresa.com ana@empresa.com
El tipo de objeto, sin embargo, tiene 139 propiedades. Si su script lee el cargo, el departamento o las licencias sin pedirlos con -Property, las encontrará vacías. Y una columna vacía no da error: simplemente sale en blanco en el CSV.
Filtrar en el servidor y contar
-Filter hace que el filtro lo aplique Microsoft, y no su equipo. Por eso es mucho más rápido que traer todo y filtrar con Where-Object. Algunos filtros, como contar las licencias de cada cuenta, son consultas avanzadas. Para esas, Microsoft exige -ConsistencyLevel eventual y -CountVariable:
Get-MgUser -Filter "startsWith(displayName, 'Ana')" -All
Get-MgUser -Filter "accountEnabled eq false" -All -Property DisplayName, UserPrincipalName
Get-MgUser -Filter 'assignedLicenses/$count eq 0' -ConsistencyLevel eventual -CountVariable total -All
Ojo con las comillas del tercer ejemplo. Van simples para que PowerShell no lea $count como una variable.
Los objetos vienen anidados
Muchas propiedades de Graph son objetos dentro de objetos. Por ejemplo, las licencias compradas de un plan están en PrepaidUnits.Enabled, y la fecha del último inicio de sesión, en SignInActivity.LastSuccessfulSignInDateTime. Para verlas en una tabla, hay que sacarlas con Select-Object y una propiedad calculada, como en los escenarios que siguen.
Escenarios de ejemplo de Microsoft 365 PowerShell
Cada escenario empieza con la conexión y los ámbitos que necesita. Así puede copiarlo entero. Cambie los nombres de ejemplo, como ana.perez@empresa.com, por los de su organización.
1. Inventario de usuarios en un archivo CSV
Connect-MgGraph -Scopes "User.Read.All" -NoWelcome
Get-MgUser -All -PageSize 999 -Property DisplayName, UserPrincipalName, JobTitle, Department, AccountEnabled, UsageLocation, CreatedDateTime |
Select-Object DisplayName, UserPrincipalName, JobTitle, Department, AccountEnabled, UsageLocation, CreatedDateTime |
Export-Csv -Path .\usuarios.csv -NoTypeInformation -Encoding UTF8
Después, el archivo se abre en Excel. Con él se responde en un minuto quién tiene cuenta, de qué área es y cuáles cuentas están bloqueadas.
2. Cuántas licencias quedan libres
Connect-MgGraph -Scopes "LicenseAssignment.Read.All" -NoWelcome
Get-MgSubscribedSku -All | Select-Object SkuPartNumber,
@{ n = 'Compradas'; e = { $_.PrepaidUnits.Enabled } },
@{ n = 'Asignadas'; e = { $_.ConsumedUnits } },
@{ n = 'Libres'; e = { $_.PrepaidUnits.Enabled - $_.PrepaidUnits.Warning - $_.ConsumedUnits } }
La fórmula es la de Microsoft: licencias activas, menos las que están en aviso, menos las asignadas. Según Microsoft, las que están en aviso son de una suscripción vencida, en su periodo de gracia para renovar, así que no cuentan como libres. El nombre del plan sale en código: Microsoft 365 Business Standard, por ejemplo, aparece como O365_BUSINESS_PREMIUM, y Business Premium como SPB.
3. Alta de una persona con su licencia
Connect-MgGraph -Scopes "User.ReadWrite.All", "LicenseAssignment.ReadWrite.All" -NoWelcome
$clave = @{ Password = 'Cambie-Esta-Clave-2026!'; ForceChangePasswordNextSignIn = $true }
$nueva = New-MgUser -DisplayName 'Ana Pérez' -GivenName 'Ana' -Surname 'Pérez' -UserPrincipalName 'ana.perez@empresa.com' -MailNickname 'ana.perez' -UsageLocation 'CO' -JobTitle 'Contadora' -Department 'Finanzas' -PasswordProfile $clave -AccountEnabled
$sku = Get-MgSubscribedSku -All | Where-Object SkuPartNumber -eq 'O365_BUSINESS_PREMIUM'
Set-MgUserLicense -UserId $nueva.Id -AddLicenses @{ SkuId = $sku.SkuId } -RemoveLicenses @()
Tres detalles evitan errores al asignar licencias con PowerShell. Primero, -UsageLocation es obligatorio para asignar una licencia, y en Colombia es CO. Segundo, -RemoveLicenses @() va aunque no quite nada: la API de Microsoft lo exige, aunque el módulo no lo marca como obligatorio. Tercero, ForceChangePasswordNextSignIn obliga a la persona a cambiar la clave en su primer inicio de sesión.
4. Agregar a una persona a un grupo
Connect-MgGraph -Scopes "GroupMember.ReadWrite.All", "User.Read.All" -NoWelcome
$grupo = Get-MgGroup -Filter "displayName eq 'Finanzas'"
$usuario = Get-MgUser -UserId 'ana.perez@empresa.com'
New-MgGroupMember -GroupId $grupo.Id -DirectoryObjectId $usuario.Id
Si la organización asigna licencias por grupo, este paso basta para que la persona reciba la suya. Eso sí, según Microsoft, la asignación por grupo no llega a los grupos anidados: solo reciben licencia los miembros directos.
5. Quitar un servicio de la licencia sin perder los ya quitados
Esta es la trampa más cara de las licencias con PowerShell, y la documentación de Microsoft la advierte. En Set-MgUserLicense, la lista DisabledPlans reemplaza la que la persona ya tenía. Si no la suma, vuelve a activar lo que alguien había desactivado antes. El siguiente bloque desactiva Microsoft Bookings y conserva lo anterior:
Connect-MgGraph -Scopes "LicenseAssignment.ReadWrite.All" -NoWelcome
$upn = 'ana.perez@empresa.com'
$sku = Get-MgSubscribedSku -All | Where-Object SkuPartNumber -eq 'O365_BUSINESS_PREMIUM'
$detalle = Get-MgUserLicenseDetail -UserId $upn | Where-Object SkuId -eq $sku.SkuId
$yaQuitados = @($detalle.ServicePlans | Where-Object ProvisioningStatus -eq 'Disabled' | Select-Object -ExpandProperty ServicePlanId)
$nuevos = @($sku.ServicePlans | Where-Object ServicePlanName -eq 'MICROSOFTBOOKINGS' | Select-Object -ExpandProperty ServicePlanId)
$quitar = @($yaQuitados + $nuevos | Select-Object -Unique)
Set-MgUserLicense -UserId $upn -AddLicenses @{ SkuId = $sku.SkuId; DisabledPlans = $quitar } -RemoveLicenses @()
6. Retiro de una persona: bloquear y cerrar sus sesiones
Connect-MgGraph -Scopes "User.ReadWrite.All", "User.RevokeSessions.All" -NoWelcome
$upn = 'ana.perez@empresa.com'
Update-MgUser -UserId $upn -AccountEnabled:$false
Revoke-MgUserSignInSession -UserId $upn
Get-MgUser -UserId $upn -Property DisplayName, AccountEnabled | Select-Object DisplayName, AccountEnabled
Bloquear la cuenta impide los inicios de sesión nuevos. En cambio, cerrar las sesiones invalida las que ya estaban abiertas, por ejemplo en el celular. Según Microsoft, puede tardar unos minutos en aplicarse.
No quite la licencia el mismo día sin decidir qué pasa con el correo y los archivos. A menudo conviene antes convertir el buzón en un buzón compartido o pasar sus archivos a otra persona.
7. Cuentas sin licencia
Connect-MgGraph -Scopes "User.Read.All" -NoWelcome
Get-MgUser -Filter 'assignedLicenses/$count eq 0' -ConsistencyLevel eventual -CountVariable sinLicencia -All -Property DisplayName, UserPrincipalName, AccountEnabled, UserType |
Select-Object DisplayName, UserPrincipalName, AccountEnabled, UserType
"Cuentas sin licencia: $sinLicencia"
Sin embargo, no todas son un problema. Los invitados y las cuentas de los buzones compartidos no necesitan licencia. Revise sobre todo las de tipo Member que están activas.
8. Cuentas que nadie usa desde hace 90 días
Connect-MgGraph -Scopes "User.Read.All", "AuditLog.Read.All" -NoWelcome
$limite = (Get-Date).AddDays(-90)
Get-MgUser -All -PageSize 500 -Property DisplayName, UserPrincipalName, AccountEnabled, SignInActivity |
Where-Object { $_.AccountEnabled -and $_.SignInActivity.LastSuccessfulSignInDateTime -lt $limite } |
Select-Object DisplayName, UserPrincipalName, @{ n = 'UltimoInicioCorrecto'; e = { $_.SignInActivity.LastSuccessfulSignInDateTime } } |
Export-Csv -Path .\inactivos.csv -NoTypeInformation -Encoding UTF8
Este escenario tiene dos requisitos que el código no muestra. La actividad de inicio de sesión exige una licencia Microsoft Entra ID P1 o P2 y el permiso AuditLog.Read.All. Además, la fecha del último inicio correcto solo existe desde el 1 de diciembre de 2023.
Un detalle de PowerShell: una cuenta que nunca inició sesión tiene la fecha vacía, y PowerShell considera vacío menor que cualquier fecha. Por eso, la lista incluye también las cuentas recién creadas. Revíselas antes de bloquear nada.
9. Quién tiene roles de administrador
Connect-MgGraph -Scopes "RoleManagement.Read.Directory" -NoWelcome
Get-MgDirectoryRole -All | ForEach-Object {
$rol = $_.DisplayName
Get-MgDirectoryRoleMember -DirectoryRoleId $_.Id -All | ForEach-Object {
[pscustomobject]@{ Rol = $rol; Miembro = $_.AdditionalProperties['userPrincipalName'] }
}
} | Sort-Object Rol
Un matiz de Microsoft: Get-MgDirectoryRole solo lista los roles activados, es decir, los que ya se han asignado alguna vez. Para esta revisión basta, porque un rol sin activar no tiene a nadie. Por eso, revise esta lista cada mes. Cuantas menos personas tengan el rol de administrador global, menor es el daño si alguien roba una de esas cuentas.
10. Restaurar un usuario borrado
Connect-MgGraph -Scopes "User.Read.All", "User.DeleteRestore.All" -NoWelcome
Get-MgDirectoryDeletedItemAsUser -All -Property Id, DisplayName, UserPrincipalName, DeletedDateTime |
Select-Object Id, DisplayName, UserPrincipalName, DeletedDateTime
Restore-MgDirectoryDeletedItem -DirectoryObjectId 'id-del-usuario'
Según Microsoft, si se restaura dentro de los 30 días siguientes al borrado, vuelven la cuenta y sus datos, y el buzón se recupera completo. Por eso, no deje pasar ese plazo si cree que puede necesitarla.
11. Incidentes abiertos en Microsoft 365
Connect-MgGraph -Scopes "ServiceHealth.Read.All" -NoWelcome
Get-MgServiceAnnouncementIssue -All | Where-Object { -not $_.IsResolved } |
Select-Object Service, Title, Classification, StartDateTime
Así, sirve para responder rápido a la pregunta de si el problema es de Microsoft o de la empresa.
Automatizar sin usuario: conexión con certificado
Los ejemplos anteriores usan una conexión con usuario, que pide iniciar sesión. En cambio, para un script programado, como un informe nocturno, Microsoft documenta la conexión solo de aplicación:
Connect-MgGraph -ClientId "id-de-la-aplicacion" -TenantId "id-de-la-organizacion" -CertificateThumbprint "huella-del-certificado" -NoWelcome
Antes hay que registrar una aplicación en Microsoft Entra ID, darle permisos de aplicación, que exigen el consentimiento de un administrador, y subirle la parte pública de un certificado. El certificado debe estar instalado en el almacén del usuario que ejecuta el script. Microsoft documenta también el uso de un secreto de cliente. Sin embargo, un certificado evita guardar una contraseña dentro del script.
Un detalle del módulo: la documentación usa -CertificateName, y el parámetro real se llama -CertificateSubjectName. Los dos funcionan, porque el primero es otro nombre del segundo. Así lo comprobamos en la versión 2.41.2.
Ojo con un punto de seguridad. Una aplicación con User.ReadWrite.All puede cambiar cualquier cuenta sin que nadie inicie sesión. Dele solo los permisos que su script necesita y guarde el certificado como guardaría una llave maestra.
Recomendaciones para administrar Microsoft 365 con PowerShell
Estas recomendaciones resumen lo que vimos en las fuentes de Microsoft y en el equipo de prueba:
- Use Microsoft Graph PowerShell y retire los scripts de MSOnline y Azure AD PowerShell.
- Instale y cargue solo los submódulos que usa. No ejecute
Import-Module Microsoft.Graph. - Prefiera PowerShell 7. En Windows PowerShell 5.1, el SDK completo no cabe.
- Mantenga el módulo al día con
Update-Module, en cada versión de PowerShell que use. - Pida al conectarse solo los ámbitos que necesita.
Find-MgGraphCommandle dice cuáles. - En los listados, use siempre
-Ally pida con-Propertylas columnas que va a leer. - Fije el país de uso de cada cuenta antes de asignar licencias con PowerShell.
- Al quitar servicios de una licencia, sume los que ya estaban quitados.
- Desconéctese al terminar, sobre todo si administra varias organizaciones.
- Guarde sus scripts y los CSV que generan. Son la prueba de qué se cambió, cuándo y por qué.
Lo que no probamos
Una guía honesta dice dónde termina su laboratorio:
- No nos conectamos a ninguna organización real. Medimos el módulo sin conexión y validamos cada comando y cada parámetro contra Microsoft Graph PowerShell 2.41.2, cargado sin instalarlo.
- Por tanto, las salidas con datos reales no las vimos en pantalla. Los límites que citamos, como las 100 cuentas por página o los 30 días para restaurar, son los publicados por Microsoft.
- No probamos la conexión con certificado ni el modo de compatibilidad de Microsoft Entra PowerShell.
- No probamos el módulo en Linux ni en macOS.
Lo que sí medimos lo hicimos en Windows 11, con Windows PowerShell 5.1 y PowerShell 7.6.7, sin permisos de administrador.
Cuándo conviene delegar la administración de Microsoft 365
Microsoft 365 PowerShell resuelve en minutos lo que en el panel lleva días. Sin embargo, alguien tiene que revisar las licencias cada mes, dar de baja a tiempo a quien se va y vigilar los roles de administrador.
En KHARONTE, ese trabajo forma parte de la administración de Microsoft 365 de nuestra línea de gestión de plataformas: usuarios, licencias, grupos y accesos. Cada solicitud entra por nuestra mesa de ayuda TI, queda registrada y se resuelve con su trazabilidad.
Si además quiere delegar los servidores, el respaldo y el soporte de los puestos de trabajo, todo eso entra en nuestros servicios administrados de TI. Puede ver el alcance completo en la página de servicios de TI para empresas.
Preguntas frecuentes sobre Microsoft 365 PowerShell
¿Qué módulo de PowerShell se usa hoy para Microsoft 365?
Para usuarios, licencias y grupos, Microsoft Graph PowerShell, que reemplazó a Azure AD PowerShell. Para el correo, SharePoint y Teams, sus módulos propios: ExchangeOnlineManagement, el de SharePoint Online y MicrosoftTeams.
¿Cómo me conecto a Microsoft 365 con PowerShell?
Instale el módulo con Install-Module Microsoft.Graph.Users -Scope CurrentUser y ejecute Connect-MgGraph -Scopes "User.Read.All". Para cambiar datos, Connect-MgGraph necesita además los ámbitos de escritura. Se abrirá la ventana de inicio de sesión de Microsoft.
¿Todavía funciona Connect-MsolService?
No. MSOnline dejó de funcionar entre abril y mayo de 2025, y ya no se puede instalar desde la Galería de PowerShell. Su reemplazo es Connect-MgGraph.
¿Por qué Get-MgUser no trae a todos los usuarios?
Porque sin -All solo devuelve la primera página, de 100 cuentas. Añada -All, y -PageSize 999 si son muchas.
¿Por qué falla Import-Module Microsoft.Graph en Windows PowerShell 5.1?
Porque el SDK completo supera el límite de 4.096 funciones de esa versión. Cargue solo los submódulos que usa o pase a PowerShell 7.
¿Necesito una licencia especial para usar PowerShell?
No. Los módulos son gratuitos. Necesita una cuenta con el rol adecuado y los ámbitos de Graph. Solo algunos datos, como la actividad de inicio de sesión, exigen Microsoft Entra ID P1 o P2.




