Azure AD Sign-In and Audit Log Collection

IR AnalystSwitch roles in the top navigation to see different perspectives.

Collect comprehensive Azure Active Directory sign-in logs, audit logs, and provisioning logs for the investigation window. These logs reveal authentication events, privilege changes, application consent grants, and directory modifications.

Actions

  1. 1.Export Azure AD sign-in logs via Microsoft Graph: `Get-MgAuditLogSignIn -Filter "createdDateTime ge T_START and createdDateTime le T_END" -All | Export-Csv signins.csv -NoTypeInformation`. Include both interactive and non-interactive sign-ins.
  2. 2.Export Azure AD audit logs: `Get-MgAuditLogDirectoryAudit -Filter "activityDateTime ge T_START" -All | Export-Csv audit_logs.csv`. Focus on user management, application management, and role management categories.
  3. 3.Collect risky sign-ins and risk detections (requires AAD P2): `Get-MgRiskyUser -Filter "riskLevel eq 'high'" | Export-Csv risky_users.csv` and `Get-MgRiskDetection -Filter "detectedDateTime ge T_START" | Export-Csv risk_detections.csv`.
  4. 4.Export service principal sign-in logs for app-based access: `Get-MgAuditLogSignIn -Filter "signInEventTypes/any(t: t eq 'servicePrincipal')" -All | Export-Csv sp_signins.csv`.
  5. 5.Check Conditional Access policy evaluation logs to identify bypasses: filter sign-in logs where `ConditionalAccessStatus != "success"` or where legacy authentication protocols were used.

Queries

SigninLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where ResultType == 0 | summarize SignInCount=count(), DistinctIPs=dcount(IPAddress), DistinctApps=dcount(AppDisplayName) by UserPrincipalName, UserType | where DistinctIPs > 3 | order by DistinctIPs desc
AuditLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where OperationName in ("Add member to role","Add owner to application","Add app role assignment to service principal","Consent to application","Add delegated permission grant") | project TimeGenerated, OperationName, InitiatedBy, TargetResources | order by TimeGenerated asc
AADNonInteractiveUserSignInLogs | where TimeGenerated between (datetime(T_START) .. datetime(T_END)) | where ResultType == 0 | summarize TokenCount=count() by UserPrincipalName, AppDisplayName, IPAddress, ResourceDisplayName | order by TokenCount desc | take 50

Notes

  • Azure AD free tier retains sign-in logs for only 7 days. P1 retains 30 days. If you need older logs, check if they were forwarded to a SIEM or Log Analytics workspace.
  • Non-interactive sign-in logs are often overlooked but critical -- they show OAuth token refreshes and app-based access that bypass MFA.