Las licencias de Microsoft 365 sin usar vacían el presupuesto en silencio, mientras que las cuentas inactivas de Azure AD suponen un riesgo de seguridad real. Evita elementos innecesarios en tu dominio automatizando las comprobaciones con Graph API de Microsoft para identificar licencias caducadas y usuarios inactivos.
En esta guía te ayudamos a programar comprobaciones críticas con PowerShell para cerrar vulnerabilidades y escalar en varios clientes.
Si prefieres una explicación visual rápida, mira nuestra guía en vídeo sobre cómo programar comprobaciones con Graph API para licencias caducadas y usuarios inactivos.
Cómo programar comprobaciones de Graph API con PowerShell paso a paso
Automatiza las comprobaciones de Graph API para recuperar licencias desperdiciadas, proteger cuentas inactivas, cumplir con la normativa y simplificar la gestión de inquilinos, todo de una sola pasada.
📌 Caso de uso: aplica este método durante las revisiones mensuales de costes, las auditorías de seguridad o antes de renovar licencias. Es ideal para MSP que quieren recuperar licencias caducadas de Office 365 o reforzar la seguridad después de dar de baja a empleados. También te ayuda a detectar usuarios inactivos antes de que se conviertan en vulnerabilidades para tu sistema.
📌 Requisitos previos: antes de empezar con el procedimiento, asegúrate de contar con lo siguiente:
- Permisos administrativos: comprueba que tu cuenta tenga el rol de Administrador global o Lector de informes en Azure AD.
- Herramientas: Microsoft Graph PowerShell SDK (módulo Microsoft.Graph) y PowerShell 7+.
- Configuración de Azure: asegúrate de que tu aplicación de Azure AD tenga los permisos User.Read.All, LicenseAssignment.ReadWrite.All y AuditLog.Read.All para la automatización.
- Opcional pero recomendable: descarga e instala una herramienta RMM como NinjaOne para programar scripts, supervisar los resultados y escalar entre inquilinos.
Te recomendamos revisar ⚠️ Aspectos que debes tener en cuenta antes de continuar.
Paso 1: instala y autentica el SDK de Microsoft Graph
Prepara tus herramientas para automatizar las comprobaciones de licencias y usuarios. Al instalar el SDK de Graph para consultar datos de Azure AD (que el administrador suele ocultar), vinculas tu script a la API de Microsoft mediante autenticación.
Procedimiento paso a paso:
- Instala el SDK ejecutando este script en PowerShell (Admin) o Windows Terminal (Admin):
Install-Module Microsoft.Graph -Scope CurrentUser -Force
- Autentícate con este script:
Connect-MgGraph -Scopes "User.Read.All", "Reports.Read.All", "Directory.Read.All"
También puedes configurar una aplicación registrada con un secreto de cliente y usar el flujo de certificado o de credenciales de cliente para una ejecución desatendida.
Una vez autenticado el SDK de Graph, en los siguientes pasos podrás consultar los usuarios inactivos de Azure AD y las licencias caducadas de Office 365.
Paso 2: consulta los usuarios con licencias caducadas o sin asignar
Este paso te ayudará a descubrir licencias caducadas y cuentas inactivas que suponen un riesgo de seguridad y costes innecesarios.
Procedimiento paso a paso:
- En PowerShell (Admin), ejecuta este script para comprobar las licencias sin asignar a nivel de inquilino:
Get-MgSubscribedSku |Select-Object SkuPartNumber,@{Name="TotalLicenses"; Expression={$_.PrepaidUnits.Enabled}},@{Name="UsedLicenses"; Expression={$_.ConsumedUnits}},@{Name="Available"; Expression={$_.PrepaidUnits.Enabled - $_.ConsumedUnits}} |Format-Table -AutoSize
- Ejecuta este script para encontrar usuarios sin licencia:
Get-MgUser -Filter "assignedLicenses/`$count eq 0" `-ConsistencyLevel eventual `-CountVariable unlicensedCount `-All |Select-Object DisplayName, UserPrincipalName |Export-Csv -Path "UnlicensedUsers.csv" -NoTypeInformation
- Ejecuta este script para encontrar usuarios inactivos con licencia:
$daysInactive = 90$cutoffDate = (Get-Date).AddDays(-$daysInactive)# Get licensed inactive usersGet-MgUser -All -Property "DisplayName,UserPrincipalName,SignInActivity,AssignedLicenses" |Where-Object {($_.SignInActivity.LastSignInDateTime -lt $cutoffDate) -and($_.AssignedLicenses.Count -gt 0)} |Select-Object DisplayName,UserPrincipalName,@{Name="LastSignIn"; Expression={$_.SignInActivity.LastSignInDateTime}} |Export-Csv -Path "InactiveLicensedUsers.csv" -NoTypeInformation
⚠️ Importante: ten en cuenta que SignInActivity requiere Azure AD Premium P1/P2. Evita también los endpoints beta en los scripts de producción.
Después de ejecutar estos scripts, tendrás dos archivos CSV: UnlicensedUsers.csv, con los usuarios que necesitan que se les asigne una licencia, e InactiveLicensedUsers.csv, que enumera las cuentas que representan un riesgo de seguridad.
Paso 3: exporta y registra los resultados de la auditoría
Una vez que tengas la lista de licencias caducadas y sin asignar, convierte esos datos en información accionable con informes automatizados. Exportar estos datos crea registros de auditoría permanentes que sirven como prueba de cumplimiento, y la automatización garantiza que detectes los problemas antes de que salgan caros.
Procedimiento paso a paso:
- En PowerShell (Admin), ejecuta este script para exportar a un CSV con marca de tiempo:
- Script para generar un nombre de archivo con marca de tiempo:
$timestamp = Get-Date -Format "yyyyMMdd-HHmm"$reportPath = "C:\Audits\LicenseReport_$timestamp.csv"
- Exporta los usuarios inactivos con licencia:
$inactiveUsers | Export-Csv -Path $reportPath -NoTypeInformation
- Esto generará archivos CSV que se pueden abrir en cualquier programa, y la marca de tiempo crea registros con versiones para el cumplimiento.
- (Opcional): ejecuta este script para enviar los informes por correo electrónico:
Send-MailMessage -From "[email protected]" -To "[email protected]" `-Subject "ACTION REQUIRED: $($inactiveUsers.Count) Inactive Licensed Users" `-Body "Attached: Users with licenses but no sign-ins for 90+ days" `-Attachments $reportPath -SmtpServer "smtp.office365.com" -Port 587 `-Credential (Get-Credential)
- Ejecuta este script para automatizarlo por completo con la aplicación de Azure AD:
$tenantId = "YOUR_TENANT_ID"$clientId = "YOUR_CLIENT_ID"$clientSecret = "YOUR_SECRET" | ConvertTo-SecureString -AsPlainText -ForceConnect-MgGraph -TenantId $tenantId -ClientId $clientId -ClientSecret $clientSecret
💡 Consejo: guarda los secretos en Azure Key Vault. Nunca los incluyas directamente en el código.
Después de ejecutar estos scripts, tendrás informes CSV con marca de tiempo guardados localmente y, opcionalmente, alertas por correo electrónico enviadas al instante a las partes interesadas. Estos archivos te permiten programar ejecuciones automáticas mediante el Programador de tareas o herramientas RMM como NinjaOne para garantizar una supervisión continua.
Paso 4: automatiza con el Programador de tareas o una política de RMM
Programas el script una vez y deja que se ejecute cada mes, cada año o de forma indefinida, con lo que consigues una supervisión proactiva sin mover un dedo. Hacer revisiones periódicas te ayuda a detectar incidencias antes de que causen problemas. Hay dos opciones para conseguirlo:
Opción 1: con el Programador de tareas (un solo equipo)
Procedimiento paso a paso:
- Abre el Símbolo del sistema (Admin) y ejecuta este comando:
schtasks /create /tn "M365LicenseAudit" /tr `"powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\LicenseAudit.ps1" `/sc weekly /d MON /st 02:00 /ru "NT AUTHORITY\SYSTEM"
- Funciona porque se ejecuta como cuenta del sistema incluso cuando no hay ningún usuario con la sesión iniciada.
Opción 2: con herramientas RMM
Procedimiento paso a paso:
- Abre tu herramienta RMM preferida, como NinjaOne.
- Crea el script (el de la Opción 1) y guárdalo en la biblioteca de scripts de NinjaOne.
- Asigna el script al grupo de políticas de clientes de Microsoft 365.
- Configura los resultados:
- Guarda los CSV en \\ClientShare\Audits.
- Súbelos al panel del RMM para consultarlos de forma centralizada.
Una vez programado, el script se ejecutará en la fecha y hora asignadas, guardará automáticamente los informes en las carpetas designadas y mostrará las tendencias entre inquilinos.
⚠️ Aspectos que debes tener en cuenta
En esta sección destacamos los posibles retos que conviene tener presentes mientras sigues esta guía.
| Riesgos | Posibles consecuencias | Soluciones |
| 1. Faltan datos en los informes | Auditorías incompletas, riesgos sin detectar | Comprueba los permisos: ejecuta Get-MgContext para verificar ámbitos como User.Read.All.Evita los bloqueos de MFA: usa autenticación con certificado en lugar de secretos. |
| 2. El script falla sin avisar | Falsa sensación de seguridad, brechas de cumplimiento | Activa el registro: añade Start-Transcript -Path C:\Logs\Audit.log a los scripts.Supervisa el Visor de eventos: consulta en Registros de aplicaciones y servicios > PowerShell. |
| 3. No se marcan usuarios | Se pasan por alto licencias caducadas o cuentas inactivas | Valida los umbrales: confirma que $cutoffDate coincide con la política. Verifica los campos de licencia: usa |
| 4. Secretos escritos en el código | Inquilino comprometido, robo de credenciales | Rota los secretos de inmediato en Azure AD. Migra a Azure Key Vault: usa |
| 5. Dependencia de la API beta | El script deja de funcionar tras las actualizaciones de Microsoft | Sustituye los endpoints beta: usa Get-MgUser (v1.0) en lugar de Get-MgBetaUser.Suscríbete al changelog de Microsoft Graph. |
Claves para unas auditorías con Graph API y PowerShell a prueba de balas
Ajusta estos parámetros a tu entorno. La precisión evita falsas alarmas y mantiene los flujos de trabajo fluidos.
Exclusiones en los informes
Excluye de los análisis de inactividad las cuentas de servicio, los buzones compartidos y las cuentas de emergencia. Así evitas recuperar por error licencias de recursos críticos. Puedes usar filtros de PowerShell para incluirlas en una lista de permitidos con este script:
$excludedUPNs = @("[email protected]", "[email protected]")Get-MgUser | Where-Object { $_.UserPrincipalName -notin $excludedUPNs }
Umbrales de inactividad
Define los umbrales según tu tolerancia al riesgo. Si una cuenta lleva 90 días inactiva, el riesgo es bajo; 60 días es un riesgo medio, y 30 días, un riesgo alto. Según el nivel de riesgo, actúa enviando correos de aviso, revocando sus licencias o desactivando la cuenta para después recuperarla. Así te proteges frente a posibles incidencias.
Frecuencia de las auditorías
Planifica bien el momento de tus auditorías para evitar riesgos de seguridad y otros problemas. Para la mayoría de las empresas se recomienda hacer comprobaciones mensuales que coincidan con los ciclos de facturación.
En sectores con mucha rotación, las comprobaciones semanales permiten que los informes sigan el ritmo de los cambios del entorno. También deberías lanzar la auditoría de inmediato cada vez que un empleado deje la empresa.
Asignación de acciones
Crea procedimientos (SOP) que activen automáticamente los siguientes pasos. Ejecuta estos scripts para recuperar licencias y desactivar cuentas:
- Para recuperar licencias:
Set-MgUserLicense -UserId "[email protected]" -RemoveLicenses @("skuId")
- Para desactivar cuentas:
Update-MgUser -UserId "[email protected]" -AccountEnabled:$false
Después de ejecutar estos scripts, archiva los datos. Envíalos al responsable o a SharePoint mediante Graph API.
Cómo NinjaOne simplifica tus auditorías de licencias
Convierte las comprobaciones de Graph API de tareas manuales en motores de cumplimiento totalmente automatizados y multiinquilino.
- Despliegue centralizado de scripts: envía tus scripts de PowerShell con Graph API a más de 100 endpoints o inquilinos en una sola acción.
- Supervisión automática de resultados: haz seguimiento de los informes y registros CSV de todos tus clientes. Las alertas se disparan al instante si los archivos no se actualizan.
- Alertas de fallo en tus herramientas: recibe notificaciones en Slack, Teams o por correo electrónico cuando un script falle.
- Informes unificados entre inquilinos: unifica los datos de licencias caducadas de Office 365 y de usuarios inactivos en un único panel ejecutivo.
- Remediación automática: crea tickets automáticamente en tu herramienta PSA con SOP predefinidos para detectar licencias caducadas.
Controla costes y seguridad con las comprobaciones de licencias de Graph API
Automatizar las auditorías de licencias y usuarios transforma la supervisión reactiva en un control proactivo. Al recuperar licencias sin usar y desactivar cuentas inactivas, evitas fugas en la facturación y cierras vectores de ataque. Aplica el procedimiento anterior para convertir las licencias sin usar en ahorro presupuestario y las cuentas obsoletas en cumplimiento.
Guía de inicio rápido
NinjaOne te ayuda a programar comprobaciones con Graph API para licencias caducadas y usuarios inactivos. La plataforma NinjaOne ofrece herramientas y orientación para:
- Auditar licencias caducadas: usa Graph API para identificar las licencias que han superado su fecha de caducidad
- Comprobar usuarios inactivos: detecta a los usuarios que no han iniciado sesión ni usado los servicios en un periodo determinado
- Automatizar estas comprobaciones: configura tareas programadas para ejecutar estas auditorías con regularidad
Temas relacionados
- 8 buenas prácticas para proteger las API
- Cómo alertar sobre cuentas de usuario inactivas en Linux: guía completa de scripts
- Automatiza la detección de cuentas de usuario inactivas con PowerShell
- Identificar y gestionar cuentas locales sin usar en sistemas Windows
- Guía completa para desactivar cuentas de usuario en Linux dirigida a profesionales de TI
