Agent Foskett Academy • Lesson 27 • Advanced Multi-Value Investigations with mv-apply

Advanced Multi-Value Investigations with mv-apply

Some evidence does not arrive as one simple value in one simple column.
It might be stored inside an array, a dynamic field, a list of indicators, a collection of URLs, a set of entities or a nested telemetry object.

This is where mv-apply becomes useful. Instead of only expanding values into separate rows, defenders can apply filtering, projection and summarisation logic to each value while keeping the original investigation context.

In this Agent Foskett Academy lesson, you will learn how defenders use the KQL mv-apply operator to inspect arrays, nested values and multi-value telemetry inside Microsoft Defender XDR and Microsoft Sentinel investigations.

Agent Foskett Academy lesson explaining how to use mv-apply in KQL investigations
Lesson overview

Learn how mv-apply helps defenders inspect multi-value fields while preserving the surrounding investigation evidence.

Understand mv-apply
Inspect arrays and dynamic fields
Keep investigation context
🎯 mv-apply helps when evidence contains many values.
Use it when you need to inspect, filter or summarise items inside an array without losing the original event context.
Review Lesson 26 →

Why mv-apply matters

The mv-apply operator applies a subquery to each record that contains a multi-value expression.

This is useful when the field contains multiple items and the analyst wants to process those items carefully, instead of simply expanding everything and creating unnecessary noise.

Defenders can use mv-apply to inspect arrays, filter nested values, project important fields and summarise suspicious evidence while keeping the original event details available.
Inspect multi-value evidenceWork with arrays, lists and dynamic fields that contain more than one value inside a single event.
Keep event contextReview each item while still keeping the sender, user, device, timestamp or original event details.
Reduce noisy expansionApply filtering logic inside the mv-apply block so only useful evidence is returned.

Investigation scenario

Agent Foskett is investigating a suspicious email and endpoint activity chain.

Some events contain multiple URLs, several extracted indicators and nested JSON values. A simple expansion may show too many rows too quickly.

The investigation needs a controlled way to look inside each multi-value field, filter what matters and keep the original user, device, sender and timestamp evidence attached to the result.

Step 1 — Use mv-apply with a dynamic list

Start with a small example to see how mv-apply can inspect multiple indicators while keeping the original alert context.
mv-apply-dynamic-indicators.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
SecurityAlert
| where TimeGenerated > ago(7d)
| extend Indicators = dynamic(["invoice-update.com", "login-check.net", "185.10.20.30"])
| mv-apply Indicator = Indicators on (
    where Indicator contains "."
    | project Indicator
)
| project TimeGenerated, AlertName, Severity, Indicator

Step 2 — Filter each value inside the mv-apply block

The power of mv-apply is that you can filter inside the block before the final results are returned.
mv-apply-filter-suspicious-domains.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
EmailEvents
| where Timestamp > ago(30d)
| extend CandidateDomains = dynamic(["contoso.com", "secure-login-check.com", "invoice-update.net"])
| mv-apply Domain = CandidateDomains on (
    where Domain has_any ("login", "invoice", "secure")
    | project SuspiciousDomain = Domain
)
| project Timestamp, SenderFromAddress, RecipientEmailAddress, Subject, SuspiciousDomain

Step 3 — Use mv-apply with parse_json()

mv-apply often becomes useful after parse_json(), especially when a dynamic field contains nested arrays.
mv-apply-with-parse-json.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
SecurityAlert
| where TimeGenerated > ago(7d)
| extend EntitiesJson = parse_json(Entities)
| mv-apply Entity = EntitiesJson on (
    where tostring(Entity.Type) in ("account", "host", "ip")
    | project EntityType = tostring(Entity.Type), EntityValue = tostring(Entity.Name)
)
| project TimeGenerated, AlertName, Severity, EntityType, EntityValue

What mv-apply does

The mv-apply operator takes a multi-value field, applies a small query to each value and returns the result with the surrounding row context.

This makes it especially useful when investigating dynamic telemetry, alert entities, arrays and lists that need more control than a simple mv-expand.
Multi-value sourceThe array, list or dynamic field that contains several values inside one event.
Subquery blockThe logic inside the brackets where you filter, project or summarise each value.
Returned evidenceThe clean investigation output that keeps the useful value and the original event context.

mv-expand vs mv-apply

mv-expand is excellent when you simply want to break a multi-value field into separate rows.

mv-apply is better when you want to do more work on each value while it is being expanded, such as filtering, projecting or summarising inside a controlled subquery.
Use mv-expand whenYou want a straightforward row for each item in an array or list.
Use mv-apply whenYou want to process each item with additional KQL logic before showing the result.
Investigation benefitYou can keep the output focused and avoid flooding your results with unnecessary expanded rows.

Step 4 — Summarise suspicious values after mv-apply

After extracting useful values, summarise them to find repeat indicators, targeted users or repeated suspicious patterns.
summarise-after-mv-apply.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
EmailEvents
| where Timestamp > ago(30d)
| extend CandidateWords = dynamic(["invoice", "password", "verify", "payment"])
| mv-apply Word = CandidateWords on (
    where Subject contains Word
    | project MatchedWord = Word
)
| summarize EmailCount = count(), Recipients = dcount(RecipientEmailAddress) by tostring(MatchedWord)
| sort by EmailCount desc

Common investigation uses

Alert entitiesInspect accounts, devices, IP addresses and URLs stored inside Microsoft security alert entity fields.
Email investigationsReview multiple domains, indicators, words or recipients connected to phishing activity.
Endpoint huntingAnalyse lists of commands, file paths, indicators or extracted values from endpoint telemetry.

Common mistakes

Using mv-apply too earlyFilter your main dataset first, then apply mv-apply to a smaller and more relevant investigation set.
Forgetting tostring()Dynamic values often need tostring(), tolower() or another conversion before clean filtering and projection.
Creating too many rowsIf the array is large, keep the subquery focused so the output remains useful for investigation.

What you learned

mv-apply processes arraysYou learned how mv-apply can inspect each item inside a multi-value field.
Context mattersYou learned how to keep original investigation details attached to the values being inspected.
Better control than simple expansionYou learned when mv-apply gives defenders more control than a basic mv-expand workflow.
Next lesson: Creating New Evidence Fields with extend
Now that you can inspect complex multi-value telemetry, the next step is learning how to create cleaner evidence fields with extend.
Back to Academy →

Related Agent Foskett Academy lessons

Expanding Multi-Value Data with mv-expand Review how mv-expand breaks arrays and lists into separate investigation rows.
Working with Dynamic Data using parse_json() Review how defenders access nested values inside dynamic Microsoft security telemetry.

Continue learning with Extracting Evidence with extract(), KQL Threat Hunting Guide and Microsoft Security.

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

Advanced Multi-Value Investigations with mv-apply

Agent Foskett Academy Lesson 27 teaches defenders how to use the KQL mv-apply operator to inspect arrays, dynamic values and multi-value Microsoft security telemetry while preserving investigation context.

Learn KQL mv-apply for Microsoft Defender XDR and Sentinel

This lesson explains how mv-apply can support Microsoft Defender XDR and Microsoft Sentinel investigations by applying filtering and projection logic to individual values inside arrays and dynamic fields.