Agent Foskett Academy • Lesson 25 • Using extract() for Flexible Pattern Matching

Using extract() for Flexible Pattern Matching

Sometimes the evidence is easy to see, but difficult to separate.
A URL might be buried inside a command line. An IP address might be hiding inside a longer message. A ticket number might be mixed into an email subject.

In the previous lessons, you learned how to find patterns and extract structured evidence. But not every field follows a simple format.

In this Agent Foskett Academy lesson, you will learn how defenders use the KQL extract() function to pull useful values from flexible patterns using regular expressions.

Agent Foskett Academy lesson explaining how to use extract in KQL investigations
Lesson overview

Learn how extract() helps defenders pull evidence from command lines, URLs, subjects and flexible text patterns.

Understand extract()
Use regex capture groups
Extract cleaner investigation evidence
🎯 extract() gives defenders flexible evidence extraction.
When parse is too strict, extract() can use regex to pull the value you need from a changing pattern.
Review Lesson 23 →

Why extract() matters

The extract() function helps you pull a specific value from a text field using a regular expression capture group.

This is useful when the data has a recognisable pattern, but the surrounding text is not always the same.

Instead of relying on fixed separators, extract() lets you search for the value pattern itself.
Pull values from messy textExtract IP addresses, domains, ticket numbers, filenames, hashes and other indicators from longer fields.
Use regex capture groupsTell KQL exactly which part of the matching pattern should become the new evidence column.
Handle flexible patternsUse extract() when parse is too rigid or when the field changes between events.

Investigation scenario

An analyst is reviewing suspicious activity after a user reported a phishing email.

Some evidence appears in email subjects. Some appears in URLs. Some appears inside command lines. The patterns are similar, but the surrounding text keeps changing.

The analyst uses extract() to create clean investigation columns from those messy fields.

Step 1 — Extract an IP address from a URL or text field

Use extract() when you want to pull out a value that matches a pattern, such as an IPv4 address.
extract-ip-address.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
DeviceNetworkEvents
| where Timestamp > ago(7d)
| extend ExtractedIP = extract(@"(\d{1,3}(?:\.\d{1,3}){3})", 1, RemoteUrl)
| where isnotempty(ExtractedIP)
| project Timestamp, DeviceName, ActionType, ExtractedIP, RemoteUrl
| sort by Timestamp desc

Step 2 — Extract a domain from a clicked URL

URLs can contain schemes, domains, paths, parameters and tracking values. extract() can pull out the part you need.
extract-domain-from-url.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
UrlClickEvents
| where Timestamp > ago(30d)
| extend ClickedDomain = extract(@"https?://([^/]+)", 1, Url)
| project Timestamp, AccountUpn, ClickedDomain, Url, ThreatTypes, ActionType
| sort by Timestamp desc

Step 3 — Extract a ticket number from an email subject

When a value appears inside a subject line, extract() can pull out only the matching part.
extract-ticket-number.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
EmailEvents
| where Timestamp > ago(30d)
| extend TicketNumber = extract(@"Ticket[- ]?(\d+)", 1, Subject)
| where isnotempty(TicketNumber)
| project Timestamp, SenderFromAddress, RecipientEmailAddress, TicketNumber, Subject
| sort by Timestamp desc

What extract() does

The extract() function looks inside a text value, finds a regular expression match, then returns the capture group you choose.

The first number after the regex tells KQL which capture group to return. Most beginner examples use 1, which means return the first captured value.
Regex patternThe search pattern that describes the value you want to find.
Capture groupThe part inside brackets that tells extract() which value to return.
Source fieldThe original field being searched, such as Url, Subject, RemoteUrl or ProcessCommandLine.

Step 4 — Extract a script name from a command line

Command lines often contain a useful file name or script name hidden among switches and arguments.
extract-script-from-commandline.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine has_any (".ps1", ".vbs", ".js")
| extend ScriptName = extract(@"([^\\\s]+\.(?:ps1|vbs|js))", 1, ProcessCommandLine)
| project Timestamp, DeviceName, AccountName, FileName, ScriptName, ProcessCommandLine
| sort by Timestamp desc

Step 5 — Extract a suspicious encoded command

extract() can help pull the value after a known command-line switch.
extract-encoded-command.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine has_any ("-enc", "-EncodedCommand")
| extend EncodedValue = extract(@"(?i)-(?:enc|encodedcommand)\s+([^\s]+)", 1, ProcessCommandLine)
| project Timestamp, DeviceName, AccountName, FileName, EncodedValue, ProcessCommandLine
| sort by Timestamp desc

Step 6 — Use extract() with investigation parameters

You can combine extract() with let statements to keep the query reusable and easier to adjust.
extract-with-parameters.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
let Lookback = 30d;
let UrlPattern = @"https?://([^/]+)";
UrlClickEvents
| where Timestamp > ago(Lookback)
| extend ClickedDomain = extract(UrlPattern, 1, Url)
| where isnotempty(ClickedDomain)
| project Timestamp, AccountUpn, ClickedDomain, Url, ThreatTypes, ActionType
| sort by Timestamp desc

Investigator notes

Use extract() when the value has a pattern, but the surrounding text is too flexible for simple parse logic.

Start with a small result set and validate the extracted column before relying on it in joins, summaries or reports.
Keep patterns focusedA focused regex is easier to test, explain and reuse during an investigation.
Check empty valuesUse isnotempty() to confirm the extraction worked before continuing the investigation.
Use parse when possibleIf the structure is simple and consistent, parse may be easier to read than extract().
🎓 Agent Foskett Academy — Extract the clue
You now understand how to use extract() to pull useful values from flexible Microsoft security telemetry patterns.
Return to Academy

What you learned

In this lesson, you learned how to use the KQL extract() function to pull structured evidence from flexible text patterns.
Using extract()Pull values from URLs, subjects, command lines and other text fields using regex capture groups.
Creating evidence columnsTurn hidden indicators into clear fields that are easier to filter, sort and explain.
Choosing the right toolUse parse for predictable structures and extract() for more flexible patterns.

Continue your investigation

The next step is learning how to use parse_json() when important evidence is stored inside dynamic JSON fields.
Agent Foskett Academy Return to the full Academy learning path and review earlier KQL foundation lessons.
Extracting Evidence with parse Review how defenders extract structured values from predictable strings.

Continue learning with Using matches regex, Using has_any, KQL Threat Hunting Guide and Microsoft Security.

Develop IT. Protect IT. GEMXIT PTY LTD | GEMXIT UK LTD

Using extract() for Flexible Pattern Matching

Agent Foskett Academy Lesson 25 teaches defenders how to use the KQL extract() function to pull useful evidence from flexible text patterns across Microsoft Defender XDR and Microsoft Sentinel telemetry.

Learn KQL extract() for Microsoft Defender XDR and Sentinel

This lesson explains how extract() can support investigations by using regular expressions and capture groups to extract IP addresses, domains, ticket numbers, script names, encoded command values and other security evidence.