Purview DLP Share Point Alert Info
Query
let alert_id = "<<<>>>";
let query_period = 1d;
let _FilePaths =
AlertEvidence
| where Timestamp > ago(query_period)
| where EntityType == "File" and AlertId == strcat("dl", alert_id)
| extend AdditionalFields = todynamic(AdditionalFields)
| distinct SourceFilePath = strcat(AdditionalFields["Directory"], "/", AdditionalFields["Name"]), ObjectName = url_decode(tostring(AdditionalFields["Name"]))
;
CloudAppEvents
| where Timestamp > ago(query_period)
| where ActionType in ("AlertTriggered", "AlertUpdated") and tostring(RawEventData["AlertId"]) == alert_id
| summarize arg_max(Timestamp, *)
| extend
AlertId = tostring(RawEventData["AlertId"]),
Data = todynamic(tostring(RawEventData["Data"]))
| extend
UserPrincipalName = tolower(tostring(Data["f3u"])),
Severity = tostring(Data["sev"]),
Workload = tostring(Data["wl"]),
SensitiveInformationContentType = split(tostring(Data["sict"]), ";"),
SensitiveInformationTypeMatchInfo = split(tostring(Data["sitmi"]), ";"),
PolicyId = tostring(Data["dpid"]),
PolicyName = tostring(Data["dpn"]),
ManagementRuleId = tostring(Data["drid"]),
//RuleId = tostring(Data["drid"]),
RuleName = tostring(Data["drn"]),
ProtectionActions = split(tostring(Data["dact"]), ", "),
ObjectName = tostring(Data["von"]),
DMC = split(tostring(Data["dmc"]), ","),
FileOwnerName = tolower(tostring(Data["flmb"])), // It might not the same as f3u - UserPrincipalName
FileOwnerDisplayName = tostring(Data["fo"]),
USI = tostring(Data["usi"])
| where Workload == "SharePoint" and tostring(RawEventData["Category"]) == "DataLossPrevention"
| lookup kind=leftouter _FilePaths on ObjectName
| project
Timestamp,
Application,
ActionType,
AlertId,
UserPrincipalName,
Severity,
Workload,
SensitiveInformationContentType,
SensitiveInformationTypeMatchInfo,
PolicyId,
PolicyName,
ManagementRuleId,
//RuleId,
RuleName,
DMC,
ProtectionActions,
ObjectName,
SourceFilePath,
FileOwnerName,
FileOwnerDisplayName,
USI,
AlertTriggered_RawEventData = RawEventData,
AlertTriggered_ReportId = ReportId
// | summarize arg_max(CreationTime, *) by AlertId, Timestamp, SourceFilePath, ObjectName, ManagementRuleId
| join hint.strategy=shuffle kind=leftouter (
CloudAppEvents
| where Timestamp > ago(query_period)
| where ActionType in ("DLPRuleMatch", "DLPRuleUndo") and Application == "Microsoft SharePoint Online"
| mv-expand PolicyDetail = iff(array_length(RawEventData["PolicyDetails"]) > 0, RawEventData["PolicyDetails"], dynamic([""]))
| mv-expand Rule = iff(array_length(PolicyDetail["Rules"]) > 0, PolicyDetail["Rules"], dynamic([""]))
| mv-apply SensitiveInformationItem = Rule["ConditionsMatched"]["SensitiveInformation"] on (
summarize SensitiveInformation = make_list(bag_remove_keys(SensitiveInformationItem, dynamic(["SensitiveInformationDetailedClassificationAttributes"])))
)
| project
CreationTime = todatetime(RawEventData["CreationTime"]),
ManagementRuleId = tostring(Rule["ManagementRuleId"]),
ObjectName,
//IncidentId = tostring(RawEventData["IncidentId"]),
SensitiveInformation,
ActionType,
Reason = tostring(RawEventData["ExceptionInfo"]["Reason"]),
//SharePointMetaData = RawEventData["SharePointMetaData"],
SiteCollectionUrl = tostring(RawEventData["SharePointMetaData"]["SiteCollectionUrl"]),
//IsViewableByExternalUsers = tostring(RawEventData["SharePointMetaData"]["IsViewableByExternalUsers"]),
//ItemLastSharedTime = todatetime(RawEventData["SharePointMetaData"]["ItemLastSharedTime"]),
//SharedBy = todatetime(RawEventData["SharePointMetaData"]["SharedBy"]),
DLPRuleMatch_RawEventData = RawEventData,
DLPRuleMatch_ReportId = ReportId
// The same file can be updated, and suffer several "DLPRuleMatch" and "DLPRuleUndo" actions
| summarize
EvaluationEventCount = count(),
arg_min(CreationTime, SensitiveInformation, SiteCollectionUrl, DLPRuleMatch_RawEventData, DLPRuleMatch_ReportId),
(LastEvaluationTime, LastActionType, LastReason, LastSensitiveInformation) = arg_max(CreationTime, ActionType, Reason, SensitiveInformation)
by ObjectName, ManagementRuleId
| extend
LastEvaluationTime = iff(EvaluationEventCount == 1, datetime(null), LastEvaluationTime),
LastActionType = iff(EvaluationEventCount == 1, "", LastActionType),
LastReason = iff(EvaluationEventCount == 1, "", LastReason),
LastSensitiveInformation = iff(EvaluationEventCount == 1, dynamic(null), LastSensitiveInformation)
) on $left.SourceFilePath == $right.ObjectName, ManagementRuleId //, CreationTime does not always match
| project-away ObjectName1, ManagementRuleId1
// Add info about the sharing targets
// Did not have any example to work with, where a DLP alert happened and the file had ActionType in ("AddedToGroup", "AddedToSecureLink", "AddedToSharingLink")
| as hint.materialized=true _Events
| join kind=leftouter (
CloudAppEvents
| where Timestamp > ago(query_period)
| where Application == "Microsoft SharePoint Online" and ActionType in ("AddedToGroup", "AddedToSecureLink", "AddedToSharingLink") // "GroupAdded", "SecureLinkCreated", "SharingLinkCreated", "SharingSet", "SharingInheritanceBroken"
| extend
UserId = tostring(RawEventData["UserId"]),
CreationTime = todatetime(RawEventData["CreationTime"]),
SharingOperation = tostring(RawEventData["Operation"]),
SiteUrl = tostring(RawEventData["SiteUrl"]),
ListId = tostring(RawEventData["ListId"]),
ListItemUniqueId = tostring(RawEventData["ListItemUniqueId"]),
ObjectName = tostring(RawEventData["ObjectId"]),
ItemType = tostring(RawEventData["ItemType"]),
Permission = tostring(RawEventData["Permission"]),
SharingLinkScope = tostring(RawEventData["SharingLinkScope"]),
EventData = tostring(RawEventData["EventData"]),
ModifiedProperties = tostring(RawEventData["ModifiedProperties"]),
TargetUserOrGroupName = tostring(RawEventData["TargetUserOrGroupName"]),
TargetUserOrGroupType = tostring(RawEventData["TargetUserOrGroupType"]),
UniqueSharingId = tostring(RawEventData["UniqueSharingId"]),
CorrelationId = tostring(RawEventData["CorrelationId"]),
SharingEvent_RawEventData = RawEventData,
SharingEvent_ReportId = ReportId
| where tostring(toscalar(_Events | summarize make_set_if(url_decode(SourceFilePath), isnotempty(SourceFilePath)))) has ObjectName
| summarize
CreationTime = min(CreationTime),
TargetUserOrGroupName = array_sort_asc(make_set_if(TargetUserOrGroupName, isnotempty(TargetUserOrGroupName))) // You could exclude targets from the same tenant
by CorrelationId, SiteUrl, UserId, SharingOperation, UniqueSharingId, ObjectName, ItemType, Permission, SharingLinkScope, EventData, TargetUserOrGroupType
| summarize
CreationTime = min(CreationTime),
TargetUserOrGroup = make_bag(bag_pack(TargetUserOrGroupType, TargetUserOrGroupName))
by CorrelationId, SiteUrl, UserId, SharingOperation, UniqueSharingId, ObjectName, ItemType, Permission, SharingLinkScope, EventData
| extend EmptyColumns = array_concat(
iff(isnotempty(Permission), dynamic(null), dynamic(["Permission"])),
iff(isnotempty(SharingLinkScope), dynamic(null), dynamic(["SharingLinkScope"])),
iff(isnotempty(EventData), dynamic(null), dynamic(["EventData"]))
)
| summarize
CreationTime = min(CreationTime),
Operations = make_bag(bag_pack(SharingOperation, bag_remove_keys(bag_pack_columns(Permission, SharingLinkScope, EventData), EmptyColumns))),
take_any(TargetUserOrGroup)
by CorrelationId, SiteUrl, UserId, UniqueSharingId, ObjectName, ItemType, Auxiliar = tostring(TargetUserOrGroup)
| summarize
CreationTime = min(CreationTime),
Objects = make_list(bag_pack_columns(ObjectName, ItemType, Operations)),
take_any(TargetUserOrGroup)
by CorrelationId, SiteUrl, UserId, Auxiliar
| summarize
SharingTime = min(CreationTime),
SharingTargets = make_list(bag_pack_columns(TargetUserOrGroup, Objects))
by CorrelationId, SiteUrl, UserId
| sort by SharingTime asc
| summarize
SharingEvents = make_list(bag_pack_columns(SharingTime, CorrelationId, SharingTargets))
by SiteUrl, UserId
| project
SiteCollectionUrl = SiteUrl,
UserPrincipalName = UserId,
SharingEvents
) on SiteCollectionUrl, UserPrincipalName
| project-away SiteCollectionUrl1, UserPrincipalName1
| project-reorder
Timestamp,
Application,
ActionType,
AlertId,
UserPrincipalName,
Severity,
Workload,
SensitiveInformationContentType,
SensitiveInformationTypeMatchInfo,
PolicyId,
PolicyName,
ManagementRuleId,
//RuleId,
RuleName,
DMC,
ProtectionActions,
ObjectName,
SourceFilePath,
FileOwnerName,
FileOwnerDisplayName,
USI,
AlertTriggered_RawEventData,
AlertTriggered_ReportId,
CreationTime,
SensitiveInformation,
EvaluationEventCount,
LastEvaluationTime,
LastActionType,
LastReason,
LastSensitiveInformation,
SiteCollectionUrl,
DLPRuleMatch_RawEventData,
DLPRuleMatch_ReportId,
SharingEventsExplanation
This KQL (Kusto Query Language) query is designed to analyze and correlate data loss prevention (DLP) alerts and related events in a Microsoft SharePoint Online environment. Here's a simplified breakdown of what the query does:
-
Define Variables:
alert_id: A placeholder for a specific alert ID.query_period: The time range for the query, set to 1 day.
-
Extract File Paths:
- From the
AlertEvidencetable, it retrieves distinct file paths and names related to the specified alert ID within the last day.
- From the
-
Retrieve Alert Events:
- From the
CloudAppEventstable, it fetches events where an alert was triggered or updated, matching the specified alert ID. - It extracts various details such as user information, severity, workload, policy details, and file owner information.
- From the
-
Filter for SharePoint DLP Alerts:
- It filters the events to only include those related to SharePoint and categorized as data loss prevention.
-
Join with File Paths:
- It performs a left outer join with the previously extracted file paths to correlate alert events with specific files.
-
Analyze DLP Rule Matches:
- It joins with additional
CloudAppEventsto analyze rule matches and undos related to DLP policies. - It summarizes the evaluation events, capturing details like the last action type and sensitive information involved.
- It joins with additional
-
Identify Sharing Events:
- It further joins with events related to file sharing actions (e.g., added to a group or secure link) in SharePoint.
- It aggregates sharing events to identify targets and operations related to the files involved in the DLP alerts.
-
Project and Reorder Columns:
- Finally, it selects and orders the columns to present a comprehensive view of the alert, including timestamps, user details, policy information, file paths, sensitive information, and sharing events.
In summary, this query is a complex analysis that correlates DLP alerts with file activities and sharing events in SharePoint, providing insights into potential data loss incidents and how files were shared or accessed.
Details

Jose Sebastián Canós
Released: May 7, 2025
Tables
AlertEvidenceCloudAppEvents
Keywords
AlertEvidenceCloudAppEventsSharePointDataLossPreventionFileUserPrincipalNameSensitiveInformationPolicyManagementRuleIdObjectNameSourceFilePathFileOwnerNameFileOwnerDisplayNameUSISharingEvents
Operators
letagostrcattodynamicdistincturl_decodetostringinsummarizearg_maxextendtolowersplitlookupprojectjoinhint.strategymv-expandiffarray_lengthmv-applymake_listbag_remove_keysarg_mindatetimeonproject-awayasmaterializedhasmake_set_ifisnotemptyarray_sort_ascmake_bagbag_packbag_pack_columnsarray_concattake_anysortascproject-reorder