Keep Defender XDR custom detections in Git
A mail-volume query and a review process for changing custom detections without losing the query, settings, or rollback record.
Keep the query and rule settings together in Git. A reviewer needs to see the threshold, schedule, entity mappings, and response actions in the same change.
This example groups inbound Gmail activity by sender. It is a reference query for investigation, with an illustrative threshold. No live tenant execution is recorded for this revision.
Count message-recipient pairs
EmailEvents| where Timestamp > ago(1h)| where EmailDirection == "Inbound"| where SenderFromDomain =~ "gmail.com"| where isnotempty(SenderFromAddress)| where isnotempty(NetworkMessageId) and isnotempty(RecipientEmailAddress)| summarize arg_max(Timestamp, ReportId, Subject, SenderIPv4) by SenderFromAddress, NetworkMessageId, RecipientEmailAddress| summarize MessageRecipientPairs = count(), UniqueMessages = dcount(NetworkMessageId), UniqueRecipients = dcount(RecipientEmailAddress), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Subjects = make_set(Subject, 10), arg_max(Timestamp, ReportId, NetworkMessageId, RecipientEmailAddress, Subject, SenderIPv4) by SenderFromAddress| where MessageRecipientPairs > 25| project Timestamp, ReportId, SenderFromAddress, SenderIPv4, MessageRecipientPairs, UniqueMessages, UniqueRecipients, FirstSeen, LastSeen, NetworkMessageId, RecipientEmailAddress, Subject, SubjectsThe first aggregation keeps one event per sender, message ID, and recipient. The second counts those pairs per sender. One message addressed to 30 recipients counts as 30 pairs. Repeated rows with the same keys count once; a retry with a new message ID counts again.
dcount gives approximate distinct counts. The pair count uses count() after deduplication.
These are observed mail events, including events that may have been blocked. Review delivery actions and locations separately. The sender’s From address is also not proof of the sender’s identity.
Preserve a representative event
The last arg_max retains identifiers from one event in each group. A resulting alert can point to that representative message. It does not map every message in the group.
Keep the query’s time window with the rule configuration. The one-hour window here is an explicit filter. Test scheduling, delayed ingestion, and repeated alerts before using it as a scheduled detection.
Store the rule and its history
A small directory is sufficient:
gmail-volume/ query.kql rule.json README.mdUse rule.json for the reviewed API payload and README.md for the rationale, owner, exclusions, schedule, and validation date. Store exports and query results from deployment in a restricted artifact location; they can contain tenant and email data.
The Microsoft Graph detectionRule endpoint is currently documented under /beta. Microsoft does not support beta APIs for production applications. Check the current API and deployment options before implementing a client.
Review and release
- Run the query in Advanced Hunting with access to
EmailEvents. Inspect the actual matching messages. - Review legitimate bulk senders and document any exclusions.
- Export the existing rule and save the proposed payload.
- Create the new rule disabled, or update the intended rule by its recorded ID. Avoid creating a duplicate on every run.
- Check the schedule, entity mappings, and notification path.
- Enable after review, then inspect alert volume. Keep the previous payload for rollback.
Keep this signal alert-only. Domain and volume are insufficient grounds for deleting messages or blocking senders.
A deployment script should document its dry-run behavior precisely. Authentication and read-only hunting may still run; local artifacts may still be written. Remote create, update, and enable operations must stay behind the script’s mutation controls.