The Conditional Access Result Said Success — But That Wasn't the Whole Story
The sign-in was successful.
Conditional Access also reported Success.
The obvious conclusion was that the security controls had worked and there was nothing more to investigate.
Agent Foskett looked at the same event and asked a different question.
What, exactly, had succeeded?

Conditional Access Investigation
The result was accurate. The interpretation was incomplete.
The word “Success” ended the investigation too early
The first clue was not the result — it was the timeline
- 1
- 2
- 3
- 4
- 5
- 6
- 7
01:52 Successful sign-in 01:52 Conditional Access = Success 01:52 MFA requirement satisfied 02:03 New authentication method registered 02:31 Privileged role activated 02:36 Administrative changes begin
Start with the sign-in event
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
SigninLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName =~ "alex.morgan@contoso.com"
| project TimeGenerated,
UserPrincipalName,
AppDisplayName,
IPAddress,
ConditionalAccessStatus,
AuthenticationRequirement,
AuthenticationDetails,
DeviceDetail,
LocationDetails,
AppliedConditionalAccessPolicies
| order by TimeGenerated asc
Inspect the policies that were evaluated
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
SigninLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName =~ "alex.morgan@contoso.com"
| extend Policies = todynamic(AppliedConditionalAccessPolicies)
| mv-expand Policy = Policies
| project TimeGenerated,
AppDisplayName,
ConditionalAccessStatus,
PolicyName = tostring(Policy.displayName),
PolicyResult = tostring(Policy.result)
| order by TimeGenerated asc
Now inspect how authentication was satisfied
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
SigninLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName =~ "alex.morgan@contoso.com"
| project TimeGenerated,
AuthenticationRequirement,
AuthenticationDetails,
AuthenticationProcessingDetails,
IPAddress,
AppDisplayName,
ConditionalAccessStatus
| order by TimeGenerated asc
The device context deserved its own question
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
SigninLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName =~ "alex.morgan@contoso.com"
| extend Device = todynamic(DeviceDetail)
| project TimeGenerated,
IPAddress,
DeviceId = tostring(Device.deviceId),
OperatingSystem = tostring(Device.operatingSystem),
Browser = tostring(Device.browser),
IsCompliant = tostring(Device.isCompliant),
IsManaged = tostring(Device.isManaged),
TrustType = tostring(Device.trustType),
ConditionalAccessStatus
Compare the event with the user's normal sign-in pattern
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
SigninLogs
| where TimeGenerated > ago(30d)
| where UserPrincipalName =~ "alex.morgan@contoso.com"
| summarize SignIns = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by IPAddress, AppDisplayName, ConditionalAccessStatus
| order by SignIns desc

