Query Details
// Hunt : M365 - eDiscovery and Compliance Search Activity History (90d)
// Purpose : Full chronological inventory of all compliance center search and export
// activities over 90 days. Surfaces who performed searches, what content
// was scoped, and especially which searches were followed by an export.
// Start-to-export chains are key evidence in insider threat and compromised
// admin investigations. Also reveals audit log searches that could indicate
// an attacker checking what has been logged about their own activities.
// Tables : OfficeActivity
// Period : P90D
//==========================================================================================
let LookbackDays = 90d;
OfficeActivity
| where TimeGenerated > ago(LookbackDays)
| where RecordType in (
"SecurityComplianceCenter",
"ComplianceManager",
"OfficeSecurityComplianceCenter")
| where Operation in (
"SearchCreated",
"SearchStarted",
"SearchExported",
"SearchPreviewed",
"SearchPurged",
"CaseCreated",
"CaseMemberAdded",
"CaseMemberRemoved",
"HoldCreated",
"HoldUpdated",
"New-ComplianceSearch",
"Start-ComplianceSearch",
"New-ComplianceSearchAction",
"Get-ComplianceSearch",
"New-eDiscoveryCase",
"New-UnifiedAuditLogRetentionPolicy",
"AuditLogSearchStarted",
"AuditLogSearchExported",
"ContentSearchExportDownloaded")
| extend Params = tostring(Parameters)
| extend
SearchName = tostring(extract(@"Name.*?:([^\s,]+)", 1, Params)),
ContentSources = tostring(extract(@"(ExchangeLocation|SharePointLocation|OneDriveLocation).*?:([^\n]+)", 2, Params)),
IsExport = Operation in (
"SearchExported", "New-ComplianceSearchAction",
"ContentSearchExportDownloaded", "AuditLogSearchExported"),
IsAuditLogSearch = Operation in ("AuditLogSearchStarted", "AuditLogSearchExported")
// Build a timeline per user
| summarize
TotalActions = count(),
ExportActions = countif(IsExport),
AuditLogSearches = countif(IsAuditLogSearch),
SearchPurges = countif(Operation == "SearchPurged"),
Operations = make_set(Operation, 15),
SearchNames = make_set(SearchName, 10),
ClientIPs = make_set(ClientIP, 5),
FirstAction = min(TimeGenerated),
LastAction = max(TimeGenerated)
by UserId
| extend RiskIndicator = case(
SearchPurges >= 1 and ExportActions >= 1, "Export + Purge Chain — Critical",
ExportActions >= 3, "Repeated Exports — High",
AuditLogSearches >= 1, "Audit Log Self-Review — Suspicious",
ExportActions >= 1, "Export Performed",
"Informational")
| sort by ExportActions desc, TotalActions desc
| project
UserId,
TotalActions,
ExportActions,
AuditLogSearches,
SearchPurges,
Operations,
SearchNames,
ClientIPs,
RiskIndicator,
FirstAction,
LastAction
This query is designed to analyze and summarize activities related to searches and exports in the Microsoft 365 compliance center over the past 90 days. Here's a simple breakdown of what it does:
Data Source: It examines records from the OfficeActivity table, focusing on activities related to security and compliance.
Time Frame: It looks at activities that occurred within the last 90 days.
Activity Types: The query filters for specific operations, such as creating or starting searches, exporting search results, and managing compliance cases.
Extracted Information: It extracts details like search names and content sources from the activity parameters.
Activity Classification: It identifies whether an activity involved exporting data or searching audit logs.
User Activity Summary: For each user, it summarizes:
Risk Assessment: It assigns a risk indicator to each user based on their activities:
Sorting and Presentation: The results are sorted by the number of export actions and total actions, and then presented with key details for each user.
Overall, this query helps identify potentially risky behavior related to data searches and exports, which can be crucial for insider threat investigations and monitoring compromised admin activities.

David Alonso
Released: March 18, 2026
Tables
Keywords
Operators