Extracting Evidence with parse
Sometimes the evidence is right there in the logs.
The problem is that it is buried inside a longer string.
Like a clue hiding in a sock drawer full of command lines, URLs and suspicious-looking nonsense.
A command line might contain a downloaded URL. A file path might contain the username. An email subject might contain a ticket number. A device event might contain structured details that are useful, but not yet separated into clean columns.
In this Agent Foskett Academy lesson, you will learn how defenders use the KQL parse operator to extract structured evidence from messy Microsoft Defender XDR and Microsoft Sentinel telemetry.
Lesson overview
Learn how parse helps defenders turn messy strings into clean investigation columns.
Why parse matters
This is useful when important evidence is trapped inside a command line, URL, folder path, subject line or message string.
Instead of copying values manually, parse lets the query create new columns for the parts of the string that matter.
Investigation scenario
The command line contains a URL, a downloaded script name and several arguments. The evidence is visible, but it is all sitting inside one long command-line field.
The analyst uses parse to extract the important values into separate columns so the investigation becomes easier to read.
Step 1 — Extract a URL from a command line
- 1
- 2
- 3
- 4
- 5
- 6
- 7
DeviceProcessEvents | where Timestamp > ago(7d) | where ProcessCommandLine has "http" | parse ProcessCommandLine with * "http" ExtractedUrl " " * | project Timestamp, DeviceName, AccountName, FileName, ExtractedUrl, ProcessCommandLine | sort by Timestamp desc
Step 2 — Extract a username from a folder path
- 1
- 2
- 3
- 4
- 5
- 6
- 7
DeviceFileEvents | where Timestamp > ago(14d) | where FolderPath has @"C:\Users" | parse FolderPath with @"C:\Users" LocalUser @"" * | project Timestamp, DeviceName, LocalUser, FileName, FolderPath, ActionType | sort by Timestamp desc
Step 3 — Extract a domain from an email address
- 1
- 2
- 3
- 4
- 5
- 6
EmailEvents | where Timestamp > ago(30d) | parse SenderFromAddress with SenderName "@" SenderDomain | project Timestamp, SenderFromAddress, SenderDomain, RecipientEmailAddress, Subject, DeliveryAction | sort by Timestamp desc
What parse does
The fixed text parts act like anchors. The names you provide become new columns.
In simple terms, you are telling KQL: find this structure, then place the important pieces into these new fields.
Step 4 — Extract a ticket number from an email subject
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
EmailEvents | where Timestamp > ago(30d) | where Subject startswith "Ticket " | parse Subject with "Ticket " TicketNumber ":" TicketSummary | project Timestamp, SenderFromAddress, RecipientEmailAddress, TicketNumber, TicketSummary, Subject | sort by Timestamp desc
Step 5 — Extract command arguments
- 1
- 2
- 3
- 4
- 5
- 6
- 7
DeviceProcessEvents | where Timestamp > ago(7d) | where ProcessCommandLine has_any ("-enc", "-EncodedCommand", "-File") | parse ProcessCommandLine with Command " " FirstArgument " " RemainingArguments | project Timestamp, DeviceName, AccountName, Command, FirstArgument, RemainingArguments, ProcessCommandLine | sort by Timestamp desc
Step 6 — Use parse with parameters
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
let Lookback = 7d; let SuspiciousTerm = "http"; DeviceProcessEvents | where Timestamp > ago(Lookback) | where ProcessCommandLine has SuspiciousTerm | parse ProcessCommandLine with * "http" ExtractedUrl " " * | project Timestamp, DeviceName, AccountName, ExtractedUrl, ProcessCommandLine | sort by Timestamp desc
Investigator notes
If the structure changes too much, parse may miss results. In that case, start broader with contains, has_any or matches regex, then parse the results once you understand the pattern.
What you learned
Continue your investigation
Continue learning with Using startswith and endswith, Using has_any, KQL Threat Hunting Guide and Microsoft Security.
Develop IT. Protect IT. GEMXIT PTY LTD | GEMXIT UK LTD