TH2.8 Dynamic Column Parsing for M365 Logs
The data is there. It is just nested.
The most valuable fields in M365 logs are the most deeply buried. The country of a sign-in is inside LocationDetails.countryOrRegion. The browser is inside DeviceDetail.browser. The ATT&CK techniques mapped to an alert are inside parse_json(ExtendedProperties).["Techniques"]. The application permissions consented in an audit event are inside TargetResources[0].modifiedProperties.
Every campaign module extracts from these nested structures. This subsection provides the extraction patterns you will use repeatedly.
Pattern 1: Simple dot-notation access
| |
Pattern 2: Array access for TargetResources
| |
Pattern 3: Extracting from modifiedProperties
The modifiedProperties field in AuditLogs is an array of objects, each with displayName, oldValue, and newValue. This is where consent permissions, role assignments, and policy changes are recorded.
| |
Pattern 4: Parsing CloudAppEvents RawEventData
| |
Pattern 5: bag_unpack for exploration
When you do not know the structure of a dynamic column, bag_unpack expands it into individual columns:
| |
Figure TH2.8 — Five dynamic column parsing patterns. Use 1–2 for routine extraction, 3–4 for complex nested data, 5 for exploration of unfamiliar structures.
Try it yourself
Exercise: Extract consent permissions from your AuditLogs
Run Pattern 3 against your AuditLogs — extract the actual permission strings from consent events in the last 90 days. Which permissions appear most frequently? Do any events grant Mail.ReadWrite or Files.ReadWrite.All to non-admin users?
Then use bag_unpack (Pattern 5) on a CloudAppEvents RawEventData field for a "New-InboxRule" event. Examine the structure. Which parameters record the rule name, conditions, and actions?
The myth: The nested JSON in dynamic columns is unstructured and unreliable. Hunting should use only the top-level typed columns.
The reality: The top-level columns (TimeGenerated, UserPrincipalName, IPAddress, ResultType) answer “what happened.” The dynamic columns (LocationDetails, DeviceDetail, RawEventData, TargetResources) answer “how, where, and with what context.” Hunting requires both. The permission scope of a consented application is only in TargetResources.modifiedProperties. The inbox rule conditions are only in RawEventData. The country of a sign-in is only in LocationDetails. Ignoring dynamic columns means ignoring the contextual data that makes the difference between an indicator and a finding.
Extend this approach
When a dynamic column's structure varies by event type (common with RawEventData in CloudAppEvents), use the exploration pattern first: `| where ActionType == "YourEvent" | take 5 | project RawEventData`. Examine the raw JSON. Then write the targeted extraction query. Do not assume the structure matches a different ActionType's format — CloudAppEvents uses different RawEventData schemas for different operations. TH5 (inbox rules) and TH6 (OAuth) each document the specific extraction patterns for their relevant ActionTypes.
References Used in This Subsection
- Microsoft. “KQL Dynamic Data Type.” Microsoft Learn. https://learn.microsoft.com/en-us/kusto/query/scalar-data-types/dynamic
- Microsoft. “KQL mv-expand Operator.” Microsoft Learn. https://learn.microsoft.com/en-us/kusto/query/mv-expand-operator
You're reading the free modules of this course
The full course continues with advanced topics, production detection rules, worked investigation scenarios, and deployable artifacts. Premium subscribers get access to all courses.