Detection rules › Kusto
Power Platform - DLP policy updated or removed
Identifies changes to DLP policy, specifically policies which are updated or removed.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Stealth | T1480 Execution Guardrails |
Rule body kusto
id: 1b2e6172-85c5-417a-90c3-7cc80cb787f5
kind: Scheduled
name: Power Platform - DLP policy updated or removed
description: Identifies changes to DLP policy, specifically policies which are updated
or removed.
severity: Low
status: Available
requiredDataConnectors:
- connectorId: PowerPlatformAdmin
dataTypes:
- PowerPlatformAdminActivity
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1480
query: |
let create_policy_ignore_time_window = 10m;
let query_frequency = 1h;
let dlp_policy_events = PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_frequency)
| where EventOriginalType == "GovernanceApiPolicyOperation"
| where PropertyCollection has_any ("DeleteDlpPolicy", "UpdateDlpPolicy", "CreateDlpPolicy")
| mv-expand PropertyCollection
| extend
Name = tostring(PropertyCollection.Name),
Value = tostring(PropertyCollection.Value)
| summarize Properties = make_bag(bag_pack(Name, Value))
by
TimeGenerated,
EventOriginalUid
| extend
PolicyName = tostring(Properties['powerplatform.analytics.resource.display_name']),
EventType = tostring(Properties['powerplatform.analytics.resource.tenant.governance.api_policy.operation_name']),
ActorName = tostring(Properties['enduser.principal_name']),
PolicyId = tostring(Properties['powerplatform.analytics.resource.id']),
AdditionalInfo = Properties['powerplatform.analytics.resource.tenant.governance.api_policy.additional_resources'];
let delete_events = dlp_policy_events
| where EventType == "DeleteDlpPolicy";
let update_events = dlp_policy_events
| where EventType == "UpdateDlpPolicy";
let create_events = dlp_policy_events
| where EventType == "CreateDlpPolicy"
| extend ignore_time = TimeGenerated + create_policy_ignore_time_window;
union
delete_events,
(update_events
| join kind=leftouter (
create_events
| project-away TimeGenerated
)
on PolicyId
| where isempty(ignore_time) or TimeGenerated > ignore_time
| project-away ignore_time)
| where TimeGenerated >= ago(query_frequency)
| extend
AccountName = tostring(split(ActorName, "@")[0]),
UPNSuffix = tostring(split(ActorName, "@")[1])
| project
TimeGenerated,
ActorName,
EventType,
PolicyName,
PolicyId,
AccountName,
UPNSuffix,
AdditionalInfo
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: UPNSuffix
alertDetailsOverride:
alertDisplayNameFormat: PowerPlatform - DLP policy {{EventType}} event detected.
alertDescriptionFormat: A DLP policy {{PolicyName}} was as modfiied or deleted.
Event type {{EventType}}
customDetails:
Policy: PolicyId
PolicyName: PolicyName
version: 3.2.0
Stages and Predicates
Parameters
let create_policy_ignore_time_window = 10m;
let query_frequency = 1h;
Let binding: dlp_policy_events
let dlp_policy_events = PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_frequency)
| where EventOriginalType == "GovernanceApiPolicyOperation"
| where PropertyCollection has_any ("DeleteDlpPolicy", "UpdateDlpPolicy", "CreateDlpPolicy")
| mv-expand PropertyCollection
| extend
Name = tostring(PropertyCollection.Name),
Value = tostring(PropertyCollection.Value)
| summarize Properties = make_bag(bag_pack(Name, Value))
by
TimeGenerated,
EventOriginalUid
| extend
PolicyName = tostring(Properties['powerplatform.analytics.resource.display_name']),
EventType = tostring(Properties['powerplatform.analytics.resource.tenant.governance.api_policy.operation_name']),
ActorName = tostring(Properties['enduser.principal_name']),
PolicyId = tostring(Properties['powerplatform.analytics.resource.id']),
AdditionalInfo = Properties['powerplatform.analytics.resource.tenant.governance.api_policy.additional_resources'];
Derived from query_frequency.
Let binding: delete_events
let delete_events = dlp_policy_events
| where EventType == "DeleteDlpPolicy";
Derived from dlp_policy_events.
Let binding: update_events
let update_events = dlp_policy_events
| where EventType == "UpdateDlpPolicy";
Derived from dlp_policy_events.
Let binding: create_events
let create_events = dlp_policy_events
| where EventType == "CreateDlpPolicy"
| extend ignore_time = TimeGenerated + create_policy_ignore_time_window;
Derived from create_policy_ignore_time_window, dlp_policy_events.
Stage 1: union
union of 2 branches
Stage 2: source
PowerPlatformAdminActivity
Stage 3: where
where ...
Stage 4: where
where EventOriginalType =~ "GovernanceApiPolicyOperation"
Stage 5: where
where (PropertyCollection contains "DeleteDlpPolicy" or PropertyCollection contains "UpdateDlpPolicy" or PropertyCollection contains "CreateDlpPolicy")
Stage 6: mv-expand
mv-expand PropertyCollection
Stage 7: extend
extend Name, Value
Stage 8: summarize
summarize Properties by TimeGenerated, EventOriginalUid
Stage 9: extend
extend ActorName, AdditionalInfo, EventType, PolicyId, PolicyName
Stage 10: where
where EventType =~ "DeleteDlpPolicy"
Stage 11: source
PowerPlatformAdminActivity
Stage 12: where
where ...
Stage 13: where
where EventOriginalType =~ "GovernanceApiPolicyOperation"
Stage 14: where
where (PropertyCollection contains "DeleteDlpPolicy" or PropertyCollection contains "UpdateDlpPolicy" or PropertyCollection contains "CreateDlpPolicy")
Stage 15: mv-expand
mv-expand PropertyCollection
Stage 16: extend
extend Name, Value
Stage 17: summarize
summarize Properties by TimeGenerated, EventOriginalUid
Stage 18: extend
extend ActorName, AdditionalInfo, EventType, PolicyId, PolicyName
Stage 19: where
where EventType =~ "UpdateDlpPolicy"
Stage 20: join
join kind=leftouter (...)
Stage 21: where
where (TimeGenerated > "ignore_time" or isempty(ignore_time))
Stage 22: project-away
project-away ignore_time
Stage 23: where
where ...
Stage 24: extend
extend AccountName, UPNSuffix
Stage 25: project
project AccountName, ActorName, AdditionalInfo, EventType, PolicyId, PolicyName, TimeGenerated, UPNSuffix
Stage 26: summarize
summarize by TimeGenerated, EventOriginalUid
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
EventOriginalType | eq |
|
EventType | eq |
|
PropertyCollection | match |
|
TimeGenerated | gt |
|
ignore_time | is_null |
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
EventOriginalUid | summarize |
TimeGenerated | summarize |