A PowerShell script that queries Microsoft Entra ID sign-in logs and generates a rich CSV + HTML report of authentication events, with built-in detection and enrichment for YubiKey / FIDO2 passkey sign-ins.
- Two query modes — pull sign-in data directly from Microsoft Graph API, or from a Log Analytics / Microsoft Sentinel workspace via KQL.
- FIDO2 / YubiKey detection using four layered methods:
- AAGUID lookup against the built-in Yubico AAGUID registry (YubiKey 5 Series, Bio Series, Security Key Series, FIPS variants)
- Keyword matching on
authenticationMethodDetailandadditionalDetails(yubikey,yubico) - Hardware OATH token entries that reference Yubico identifiers
- FIDO MDS3 authenticator description fallback — catches future Yubico AAGUIDs not yet in the static list without requiring a code update
- FIDO Alliance MDS3 enrichment — downloads the live FIDO Alliance Metadata Service blob and cross-references each AAGUID to provide:
- Authenticator name as registered by the vendor (e.g. YubiKey 5C NFC)
- Minimum certified firmware version per the MDS3
authenticatorVersionfield (binary bitfield and decimal encodings are both handled automatically)
- HTML report with sortable columns, colour-coded rows (green = success, red = failure, highlighted for YubiKey events), inline YubiKey badges, an authentication method summary, and a FIDO2 Authenticator Inventory table.
- CSV report with all fields for downstream analysis or import into SIEM/SOAR tools.
- Multiple authentication modes — interactive device-code flow, service principal with client secret, or service principal with certificate.
For each run the script writes two files to the output directory:
| File | Description |
|---|---|
<ReportName>.csv |
All sign-in events with full field set |
<ReportName>.html |
Interactive HTML report with sorting, colour coding, and FIDO2 inventory |
| Column | Description |
|---|---|
Date / Time |
Local date and time of the sign-in |
DateTimeUTC |
ISO 8601 UTC timestamp |
DisplayName |
User display name |
UPN |
User principal name |
Application |
Application name |
AppId |
Application (client) ID |
AuthenticationMethod |
Method reported by Entra ID (e.g. FIDO2, Password, MicrosoftAuthenticatorPush) |
MethodDetail |
Step-level detail string (e.g. FIDO2 security key) |
AdditionalDetails |
Raw additional details field, which contains the AAGUID for FIDO2 sign-ins |
Aaguid |
Extracted FIDO2 AAGUID (lowercase GUID) |
MdsAuthenticatorName |
Authenticator description from FIDO MDS3 |
MdsMinFirmwareVersion |
Minimum certified firmware decoded from MDS3 (not the user's installed version) |
MdsVersionRaw |
Raw integer from MDS3 authenticatorVersion field |
Status |
Success or Failure |
ErrorCode |
Entra ID error code (0 = success) |
FailureReason |
Failure reason string when Status is Failure |
ConditionalAccess |
Conditional Access evaluation result |
IPAddress |
Source IP address |
Location |
City and country derived from the sign-in location |
IsYubiKey |
True if the event was identified as a YubiKey sign-in |
DeviceId |
Entra device ID |
OperatingSystem |
OS reported by the device |
SignInId |
Unique sign-in event ID |
PowerShell 5.1 or later (included with Windows 10/11). PowerShell 7+ is also supported.
Modules are automatically installed from the PowerShell Gallery if not already present.
| Module | Required for |
|---|---|
Microsoft.Graph.Authentication |
Graph mode — provides Connect-MgGraph and Invoke-MgGraphRequest |
Az.Accounts |
Log Analytics mode — provides Connect-AzAccount |
Az.OperationalInsights |
Log Analytics mode — provides Invoke-AzOperationalInsightsQuery |
Security note: Modules are installed without version pinning. In high-security environments, pre-install specific versions manually before running the script.
The identity used (user or service principal) must have:
| Permission | Type |
|---|---|
AuditLog.Read.All |
Application or Delegated |
Directory.Read.All |
Application or Delegated |
Assign these in Entra ID → App registrations → API permissions (Application permissions require admin consent).
The identity used must have the Log Analytics Reader role on the target workspace.
The script downloads the FIDO Alliance MDS3 blob from https://mds.fidoalliance.org/ at startup. Use -SkipMdsFetch in air-gapped or network-restricted environments to skip this step (MDS columns will be empty).
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-Mode |
string |
No | Graph |
Query mode: Graph or LogAnalytics |
-TenantId |
string |
Yes | — | Entra ID tenant ID (GUID or contoso.onmicrosoft.com) |
-ClientId |
string |
SP auth only | — | App registration client ID for service principal authentication |
-ClientSecret |
SecureString |
SP secret auth | — | Client secret. Must be a SecureString — see examples below |
-CertificateThumbprint |
string |
SP cert auth | — | Thumbprint of a certificate in the current user's certificate store |
-WorkspaceId |
string |
Log Analytics | — | Log Analytics workspace ID (GUID) |
-StartDate |
datetime |
No | 30 days ago | Start of the reporting window |
-EndDate |
datetime |
No | Now | End of the reporting window |
-OutputPath |
string |
No | Current directory | Directory where CSV and HTML files are written |
-ReportName |
string |
No | EntraAuthReport_<timestamp> |
Base filename (no extension) for output files |
-IncludeSuccessOnly |
switch |
No | — | When set, only successful sign-ins are included |
-FilterUser |
string |
No | — | Filter results to a specific UPN or display name substring |
-SkipMdsFetch |
switch |
No | — | Skip the FIDO MDS3 download. MDS columns will be empty |
The script uses named parameter sets to enforce valid authentication combinations:
| Parameter Set | Auth method |
|---|---|
GraphDeviceCode |
Interactive device-code / browser login (default) |
GraphServicePrincipalSecret |
Service principal with client secret |
GraphServicePrincipalCert |
Service principal with certificate |
LADeviceCode |
Log Analytics with interactive login |
LAServicePrincipalSecret |
Log Analytics with service principal + secret |
LAServicePrincipalCert |
Log Analytics with service principal + certificate |
.\Get-EntraAuthReport.ps1 -TenantId contoso.onmicrosoft.com `
-StartDate (Get-Date).AddDays(-7).\Get-EntraAuthReport.ps1 -TenantId contoso.onmicrosoft.com `
-StartDate 2026-04-01 -EndDate 2026-04-30 `
-IncludeSuccessOnly.\Get-EntraAuthReport.ps1 -TenantId contoso.onmicrosoft.com `
-FilterUser jdoe@contoso.com `
-StartDate (Get-Date).AddDays(-14)$secret = ConvertTo-SecureString 'my-client-secret' -AsPlainText -Force
.\Get-EntraAuthReport.ps1 `
-TenantId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' `
-ClientId 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' `
-ClientSecret $secret `
-StartDate (Get-Date).AddDays(-30) `
-OutputPath C:\Reports.\Get-EntraAuthReport.ps1 `
-TenantId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' `
-ClientId 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' `
-CertificateThumbprint 'AABBCCDDEEFF00112233445566778899AABBCCDD' `
-StartDate (Get-Date).AddDays(-7)$secret = ConvertTo-SecureString 'my-client-secret' -AsPlainText -Force
.\Get-EntraAuthReport.ps1 `
-Mode LogAnalytics `
-TenantId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' `
-ClientId 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' `
-ClientSecret $secret `
-WorkspaceId 'zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz' `
-StartDate (Get-Date).AddDays(-7).\Get-EntraAuthReport.ps1 -TenantId contoso.onmicrosoft.com `
-OutputPath C:\Reports `
-ReportName 'April2026-AuthAudit' `
-StartDate 2026-04-01 -EndDate 2026-04-30.\Get-EntraAuthReport.ps1 -TenantId contoso.onmicrosoft.com `
-SkipMdsFetch `
-StartDate (Get-Date).AddDays(-7)The MdsMinFirmwareVersion column shows the minimum certified firmware for the authenticator model as recorded in the FIDO Alliance MDS3 — it is not the firmware version installed on the user's specific device. Entra ID sign-in logs do not expose the actual installed firmware. The MDS3 value represents the baseline version that was certified when the AAGUID was registered; individual devices in the field may be running a newer version.
The Microsoft Graph beta sign-in log endpoint evaluates the entire date-range filter server-side before returning results. For wide time windows this can exceed the SDK's 300-second HTTP timeout. The script mitigates this by querying one day at a time and paginating each day independently. For tenants with very high daily sign-in volume, use -FilterUser or -IncludeSuccessOnly to reduce the dataset, or shorten the date window.
MIT