Module 4: Entra ID Sign-In Log Analysis
Your first real investigation skill — reading sign-in logs to identify compromised accounts, risky sign-ins, and conditional access failures.
What You'll Learn
- Read and interpret Entra ID sign-in log fields
- Identify impossible travel, token replay, and MFA anomalies
- Write KQL queries against SigninLogs and AADNonInteractiveUserSignInLogs
- Distinguish between interactive and non-interactive sign-in patterns
SC-200 Exam Objectives Covered
- Investigate Entra ID sign-in logs
- Analyze identity-related threats
Sign-in logs are the foundation of identity-based investigation in Microsoft 365 environments. Every authentication attempt — successful or failed, interactive or background, from a browser or a mobile app — generates a record in the Entra ID sign-in logs. When an account is compromised, the sign-in logs are where you find the first evidence: an anomalous IP address, a new device, a failed MFA challenge followed by a successful bypass, or a token refresh from a country the user has never visited.
This module teaches you to read these logs fluently. By the end, you will be able to look at a sign-in event and immediately assess whether it is normal, suspicious, or definitively malicious — and write the KQL to surface similar events across your entire tenant.
Two Tables, Not One
Microsoft Sentinel stores Entra ID sign-in data in two separate tables. This is the first thing new analysts get wrong — they query one table and miss half the picture.
SigninLogs — Interactive sign-ins. These are events where the user directly provides an authentication factor: typing a password, completing an MFA prompt, tapping a FIDO2 key, or approving a push notification. When you sign in to Outlook via a browser and enter your credentials, that produces a record in SigninLogs.
AADNonInteractiveUserSignInLogs — Non-interactive sign-ins. These are events where a client application or operating system component authenticates on behalf of the user without requiring a new authentication factor. Token refreshes, SSO cookie replays, and background app re-authentication all land here. When Outlook silently refreshes your access token every hour, that produces a record in AADNonInteractiveUserSignInLogs.
(Source: Connect Azure Active Directory data to Microsoft Sentinel — Microsoft Learn)
Why this matters for investigations: An attacker who steals a session token (as in an AiTM phishing attack) does not need to interactively sign in again. Their activity appears exclusively in the non-interactive table as token refreshes. If you only query SigninLogs, you will see the initial compromise but miss the ongoing access. Always query both tables.
There are two additional sign-in log tables that you may encounter:
AADServicePrincipalSignInLogs— Sign-ins by application service principals (no human user involved)AADManagedIdentitySignInLogs— Sign-ins by Azure managed identities
These are relevant for investigating compromised applications and automation accounts, but for most SOC work, the two user-facing tables are your primary focus.
Querying Both Tables Together
The two tables share most column names but differ in data types for certain fields — notably DeviceDetail is stored as a dynamic type in SigninLogs but as a string in AADNonInteractiveUserSignInLogs. The simplest approach is to use union with isfuzzy=true:
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(24h)
| where UserPrincipalName == "j.smith@northgateeng.com"
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, ResultType, ResultDescription, IsInteractive
| sort by TimeGenerated desc
The isfuzzy=true flag tells KQL to ignore schema mismatches between the two tables. The IsInteractive column (boolean) tells you which table each row came from.
(Source: Parsing Interactive and Non-Interactive Sign-In Logs — isroot.nl)
Key Fields in the Sign-In Log
Each sign-in record contains dozens of columns. These are the ones that matter for security investigations:
Identity Fields
| Field | What It Contains | Investigation Use |
|---|---|---|
UserPrincipalName | The user’s email-format login (e.g., j.smith@northgateeng.com) | Primary identifier for user-focused queries |
UserId | The user’s Entra ID object GUID | Stable identifier — UPNs can change, GUIDs do not |
UserDisplayName | The user’s display name | Useful for reports, not for filtering |
AppDisplayName | The application being accessed (e.g., “Exchange Online”, “Microsoft Teams”) | Identifies which service was targeted |
AppId | The application’s GUID | Stable application identifier |
ResourceDisplayName | The resource being accessed | Often the same as AppDisplayName, but not always |
Authentication Result
| Field | What It Contains | Investigation Use |
|---|---|---|
ResultType | Numeric error code (0 = success) | The single most important field for triage |
ResultDescription | Human-readable description of the result | Explains what ResultType means |
ConditionalAccessStatus | “success”, “failure”, or “notApplied” | Shows whether Conditional Access policies blocked or allowed the sign-in |
ResultType is a numeric code. 0 means the sign-in succeeded. Any non-zero value indicates a failure or interruption. The most important error codes for security investigations:
| Code | Meaning | Security Relevance |
|---|---|---|
0 | Success | The user (or attacker) authenticated successfully |
50126 | Invalid username or password | Wrong credentials — could be brute force |
50053 | Account locked | Too many failed attempts triggered lockout |
50057 | Account disabled | Attempted sign-in to a disabled account |
50074 | Strong authentication required (MFA challenge issued) | User was prompted for MFA — check if they completed it |
50076 | MFA required by policy | Conditional Access required MFA |
53003 | Blocked by Conditional Access | CA policy explicitly denied access |
530032 | Blocked — security default requires MFA registration | User has not registered for MFA |
70043 | Token expired (non-interactive) | Normal token lifecycle — not a failure |
700016 | Application not found in target tenant | Potential misconfiguration or phishing app |
(Source: Microsoft Entra authentication and authorization error codes — Microsoft Learn)
A critical nuance: ResultType 70043 in non-interactive logs generates enormous volume and is normal operational behaviour — it represents expired tokens being refreshed. New analysts often treat this as suspicious. It is not. Do not build alert rules on this code without understanding the context.
Location and Network
| Field | What It Contains | Investigation Use |
|---|---|---|
IPAddress | Source IP of the sign-in attempt | Correlate with known VPN ranges, threat intel, or geolocation |
Location | Country/region string | Quick geographic check |
LocationDetails | JSON object with city, state, countryOrRegion, geoCoordinates | Detailed location for impossible travel analysis |
AutonomousSystemNumber | The ASN of the source network | Identifies the ISP or hosting provider — cloud hosting ASNs are suspicious for user sign-ins |
Device and Client
| Field | What It Contains | Investigation Use |
|---|---|---|
DeviceDetail | JSON: deviceId, operatingSystem, browser, trustType, isCompliant | Identifies the machine and whether it is managed |
ClientAppUsed | The client protocol (e.g., “Browser”, “Mobile Apps and Desktop clients”, “Exchange ActiveSync”) | Legacy auth protocols are higher risk |
UserAgent | Full user agent string | Detect automation tools, unusual browsers, or spoofed agents |
Risk Assessment
| Field | What It Contains | Investigation Use |
|---|---|---|
RiskLevelDuringSignIn | Entra ID Protection’s real-time risk: none, low, medium, high | Indicates whether Entra flagged this sign-in as risky |
RiskLevelAggregated | Cumulative user risk level | The user’s overall risk posture, not just this sign-in |
RiskDetail | Reason for the risk assessment | Explains why risk was elevated |
RiskEventTypes_V2 | Specific risk detections triggered | Lists the exact risk detections (e.g., “anonymizedIPAddress”, “impossibleTravel”) |
Conditional Access
| Field | What It Contains | Investigation Use |
|---|---|---|
ConditionalAccessPolicies | JSON array of every CA policy evaluated | Shows which policies applied, which granted, and which blocked |
ConditionalAccessStatus | Overall CA result | Quick check: was access conditional, granted, or blocked? |
AuthenticationRequirement | “singleFactorAuthentication” or “multiFactorAuthentication” | Shows whether MFA was required for this sign-in |
(Source: Microsoft Entra activity logs schema — Microsoft Learn)
Investigation Patterns
Pattern 1: Account Compromise Triage
When an account is reported as potentially compromised, this is the first query to run:
let TargetUser = "j.smith@northgateeng.com";
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName =~ TargetUser
| summarize
TotalSignIns = count(),
SuccessCount = countif(ResultType == 0),
FailCount = countif(ResultType != 0),
DistinctIPs = dcount(IPAddress),
DistinctCountries = dcount(tostring(LocationDetails.countryOrRegion)),
DistinctApps = dcount(AppDisplayName),
InteractiveCount = countif(IsInteractive == true),
NonInteractiveCount = countif(IsInteractive == false)
This gives you the shape of the user’s activity in one pass: how many sign-ins, how many succeeded, how many unique locations and IPs, and the split between interactive and non-interactive. A compromised account often shows a sudden spike in distinct IPs and countries compared to the user’s baseline.
Follow up with a detailed timeline:
let TargetUser = "j.smith@northgateeng.com";
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(48h)
| where UserPrincipalName =~ TargetUser
| where ResultType == 0
| extend Country = tostring(LocationDetails.countryOrRegion)
| extend City = tostring(LocationDetails.city)
| extend OS = tostring(DeviceDetail.operatingSystem)
| extend Browser = tostring(DeviceDetail.browser)
| project TimeGenerated, IsInteractive, AppDisplayName, IPAddress, Country, City, OS, Browser, ConditionalAccessStatus, AuthenticationRequirement
| sort by TimeGenerated desc
Scan the output for: IP addresses you do not recognise, countries outside the user’s normal pattern, unusual applications (especially “Azure Portal” or “Microsoft Graph” if the user is not an IT administrator), and sign-ins where MFA was not required.
Pattern 2: Failed Sign-In Analysis (Brute Force / Password Spray)
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType in (50126, 50053, 50057)
| summarize
FailedAttempts = count(),
DistinctUsers = dcount(UserPrincipalName),
DistinctApps = dcount(AppDisplayName),
Users = make_set(UserPrincipalName, 20)
by IPAddress, bin(TimeGenerated, 1h)
| where FailedAttempts > 10
| sort by FailedAttempts desc
This groups failures by source IP per hour. The pattern tells you the attack type:
- One IP, many users, same error → Password spray. The attacker tries one common password against many accounts.
- One IP, one user, many errors → Brute force. The attacker is hammering one account.
- Many IPs, one user, same error → Distributed brute force. Multiple IPs targeting a single account — harder to detect by IP alone.
- Many IPs, many users → Large-scale credential stuffing using leaked credential databases.
Pattern 3: Impossible Travel Detection
Entra ID Protection has built-in impossible travel detection, but understanding the logic lets you write custom hunting queries and validate alerts:
let TargetUser = "j.smith@northgateeng.com";
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| where UserPrincipalName =~ TargetUser
| extend Country = tostring(LocationDetails.countryOrRegion)
| extend Latitude = todouble(LocationDetails.geoCoordinates.latitude)
| extend Longitude = todouble(LocationDetails.geoCoordinates.longitude)
| sort by TimeGenerated asc
| extend PrevTime = prev(TimeGenerated)
| extend PrevLat = prev(Latitude)
| extend PrevLon = prev(Longitude)
| extend PrevCountry = prev(Country)
| extend TimeDeltaMinutes = datetime_diff('minute', TimeGenerated, PrevTime)
| where Country != PrevCountry and TimeDeltaMinutes < 120
| project TimeGenerated, Country, City = tostring(LocationDetails.city), IPAddress, TimeDeltaMinutes, PrevCountry
This finds cases where the same user signed in from two different countries within two hours. True impossible travel — for example, London to Moscow in 45 minutes — is a strong indicator of compromise. However, VPN usage causes false positives: a user might be physically in London but their VPN exit node is in Frankfurt. Correlate with your organisation’s known VPN exit IPs before escalating.
Pattern 4: Token Replay / Session Theft Detection
AiTM phishing campaigns steal session tokens rather than passwords. The stolen token is replayed from the attacker’s infrastructure. Detecting this requires looking for sign-ins where:
- The sign-in succeeded (ResultType == 0)
- MFA was not challenged (because the stolen token bypasses MFA)
- The IP or ASN does not match the user’s normal pattern
- The sign-in appears in the non-interactive table (token refresh)
let SuspiciousASNs = dynamic([
"AS14061", "AS16509", "AS13335", "AS20473", "AS63949"
]); // Common hosting providers: DigitalOcean, AWS, Cloudflare, Vultr, Linode
AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0
| where AutonomousSystemNumber in (SuspiciousASNs)
| extend Country = tostring(LocationDetails.countryOrRegion)
| summarize
TokenRefreshes = count(),
DistinctApps = make_set(AppDisplayName),
Countries = make_set(Country)
by UserPrincipalName, IPAddress, AutonomousSystemNumber
| where TokenRefreshes > 5
| sort by TokenRefreshes desc
Legitimate users rarely sign in from cloud hosting providers. If a non-interactive token refresh comes from DigitalOcean or AWS, and the user is not a developer running cloud-based tools, this warrants immediate investigation.
Pattern 5: Conditional Access Bypass Monitoring
Conditional Access policies are your enforcement layer. Monitoring for sign-ins that were not evaluated by CA — or where CA was applied but resulted in “notApplied” — identifies gaps in your policy coverage:
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| where ConditionalAccessStatus == "notApplied"
| summarize
Count = count(),
Users = dcount(UserPrincipalName),
Apps = make_set(AppDisplayName)
by AuthenticationRequirement
| sort by Count desc
Successful sign-ins where no Conditional Access policy was applied represent your policy blind spots. If AuthenticationRequirement shows “singleFactorAuthentication” on these, users are accessing resources with only a password — no MFA, no device compliance check, no location restriction.
Pattern 6: Legacy Authentication Detection
Legacy authentication protocols (SMTP, IMAP, POP3, Exchange ActiveSync with basic auth) do not support MFA. Attackers target these protocols specifically because they bypass your Conditional Access MFA requirements:
SigninLogs
| where TimeGenerated > ago(7d)
| where ClientAppUsed in ("Exchange ActiveSync", "IMAP4", "POP3", "SMTP", "Authenticated SMTP", "Other clients")
| where ResultType == 0
| summarize
Count = count(),
Users = make_set(UserPrincipalName, 20)
by ClientAppUsed
| sort by Count desc
Any successful sign-in via a legacy protocol should be investigated. Microsoft has been deprecating basic authentication, but some tenants still have it enabled for specific mailboxes or applications. If you find active legacy auth in your environment, the remediation is to block these protocols via Conditional Access.
Reading a Sign-In Event: A Worked Example
When you open a single sign-in event — whether in the Entra admin center or by querying the log — read it in this order:
- ResultType: Did it succeed (0) or fail? If it failed, the error code tells you why.
- IPAddress and Location: Is this IP known? Is the country expected for this user?
- AppDisplayName: What were they accessing? “Exchange Online” is normal for most users. “Azure Portal” or “Microsoft Graph Explorer” might not be.
- DeviceDetail: Is this a managed device (trustType = “AzureAd” or “Hybrid Azure AD Joined”)? Or is it an unmanaged personal device?
- ConditionalAccessStatus: Was CA applied? Did it grant or block? If “notApplied”, why not?
- AuthenticationRequirement: Was MFA required? If single-factor only, is that expected for this user and application?
- RiskLevelDuringSignIn: Did Entra ID Protection flag this? If risk is “medium” or “high”, what risk detection triggered it?
- IsInteractive: Was this a direct user sign-in, or a background token refresh? Non-interactive successes from unexpected IPs are a hallmark of token theft.
This eight-step checklist applies to every sign-in event you investigate, whether during routine monitoring or active incident response.
Volume Management
Non-interactive sign-in logs generate significantly more volume than interactive logs — often 10x to 50x more. Token refreshes for Exchange Online, Teams, SharePoint, and OneDrive occur continuously throughout the day for every active user. This has cost implications for Sentinel ingestion.
The ConditionalAccessPolicies field in non-interactive logs is the largest contributor to log size, often representing 50–80% of each record’s byte size. Many organisations use Data Collection Rules (DCRs) to strip this field from non-interactive logs before ingestion, reducing costs without losing investigative value. The conditional access outcome for non-interactive sign-ins is almost always identical to the outcome of the preceding interactive sign-in.
(Source: Save money on your Sentinel ingestion costs with Data Collection Rules — Microsoft Community Hub)
If your organisation has not yet addressed non-interactive log volume, raise it. It is one of the most impactful cost optimisations available in Sentinel.
SC-200 Exam Relevance
Two SC-200 objectives map directly to this module:
Investigate Entra ID sign-in logs: The exam expects you to interpret sign-in log fields, identify the meaning of common ResultType codes, and understand the difference between interactive and non-interactive sign-ins. Scenario-based questions may show a sign-in log entry and ask you to determine whether the sign-in is suspicious.
Analyze identity-related threats: Questions will test your ability to recognise patterns like impossible travel, password spray (many users failing from one IP), and token replay (non-interactive successes from cloud hosting IPs). Know which fields to check and in what order.
(Source: SC-200 study guide — Microsoft Learn)
Key Takeaways
- Always query both tables.
SigninLogsfor interactive,AADNonInteractiveUserSignInLogsfor background token activity. Useunion isfuzzy=trueto combine them. - ResultType 0 is success. Everything else is a failure or interruption. Learn the critical error codes: 50126 (bad password), 50053 (lockout), 53003 (CA block).
- Check the ASN, not just the IP. An IP from DigitalOcean or AWS authenticating as a regular user is more suspicious than an IP from a known UK ISP.
- Non-interactive sign-ins from unexpected locations are a token theft indicator. This is how AiTM-stolen tokens manifest — not as new interactive logins, but as quiet token refreshes.
- ConditionalAccessStatus “notApplied” is a gap, not a pass. It means no policy evaluated the sign-in. That is a security hole, not a clean bill of health.
- Legacy auth bypasses MFA. If you find successful IMAP, POP3, or SMTP sign-ins, your MFA posture has a hole that attackers will find.
References
- Microsoft Entra authentication and authorization error codes — learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes
- Microsoft Entra activity logs schema — learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-activity-log-schemas
- Non-interactive sign-in logs — learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-noninteractive-sign-ins
- Connect Azure Active Directory data to Sentinel — learn.microsoft.com/en-us/azure/sentinel/connect-azure-active-directory
- Non-interactive logins: minimizing the blind spot — techcommunity.microsoft.com/blog/microsoftsentinelblog/non-interactive-logins-minimizing-the-blind-spot/2287932
- Save money on Sentinel ingestion with DCRs — techcommunity.microsoft.com/blog/microsoftsentinelblog/save-money-on-your-sentinel-ingestion-costs-with-data-collection-rules/4270256
- Troubleshoot sign-in errors — learn.microsoft.com/en-us/entra/identity/monitoring-health/howto-troubleshoot-sign-in-errors
- SC-200 study guide (January 2026 update) — learn.microsoft.com/en-us/credentials/certifications/resources/study-guides/sc-200
This concludes the free modules. You now have the foundations: the M365 security ecosystem (Module 1), KQL query skills (Module 2), portal navigation and incident management (Module 3), and identity investigation through sign-in logs (Module 4).
Ready for the full SC-200 track? Subscribe → to unlock Modules 5–28, including Sentinel workspace configuration, Defender for Endpoint investigations, the AiTM phishing deep-dive, threat hunting with advanced KQL, and monthly scenario challenges.