Inicios de sesión fuera de México
Lista los inicios de sesión correctos de los últimos días que vienen de un país no permitido o desconocido. Si tu empresa opera solo en México, cada fila merece una revisión.
Ejemplo en México
Distribuidora de alimentos en Culiacán, 90 personas
Toda la operación está en Sinaloa, pero alguien de Finanzas recibió un correo raro y el director quiere saber si alguien entró desde otro país.
.\get-inicios-sesion-fuera-de-mexico.ps1 -Dias 14 -RutaCsv .\fuera-de-mexico.csvAparecen dos inicios desde otro país en la cuenta de Finanzas, sin viajes registrados. Revoca las sesiones, cambia la contraseña y activa MFA para esa área.
Cómo implementarlo
- Abre PowerShell 7 con el módulo Microsoft.Graph.Authentication instalado.
- Corre el script con -Dias, -PaisesPermitidos y -RutaCsv e inicia sesión con una cuenta con el rol Lector de informes.
- Agrupa el CSV por Cuenta y Pais, y marca lo que no corresponda a un viaje conocido.
- Confirma con la persona; si no fue ella, restablece su contraseña, revoca sus sesiones y considera una política de acceso condicional por ubicación.
Ver el código completo de get-inicios-sesion-fuera-de-mexico.ps1
<#
.SYNOPSIS
¿Quién inició sesión correctamente desde fuera de los países permitidos?
.DESCRIPTION
Revisa los inicios de sesión correctos de los últimos N días y lista los que vienen de otro país o de uno
que Microsoft no pudo determinar. Útil cuando la empresa opera solo en México y un acceso desde otro país es una señal de alerta.
Solo lectura: no cambia nada en el tenant.
.PARAMETER RutaCsv
Ruta del CSV de salida. Si se omite, solo se muestra en pantalla.
.PARAMETER Dias
Días hacia atrás que se revisan, de 1 a 30. Por defecto, 7.
.PARAMETER PaisesPermitidos
Códigos de país de dos letras que no se reportan. Por defecto, MX.
.PARAMETER ZonaHoraria
Zona horaria para mostrar las fechas. Por defecto, Central Standard Time (Mexico).
.EXAMPLE
.\get-inicios-sesion-fuera-de-mexico.ps1 -Dias 14 -PaisesPermitidos MX,US -RutaCsv .\fuera-de-mexico.csv
.NOTES
Permisos: AuditLog.Read.All
Rol mínimo: Lector de informes (Reports Reader)
Licencia: Microsoft Entra ID P1
Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas.
#>
[CmdletBinding()]
param(
[string]$RutaCsv,
# Microsoft Entra ID P1 y P2 conservan los inicios de sesión 30 días.
[ValidateRange(1, 30)][int]$Dias = 7,
[ValidatePattern('^[A-Za-z]{2}$')][string[]]$PaisesPermitidos = @('MX'),
[string]$ZonaHoraria = 'Central Standard Time (Mexico)'
)
$ErrorActionPreference = 'Stop'
$permisos = @('AuditLog.Read.All')
if (-not (Get-Command Connect-MgGraph -ErrorAction SilentlyContinue)) {
throw 'Falta el módulo. Instálalo con: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser'
}
$zona = [TimeZoneInfo]::FindSystemTimeZoneById($ZonaHoraria)
Connect-MgGraph -Scopes $permisos -NoWelcome
function Format-FechaLocal {
param($Valor)
if (-not $Valor) { return $null }
$utc = if ($Valor -is [datetimeoffset]) { $Valor.UtcDateTime }
elseif ($Valor -is [datetime]) { if ($Valor.Kind -eq 'Unspecified') { [datetime]::SpecifyKind($Valor, 'Utc') } else { $Valor.ToUniversalTime() } }
else { [datetimeoffset]::Parse("$Valor", [cultureinfo]::InvariantCulture).UtcDateTime }
[TimeZoneInfo]::ConvertTimeFromUtc($utc, $zona).ToString('yyyy-MM-dd HH:mm', [cultureinfo]::InvariantCulture)
}
$desde = [datetime]::UtcNow.AddDays(-$Dias).ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", [cultureinfo]::InvariantCulture)
# El país se filtra aquí porque la API solo permite eq y startsWith sobre él. Sin rango de fechas, la consulta puede agotar el tiempo.
$uri = "https://graph.microsoft.com/v1.0/auditLogs/signIns?`$filter=createdDateTime ge $desde and status/errorCode eq 0&`$top=1000"
$inicios = while ($uri) {
$pagina = Invoke-MgGraphRequest -Method GET -Uri $uri
$pagina.value
$uri = $pagina.'@odata.nextLink'
}
$filas = foreach ($s in $inicios) {
if ([int]$s.status.errorCode -ne 0) { continue }
$pais = "$($s.location.countryOrRegion)".Trim()
if ($pais -and $pais -in $PaisesPermitidos) { continue }
[pscustomobject]@{
Fecha = Format-FechaLocal $s.createdDateTime
Nombre = $s.userDisplayName
Cuenta = $s.userPrincipalName
Pais = if ($pais) { $pais } else { 'Desconocido' }
Ciudad = $s.location.city
DireccionIP = $s.ipAddress
Aplicacion = $s.appDisplayName
Cliente = $s.clientAppUsed
AccesoCondicional = $s.conditionalAccessStatus
}
}
Write-Host "Inicios de sesión correctos fuera de $($PaisesPermitidos -join ', ') en $Dias días: $(@($filas).Count)"
if ($RutaCsv) {
# Excel ejecutaría como fórmula un nombre que empiece con estos caracteres.
foreach ($fila in $filas) { foreach ($p in $fila.PSObject.Properties) { if ($p.Value -is [string] -and $p.Value -match '^[=+\-@]') { $p.Value = "'" + $p.Value } } }
# UTF-8 con BOM para que Excel en español muestre bien los acentos.
$filas | Export-Csv -Path $RutaCsv -NoTypeInformation -Encoding utf8BOM
Write-Host "CSV guardado en $RutaCsv"
} else {
$filas | Format-Table -AutoSize
}