Advanced KQL: Advanced Time Series Analysis.
The event looked harmless on its own.
One failed sign-in. One PowerShell command. One outbound connection. One file copied to cloud storage.
But when Agent Foskett placed the events on a timeline, the pattern changed completely.
The compromise was not hiding in a single row. It was hiding in the rhythm of the activity over time.
Lesson overview
Learn how to analyse Microsoft Defender XDR telemetry over time using bin(), summarize, make-series, anomaly detection and timecharts.
Why time series analysis matters
The time series mindset
Step 1 — Build simple time buckets with bin()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
let TimeFrame = 7d;
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| summarize Events = count()
by TimeBucket = bin(Timestamp, 1h)
| order by TimeBucket ascStep 2 — Trend failed sign-ins over time
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
let TimeFrame = 14d;
IdentityLogonEvents
| where Timestamp > ago(TimeFrame)
| where ActionType =~ "LogonFailed"
| summarize FailedLogons = count(),
Users = dcount(AccountUpn),
IPAddresses = dcount(IPAddress)
by TimeBucket = bin(Timestamp, 1h)
| order by TimeBucket ascStep 3 — Compare users across time
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
let TimeFrame = 7d;
IdentityLogonEvents
| where Timestamp > ago(TimeFrame)
| where ActionType =~ "LogonFailed"
| summarize FailedLogons = count()
by AccountUpn,
TimeBucket = bin(Timestamp, 1h)
| where FailedLogons > 5
| order by TimeBucket asc, FailedLogons descStep 4 — Visualise endpoint activity with render timechart
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
let TimeFrame = 3d;
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("powershell.exe", "cmd.exe", "wscript.exe", "cscript.exe", "mshta.exe")
| summarize Events = count()
by TimeBucket = bin(Timestamp, 30m),
FileName
| render timechartStep 5 — Use make-series for regular time series output
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
let StartTime = ago(7d);
let EndTime = now();
IdentityLogonEvents
| where Timestamp between (StartTime .. EndTime)
| where ActionType =~ "LogonFailed"
| make-series FailedLogons = count()
default = 0
on Timestamp
from StartTime to EndTime step 1hStep 6 — Detect anomalies with series_decompose_anomalies()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
let StartTime = ago(14d);
let EndTime = now();
IdentityLogonEvents
| where Timestamp between (StartTime .. EndTime)
| where ActionType =~ "LogonFailed"
| make-series FailedLogons = count()
default = 0
on Timestamp
from StartTime to EndTime step 1h
| extend Anomalies = series_decompose_anomalies(FailedLogons)
| render anomalychartStep 7 — Hunt regular beaconing behaviour
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
let TimeFrame = 24h;
DeviceNetworkEvents
| where Timestamp > ago(TimeFrame)
| where isnotempty(RemoteUrl) or isnotempty(RemoteIP)
| summarize Connections = count(),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp)
by DeviceName,
RemoteUrl,
RemoteIP,
TimeBucket = bin(Timestamp, 10m)
| where Connections > 2
| order by DeviceName asc, RemoteUrl asc, TimeBucket ascStep 8 — Identify data movement over time
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
let TimeFrame = 7d;
CloudAppEvents
| where Timestamp > ago(TimeFrame)
| where ActionType has_any ("FileDownloaded", "FileUploaded", "FileAccessed", "FileShared")
| summarize CloudActions = count(),
Objects = dcount(ObjectName),
Applications = make_set(Application, 10)
by AccountDisplayName,
TimeBucket = bin(Timestamp, 1h)
| where CloudActions > 20
| order by TimeBucket asc, CloudActions descStep 9 — Build a ransomware activity timeline
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
let TimeFrame = 48h;
let SuspiciousProcesses =
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName has_any ("powershell", "cmd", "vssadmin", "wbadmin", "bcdedit")
| project Timestamp, DeviceName, Source = "Process", Detail = FileName;
let FileActivity =
DeviceFileEvents
| where Timestamp > ago(TimeFrame)
| where ActionType has_any ("FileCreated", "FileModified", "FileRenamed", "FileDeleted")
| project Timestamp, DeviceName, Source = "File", Detail = ActionType;
union SuspiciousProcesses, FileActivity
| summarize Events = count()
by DeviceName,
Source,
TimeBucket = bin(Timestamp, 15m)
| order by TimeBucket ascStep 10 — Compare current activity with a baseline
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
let BaselineStart = ago(14d);
let BaselineEnd = ago(2d);
let CurrentStart = ago(2d);
let Baseline =
DeviceProcessEvents
| where Timestamp between (BaselineStart .. BaselineEnd)
| where FileName in~ ("powershell.exe", "cmd.exe", "mshta.exe")
| summarize BaselineEvents = count() by DeviceName, FileName;
let Current =
DeviceProcessEvents
| where Timestamp > CurrentStart
| where FileName in~ ("powershell.exe", "cmd.exe", "mshta.exe")
| summarize CurrentEvents = count() by DeviceName, FileName;
Current
| join kind=leftouter Baseline on DeviceName, FileName
| extend BaselineEvents = coalesce(BaselineEvents, 0)
| extend Increase = CurrentEvents - BaselineEvents
| where Increase > 20
| order by Increase descTime series investigation patterns
Common time series mistakes
Investigation checklist
Related Agent Foskett Academy lessons
Coming next
Final thought
Advanced KQL Advanced Time Series Analysis in Microsoft Defender XDR
Agent Foskett Academy Lesson 128 teaches defenders how to use advanced time series analysis in KQL for Microsoft Defender XDR investigations, including bin(), summarize, make-series, anomaly detection and timechart visualisation.
Learn KQL time series analysis for Microsoft security investigations
Time series analysis helps security analysts detect spikes, trends, anomalies, regular intervals, beaconing behaviour, failed sign-in bursts, ransomware timelines and data exfiltration patterns across Defender XDR telemetry.
Microsoft Defender XDR KQL timechart and anomaly detection tutorial
This lesson explains how to group events into time buckets, compare current activity with baselines, visualise process and identity behaviour, and investigate suspicious activity over time using Microsoft Defender XDR advanced hunting.
