admin_hub

sysop@hub:~/tools/signin-analyzer$

signin-analyzer

Drop an Entra ID sign-in log export. You get a triage report: what looks like an attack, which accounts are affected, what to do next, and a KQL query to keep hunting. The file is read by your browser and never uploaded, so it also works offline.

getting the export

SourceHow
Entra admin centerMonitoring & health, Sign-in logs, set the date range, Download, CSV. Interactive and non-interactive sign-ins are separate files; analyse them one at a time.
Microsoft Graph PowerShellGet-MgAuditLogSignIn -All | ConvertTo-Json -Depth 6 | Out-File signins.json (PowerShell 7, needs AuditLog.Read.All)
One script for both analyzersGenerate it in the script generator: a read-only export of Conditional Access policies and sign-ins.
Log Analytics / SentinelSigninLogs | where TimeGenerated > ago(7d), then Export, CSV (all columns)

what it checks

CheckTriggers when
Password sprayOne IP fails with a wrong password, lockout or unknown user against 5 or more accounts. Critical if that IP then signs in successfully.
Password guessing10 or more wrong-password or lockout events on one account. Critical if an address with 3 or more of those failures later succeeds.
Country switchTwo successful sign-ins by one account from different countries and different IPs within 2 hours.
MFA fatigue5 or more failed or denied MFA prompts (500121) for one account. Critical if a sign-in succeeds during or within an hour after.
Legacy authenticationAny sign-in over IMAP, POP, SMTP, ActiveSync or "Other clients". High if any succeed.
Scripted clientsUser agents of HTTP libraries and tools (python-requests, axios, curl, BAV2ROPC and similar). High if any succeed.
Rare countriesCountries with under 2% of successful sign-ins, once there are at least 50 successes.
Single-factor successSuccessful sign-ins where the authentication requirement was single-factor.
Disabled and unknown accountsAttempts on disabled accounts (50057), or 5 or more different non-existent usernames (50034).
Conditional Access blocksSign-ins stopped by policy (53003, 53000, 53001). The password was correct in these cases.

These are heuristics, the same first-pass questions an analyst asks of a sign-in log. They produce leads, not verdicts: a VPN looks like a country switch, a forgotten phone looks like password guessing. Location is only as good as the IP geolocation in the export, and compared at country level. A check that needs a column your export lacks is listed as not checked instead of silently passing. This is no replacement for Entra ID Protection or a SIEM that watches continuously.

Related: AADSTS error lookup incident runbook generator compromised account checklist KQL cheat sheet