Using make_set() in KQL.
The alert was only one row.
But the investigation had many users, devices, IP addresses and domains.
Agent Foskett did not need every duplicate event.
He needed the unique evidence set.
Lesson overview
Learn how to use make_set() with summarize to create unique lists of investigation values, helping defenders understand scope, affected entities and repeated activity across Microsoft Defender XDR telemetry.
Why make_set() matters
The make_set() investigation workflow
Step 1 — Build a unique process list per device
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
DeviceProcessEvents
| where Timestamp > ago(24h)
| summarize EventCount = count(),
UniqueProcesses = make_set(FileName, 50)
by DeviceName
| order by EventCount descStep 2 — Summarise remote IPs per device
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
DeviceNetworkEvents
| where Timestamp > ago(24h)
| where isnotempty(RemoteIP)
| summarize ConnectionCount = count(),
UniqueRemoteIPs = dcount(RemoteIP),
RemoteIPSet = make_set(RemoteIP, 50)
by DeviceName
| order by UniqueRemoteIPs descStep 3 — Create a URL set for suspicious email senders
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
EmailUrlInfo
| where Timestamp > ago(7d)
| where isnotempty(Url)
| summarize UrlCount = count(),
UniqueUrls = dcount(Url),
UrlSet = make_set(Url, 25)
by SenderFromAddress
| where UniqueUrls > 3
| order by UniqueUrls descStep 4 — Compare make_set() with make_list()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonSuccess"
| summarize UniqueDevices = make_set(DeviceName, 20),
DeviceSequence = make_list(DeviceName, 20),
LogonCount = count()
by AccountName
| order by LogonCount descStep 5 — Build an IOC summary
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
let IOCs = dynamic(["evil.example", "malicious.example", "203.0.113.10"]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl has_any (IOCs) or RemoteIP in (IOCs)
| summarize SeenOnDevices = make_set(DeviceName, 50),
SeenByAccounts = make_set(InitiatingProcessAccountUpn, 50),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp),
EventCount = count()
by RemoteUrl,
RemoteIP
| order by LastSeen descStep 6 — Summarise activity by account
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
DeviceNetworkEvents
| where Timestamp > ago(24h)
| where isnotempty(InitiatingProcessAccountUpn)
| summarize Devices = make_set(DeviceName, 20),
Processes = make_set(InitiatingProcessFileName, 30),
RemoteUrls = make_set(RemoteUrl, 30),
RemoteIPs = make_set(RemoteIP, 30),
EventCount = count()
by InitiatingProcessAccountUpn
| order by EventCount descmake_set() patterns to remember
Real-world investigation
Investigation checklist
Related Agent Foskett Academy lessons
Coming next
Final thought
Using make_set() in KQL
Agent Foskett Academy Lesson 96 teaches defenders how to use make_set() in KQL to build unique lists of users, devices, IP addresses, URLs, domains and indicators during Microsoft Defender XDR investigations.
Learn make_set() for Microsoft Defender XDR
The KQL make_set() aggregation function helps analysts summarise repeated telemetry into unique evidence sets that explain investigation scope across Defender XDR hunting tables.
KQL make_set() tutorial for Microsoft security analysts
This lesson explains how to combine make_set(), summarize, count(), dcount() and dynamic arrays to create clear threat hunting summaries for Microsoft security operations.
