CollectionP2~60 min

Azure AD Sign-In and Audit Log Collection

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
index=o365 sourcetype=o365:management:activity Workload=AzureActiveDirectory Operation="UserLoggedIn" ResultStatus=Success earliest=T_START latest=T_END | stats count dc(ClientIP) as DistinctIPs dc(AppDisplayName) as DistinctApps by UserId | search DistinctIPs>3 | sort -DistinctIPs
index=o365 sourcetype=o365:management:activity Workload=AzureActiveDirectory Operation="Add member to role." OR Operation="Consent to application." OR Operation="Add app role assignment to service principal." earliest=T_START latest=T_END | table _time, UserId, Operation, Target{}.ID, ClientIP | sort _time

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.

Where to Go Next

Related Resources