signin_failure_stats
Aggregate tenant-wide Entra ID sign-in failures by AADSTS code, top users, apps, and source IPs; flag IPs spraying passwords across multiple accounts.
Instructions
Tenant-wide sign-in failure aggregation -- the Entra ID counterpart to the RADIUS failure patrol.
Time-bounded (ENTRAADM_DEADLINE, default 45 s): on a wide window or a busy day the scan
stops early and capped=true marks the counts as a lower bound; narrow hours for a
full count.
Aggregates failed sign-ins across the whole tenant into four views: top
AADSTS error codes (with the same meaning annotations as
signin_logs), top failing users, top applications, and top source
IPs. spray_suspects flags any IP with failed sign-ins against 5 or
more distinct users -- Entra's smart lockout is per-account, so a
low-and-slow password spray from one IP across many accounts does not
trip it the way a brute force against one account does; this is the
observation a per-account view cannot make on its own. This mirrors the
KeyCloak-side spray detection this fleet already relies on; neither the
official Microsoft MCP Server for Enterprise nor Graph itself offers this
aggregation.
When ENTRAADM_SITES_INI names the tenant's own address ranges, IPs inside
them get site (and site_name) in top_ips, and suspects inside
them move from spray_suspects to internal_spray_suspects: a NAT
egress or a room of shared workstations fails for many users every day,
and would otherwise bury an outside source. Internal suspects are kept,
not dropped, because an internal host can be compromised too. sites
reports whether the file loaded; an invalid CIDR is reported in
sites.error while the valid ranges are still used, and a file that
cannot be read leaves every IP in spray_suspects.
Read-only (AuditLog.Read.All application permission, or -- for azure-cli
auth -- the Reports Reader directory role). Graph cannot filter sign-ins
on status/errorCode server-side, so this walks up to max_pages of the
full sign-in log for the window and aggregates client-side --
capped=true means the page budget ran out before the window was
fully scanned, so the counts below are a sample of the window, not a
census of it.
Args: hours: How far back to look, clamped to [1, 720] (30 days). max_pages: Page budget (default: ENTRAADM_MAX_PAGES_DEFAULT). tenant: Tenant to read: a [tenant.] section name or one of its domains (default: the default tenant).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| hours | No | ||
| tenant | No | ||
| max_pages | No |