Lesson 139 — Building an Identity Compromise Timeline
An identity compromise rarely arrives as one perfect alert.
It arrives as fragments: a failed sign-in, a successful authentication, an unfamiliar IP address, a directory change, cloud activity and perhaps a privileged action several minutes later.
The investigator's job is to stop viewing those fragments independently and determine their order. In this lesson we use KQL to reconstruct the sequence, identify the turning points and build a timeline where every conclusion can be traced back to evidence.

What you will investigate
A suspected identity compromise reconstructed from authentication, audit, identity and cloud evidence.
The investigation begins
Learning objectives
Reconstruct a suspected identity compromise chronologically, identify the earliest relevant authentication activity, add directory and cloud evidence, combine multiple event streams and distinguish observed facts from investigative conclusions.
Why chronology matters
Sequence changes meaning. A privileged role activation followed by an unfamiliar sign-in tells a different story from an unfamiliar sign-in followed minutes later by privilege activation. The events may be identical; their order is not.
Step 1 — establish the authentication sequence
Begin with SigninLogs and include failures as well as successes. The compromise may have started before the first successful authentication.
let TargetUser = "alex.wilson@contoso.com"; SigninLogs | where TimeGenerated > ago(24h) | where UserPrincipalName =~ TargetUser | project EventTime=TimeGenerated, ResultType, ResultDescription, AppDisplayName, IPAddress, Location, ConditionalAccessStatus, AuthenticationRequirement | order by EventTime asc
Do not start at the alert time
An alert is often generated after the important activity has already begun. Search backwards far enough to capture reconnaissance, failed authentication, MFA activity or other events that may explain the later success.
Success is a turning point
Repeated failures followed by a success from the same unusual source can become a critical transition in the timeline. It still requires context, but it gives the investigator a precise point from which to examine subsequent activity.
Step 2 — understand whether the source was new
Look beyond the incident window. Summarize the user's recent source, location and application history so you can decide whether the suspicious event represents a genuine change in behaviour.
let TargetUser = "alex.wilson@contoso.com"; SigninLogs | where TimeGenerated > ago(7d) | where UserPrincipalName =~ TargetUser | summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), EventCount=count() by IPAddress, Location, AppDisplayName | order by FirstSeen asc
First seen does not mean malicious
A new IP address can be caused by travel, mobile networks, home internet, VPN infrastructure or normal address changes. The value of first-seen evidence comes from combining it with timing and subsequent actions.
Baseline before conclusion
The timeline should distinguish between “new to this user” and “malicious.” One is an observation. The other is a conclusion requiring additional evidence.
Step 3 — add directory and identity changes
Search AuditLogs for operations involving the identity as an initiator or target. Authentication compromise often becomes visible through what changes after access is obtained.
let TargetUser = "alex.wilson@contoso.com"; AuditLogs | where TimeGenerated > ago(24h) | where tostring(InitiatedBy) contains TargetUser or tostring(TargetResources) contains TargetUser | project EventTime=TimeGenerated, SourceTable="AuditLogs", EventType=OperationName, Detail=ActivityDisplayName, Result, InitiatedBy, TargetResources | order by EventTime asc
Look for persistence
Authentication-method registration, role changes, application consent, account changes and other directory operations can indicate attempts to maintain access. Their significance increases when they follow suspicious authentication closely.
Actor and target are different questions
An audit event can involve the compromised identity as the actor, the target or both. Preserve InitiatedBy and TargetResources until you understand who performed the action and what was changed.
Step 4 — add post-authentication cloud activity
Now inspect what the account did in connected cloud applications after the suspicious sign-in.
let TargetUser = "alex.wilson@contoso.com"; CloudAppEvents | where Timestamp > ago(24h) | where AccountId =~ TargetUser or AccountDisplayName =~ TargetUser | project EventTime=Timestamp, SourceTable="CloudAppEvents", EventType=ActionType, Detail=Application, IPAddress, CountryCode, City, ObjectName | order by EventTime asc
Actions give authentication meaning
A suspicious sign-in with no meaningful follow-on activity is one investigation. The same sign-in followed by file access, mailbox changes, sharing or administrative actions is another. Post-authentication behaviour helps establish impact.
Keep the objects where possible
Knowing that an action occurred is useful. Knowing which file, mailbox, application or object was touched can make the timeline much more defensible and help determine scope.
Step 5 — assemble the master timeline
Shape the important datasets into common columns and stack them with union. The goal is one ordered sequence rather than four separate query results.
let TargetUser = "alex.wilson@contoso.com"; let SignIns = SigninLogs | where TimeGenerated > ago(24h) | where UserPrincipalName =~ TargetUser | project EventTime=TimeGenerated, SourceTable="SigninLogs", EventType="Sign-in", Detail=AppDisplayName, IPAddress; let IdentityEvents = IdentityLogonEvents | where Timestamp > ago(24h) | where AccountUpn =~ TargetUser | project EventTime=Timestamp, SourceTable="IdentityLogonEvents", EventType=ActionType, Detail=Protocol, IPAddress; let AuditEvents = AuditLogs | where TimeGenerated > ago(24h) | where tostring(InitiatedBy) contains TargetUser or tostring(TargetResources) contains TargetUser | project EventTime=TimeGenerated, SourceTable="AuditLogs", EventType=OperationName, Detail=ActivityDisplayName, IPAddress=""; let CloudEvents = CloudAppEvents | where Timestamp > ago(24h) | where AccountId =~ TargetUser or AccountDisplayName =~ TargetUser | project EventTime=Timestamp, SourceTable="CloudAppEvents", EventType=ActionType, Detail=Application, IPAddress; union isfuzzy=true SignIns, IdentityEvents, AuditEvents, CloudEvents | order by EventTime asc
The timeline is an investigative model
The combined query is not claiming every event is malicious. It creates a working chronology containing the events relevant to the hypothesis. The analyst still has to classify each event.
Use consistent labels
Generic fields such as EventTime, SourceTable, EventType and Detail make the timeline readable while preserving the original source for validation.
Step 6 — identify infrastructure connecting the sequence
Summarize the timeline by IP address to see whether the same infrastructure appears at several stages of the incident.
let TargetUser = "alex.wilson@contoso.com"; let Timeline = union isfuzzy=true ( SigninLogs | where TimeGenerated > ago(24h) | where UserPrincipalName =~ TargetUser | project EventTime=TimeGenerated, SourceTable="SigninLogs", EventType="Sign-in", Detail=AppDisplayName, IPAddress ), ( AuditLogs | where TimeGenerated > ago(24h) | where tostring(InitiatedBy) contains TargetUser or tostring(TargetResources) contains TargetUser | project EventTime=TimeGenerated, SourceTable="AuditLogs", EventType=OperationName, Detail=ActivityDisplayName, IPAddress="" ), ( CloudAppEvents | where Timestamp > ago(24h) | where AccountId =~ TargetUser or AccountDisplayName =~ TargetUser | project EventTime=Timestamp, SourceTable="CloudAppEvents", EventType=ActionType, Detail=Application, IPAddress ); Timeline | summarize EventCount=count(), FirstSeen=min(EventTime), LastSeen=max(EventTime), Sources=make_set(SourceTable, 10), EventTypes=make_set(EventType, 30) by IPAddress | order by FirstSeen asc
Blank IP addresses are still evidence
Some audit events do not expose an IP address in the same way as sign-in or cloud activity. Do not discard them. Their timestamp and identity relationship may still place them precisely in the sequence.
Correlation is cumulative
No single connector needs to carry the whole case. Identity + close timing + matching IP + unusual behaviour + consequential changes can collectively support a much stronger conclusion than any one field alone.
From raw events to investigative phases
| Timeline phase | Typical evidence | Investigation question |
|---|---|---|
| Pre-access | Failures, unfamiliar source, MFA attempts. | Was someone testing or attempting access? |
| Initial access | Successful authentication. | When did access first succeed? |
| Post-authentication | Cloud and application activity. | What did the identity do after access? |
| Privilege | Role activation or privileged operations. | Did the actor increase capability? |
| Persistence | Authentication methods, consent or account changes. | Was access made easier to regain? |
| Impact | Data access, sharing, mailbox or administrative changes. | What was affected? |
Separate fact from interpretation
| Evidence statement | Interpretation |
|---|---|
| 01:53 — successful sign-in from IP X. | The account authenticated successfully from that source. |
| IP X was not seen in the previous seven days. | The source is unusual for the observed baseline. |
| 02:07 — privileged activity occurred. | Privilege-related activity followed the authentication by 14 minutes. |
| 02:16 — authentication method changed. | The sequence may be consistent with persistence, but intent requires corroboration. |
Agent Foskett's investigation
Investigation questions to ask next
- What is the earliest suspicious event in the sequence?
- Were there failed attempts before successful authentication?
- Was the successful source new or unusual for the user?
- What happened in the first five, fifteen and sixty minutes after access?
- Did the identity perform directory or privileged changes?
- Were authentication methods, roles or application permissions modified?
- What cloud objects or data were accessed?
- Which events share infrastructure, identity or close timing?
- Which statements are facts and which are interpretations?
- Can every major conclusion be traced back to the original telemetry?
Lesson 139 key takeaways
- Identity compromise is best understood as a sequence, not a collection of alerts.
- Search before the alert time to find the real beginning.
- Failed authentication can be important pre-access evidence.
- Compare suspicious sources with the user's recent baseline.
- Add audit and cloud activity to establish what happened after authentication.
- Use
unionto assemble multiple event streams chronologically. - Preserve the source table so every event remains traceable.
- Separate observed facts from investigative interpretation.
- Use identity, time, infrastructure and behaviour as cumulative correlation signals.
- A defensible timeline explains not only what happened, but the order in which it happened.
Continue your KQL investigation training
Related Agent Foskett Investigations
🔎 KQL Academy — Module 11: Identity Threat Hunting
Build an identity compromise timeline with KQL
Lesson 139 of the Agent Foskett KQL Academy teaches analysts how to reconstruct a Microsoft identity compromise chronologically using SigninLogs, AuditLogs, IdentityLogonEvents and CloudAppEvents.
Microsoft Entra identity compromise investigation timeline
By combining authentication, directory, identity and cloud evidence, analysts can identify the earliest suspicious event, establish initial access, track post-authentication activity and create a defensible sequence where each conclusion remains traceable to its original telemetry.
