Forum Discussion
Custom Detection Rules as Code in Sentinel Repositories: What Your Pipeline Owns Now
While going through the June Sentinel updates I almost scrolled past this one, and I think that would have been a mistake: custom detection rules can now be managed as code in Sentinel Repositories, the same way analytics rules, playbooks, parsers and workbooks already are. You connect a GitHub or Azure DevOps repo, enable the Custom Detection Rules content type, and rules are synced on every commit. There is also a standalone path via the Bicep CLI for teams running their own pipelines.
The feature is in preview per the Learn documentation, and in my view it matters more than the low-key rollout suggests. Microsoft has been positioning custom detections as the unified experience for building rules over both Defender XDR and Sentinel data since late 2025. If custom detections are becoming the primary detection type, then this preview is the moment your primary detection type becomes pipeline-managed. I spent some time in the documentation to understand what that actually means, and there is one implication I have not seen anyone talk about yet.
How it works
Custom detection rules use a different mechanism than every other content type in Repositories. Analytics rules deploy as Microsoft.OperationalInsights/workspaces/providers/alertRules resources, with the Microsoft.SecurityInsights provider sitting in the resource name. Custom detection rules instead use a dedicated Bicep extension. You declare it in a `bicepconfig.json` at the repo root:
{ "extensions": { "MicrosoftSecurity": "br:mcr.microsoft.com/bicep/extensions/microsoftsecurity:v1.0.1" } }
The rule itself is a `Microsoft.Security/detectionRules` resource. This is the structure from the Microsoft documentation:
extension MicrosoftSecurity
resource detectionRule 'Microsoft.Security/detectionRules@2026-06-01-preview' = {
id: 'custom-rule-id'
displayName: 'Custom Rule Display Name'
status: 'enabled'
queryCondition: {
queryText: 'DeviceProcessEvents | take 10 | project DeviceId, Timestamp, FileName'
}
schedule: {
frequency: 'PT1H'
}
detectionAction: {
alertTemplate: {
title: '<ruleTitle>'
description: 'Custom detection rule'
severity: 'medium'
tactics: [
{
tactic: 'Execution'
techniques: [
{
technique: 'T1059'
}
]
}
]
entityMappings: {
hosts: [
{
id: 'h'
deviceIdColumn: 'DeviceId'
}
]
}
}
}
}Rules are uniquely identified by the `id` property, which you provide in the template. Deployment is either the automatic Repositories sync or a plain `az deployment group create` against a resource group. That last part is what I like most about the design: any CI/CD system that can run Azure CLI can ship these rules.
Prerequisites beyond the standard Repositories setup: a Microsoft 365 E5 license or equivalent that includes Defender XDR, and a Sentinel workspace onboarded to the Defender portal. Two preview limitations are documented: custom frequency for Sentinel-only data is not supported yet, and neither are custom details.
The part that made me stop reading and think
Repositories are designed as the single source of truth. The documentation is explicit that content in your repo overwrites changes made through the portal. That is the whole point of the feature, and for analytics rules it has been mostly harmless. For custom detections I see a wrinkle.
When Microsoft renames tables or columns in the advanced hunting schema, those naming changes are applied automatically to queries saved in Microsoft Defender, including the queries inside custom detection rules. The docs are equally explicit that this automatic migration does not cover queries run via API or saved anywhere outside Defender. A Git repo is outside Defender.
Play that forward with a current example. The `AIAgentsInfo` table stopped being accessible on July 1, 2026, replaced by the unified `AgentsInfo` table with a changed column set. A portal-managed custom detection referencing the old table got migrated automatically. The same rule managed as code did not, because the authoritative copy of the query now lives in your repo, and nothing in the sync path rewrites your Bicep files. Your repo is now the thing standing between Microsoft's server-side fix and your production detection. Either the sync starts failing, or the stale query gets reasserted over the migrated rule. The documentation does not say which of the two happens, and honestly, neither is good. No alert fires for either.
And if smart deployments, which skip files that have not changed since the last deployment, apply to this content type the same way they do to the rest of Repositories, it gets slightly worse in a way I find almost funny: a stale rule would sit untouched until someone happens to edit it.
What I would put in front of the merge
To be clear, none of this is an argument against the feature. I want detections in Git, and I suspect most people reading this do too. It is an argument that moving custom detections into a repo moves the schema lifecycle responsibility into your review process, because the portal safety net explicitly does not reach into source control. Concretely, a PR touching detection content should be checked for references to deprecated or transitioning advanced hunting tables, for the result columns the custom detection docs recommend (`Timestamp` or `TimeGenerated`, plus `DeviceId` or `DeviceName` for Defender for Endpoint tables, plus `Timestamp` and `ReportId` from the same event for the other Defender tables), and for complete entity mappings, since entities drive how alerts group into incidents. One more detail from the custom detection docs that I suspect will trip up people coming from analytics rules, because it goes against years of muscle memory: avoid filtering on `Timestamp` or `TimeGenerated` in the query itself. The service prefilters data based on the detection lookback using ingestion time. The scheduled-analytics-rule reflex of always pinning a time window works against you here.
Whether you enforce these checks with a homegrown script or a linting step in the pipeline matters less than doing it before merge rather than discovering it in the alert queue. The deployment mechanics are now solved. The content governance is yours. Full transparency: I have worked through the documentation and the sample content, but I have not yet run a retired-table scenario through the sync myself. So if you are testing the preview, I would genuinely like to hear how it behaves in your environment when a repo-managed rule references a table like `AIAgentsInfo`. That failure mode is the one I want to understand before this reaches GA.
Beyond that specific case, I am curious where you all stand: are you moving custom detections into Git now, or waiting for GA? And if you already run detections as code for analytics rules, what checks have earned a permanent place in your PR pipeline?
My used references:
- Manage content as code with Microsoft Sentinel repositories: https://learn.microsoft.com/en-us/azure/sentinel/ci-cd-custom-content
- Advanced hunting schema naming changes: https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-schema-changes
- Create custom detection rules in Microsoft Defender XDR: https://learn.microsoft.com/en-us/defender-xdr/custom-detection-rules
- Custom detections as the unified detection experience: https://techcommunity.microsoft.com/t5/microsoft-defender-threat-protection/custom-detections-are-now-the-unified-experience-for-creating/ba-p/4463875
2 Replies
- Marcel_GraewerBrass Contributor
Thanks, Jamony. There are two points in your reply I want to pick up on.
I did not give the rule ID issue enough weight in the post. Since the id property uniquely identifies the rule, changing it in a PR would presumably create a new rule rather than update the existing one, potentially leaving the old rule behind. That creates a quiet duplication risk, and a simple CI check that blocks unexpected ID changes would catch it cheaply. Good catch.
On validation against a non-production workspace, I agree that live validation provides the strongest signal, but it has a blind spot worth calling out. It only validates against the tables and columns available in that environment, and the schema can drift from production in either direction. A connector that is missing in dev can make a valid production query fail, while a table or column already migrated in dev but not yet in production can allow a query that would fail in production to pass.
That is why I would pair live validation with a static check against a maintained deprecation list. The static check is environment-independent and can catch known issues such as the AIAgentsInfo retirement regardless of what a particular workspace currently exposes. I am sketching something along those lines at the moment.
Out of curiosity, how would you implement the live validation technically? Would you call the hunting query API with | take 0 appended, or use another approach? And if you get to the retired-table test before I do, I would genuinely like to see the result here. :)
Hi Marcel, this is an excellent catch. Moving detections into Git changes more than the deployment method; it transfers responsibility for schema changes and query health to the pipeline owner. I would add a CI stage that extracts and validates the KQL against a non-production workspace, checks for deprecated tables and columns, confirms the required result columns and entity mappings, and prevents accidental changes to rule IDs. A post-deployment health test or small canary detection would also help identify a rule that deployed successfully but no longer returns valid results. I would be very interested to see whether the retired-table test causes the repository sync to fail or whether it reasserts the stale query over the portal-migrated version.