Detection-as-Code: the pipeline & governance
How analytic rules move from an engineer's idea to a production Sentinel workspace through git, pull requests, and pipelines, with every change visible, reviewed, reversible, and attributable.
develop (auto-deploys to dev Sentinel), then promote to main (deploys to prod). The pipeline calls a deploy engine (ARM/Bicep or Terraform); both go through the Azure Resource Manager API. The payoff is governance: audit trail, rollback, drift correction, and scale.What this practice is called
Managing SOC content (analytic rules, hunting queries, playbooks) through git and pipelines is called Detection-as-Code. It applies DevOps practices to security content. Getting the neighboring terms straight:
| Term | What it actually means |
|---|---|
| DevOps | The practice family: git, branches, PRs, automated pipelines. Detection-as-Code is DevOps applied to detections. |
| DevSecOps | The reverse direction: embedding security checks (SAST, secret scanning) into a software development pipeline. |
| SecOps | Security Operations generally, the SOC function. Not the name of this pipeline pattern. |
| Detection-as-Code | The correct CV/proposal phrase for this whole practice. |
Repo & environment architecture
The instinct to keep a "dev repo" and a "prod repo" is a common anti-pattern: syncing two repos creates drift and splits history. Best practice is one repo, two branches, two Sentinel workspaces: the branch determines the environment, not the repo.
Do you even need a dev workspace?
- Mid/large client or MSSP: yes. It catches broken KQL, over-firing rules, and deployment errors before the SOC queue. It doesn't need full data ingestion; a subset of connectors or replayed sample logs is enough.
- Small client: a single prod workspace is acceptable if the pipeline compensates: mandatory PR review, automated validation, and deploying new rules disabled or in audit mode first.
The UI-first authoring workflow
Detection engineers should not hand-write ARM JSON. The recommended pattern is UI-first (export-driven) authoring: create and test the rule in the dev Sentinel UI, export it as JSON, and commit that JSON to a feature branch. The UI is the authoring tool; git is the system of record.
Why DevOps? PR review is maybe 20% of the value
The peer-review gate is the visible benefit. Here is what the client is really buying:
git log is the answer.The missing layer: the pipeline is not the deployer
A GitHub Action or Azure DevOps pipeline is just an orchestrator: a runner that executes steps when something happens. By itself it doesn't know what an analytic rule is. The pipeline calls a deployment engine (ARM/Bicep or Terraform), and both funnel into one door: the Azure Resource Manager API, through which every Azure change flows.
CI vs CD — two halves of one pipeline
| CI — integration | CD — deployment | |
|---|---|---|
| When | On the pull request, before merge | After the merge |
| Job | Validate, never deploy | Actually push the change |
| For Sentinel | KQL valid? JSON well-formed? MITRE tags, severity, entities present? | Deploy command lands the rule in the workspace |
| Terraform | terraform plan posts the diff onto the PR | terraform apply executes it |
ARM/Bicep vs Terraform — pick one deployment engine
The git lifecycle, properly named
git clone <repo-url> # copy the repo locally (once)
git checkout -b feature/new-rule # create AND switch to a new branch
# ...edit or paste your exported rule JSON into a file...
git status # see what changed
git add . # stage changes (to the staging area)
git commit -m "Add brute-force rule" # snapshot staged changes
git push -u origin feature/new-rule # publish your branch to the remote
# ...then in the web UI: open a Pull Request...The mental map: working directory (your edits) → git add → staging area → git commit → local history → git push → remote. The PR/approval/merge happens in the web UI, and that is what kicks off the pipeline.
GitHub vs Azure DevOps — same concepts, different names
| Concept | GitHub | Azure DevOps |
|---|---|---|
| Where code lives | GitHub repo | Azure Repos |
| The pipeline | GitHub Actions | Azure Pipelines |
| Azure login | Secret / OIDC | Service Connection |
| Merge protection | Branch protection | Branch policies |
| Deploy approval | Environments + reviewers | Environments + checks |
IaC vs API — the solution architect's call
The question is never which service likes the API more. The real axis is state vs action:
- Desired-state configuration: things that should exist and stay a certain way (analytic rules, workbooks, connectors) → declarative IaC. You get idempotency, drift detection, and rollback for free.
- Imperative actions: things you do with no lasting state (bulk-close incidents, upload a watchlist, trigger a hunt) → API scripts. There is nothing to keep in a desired state.
Hands-on lab path — five stages
Each stage adds one real concept, so you are never lost:
Clone the repo and open examples/detection-as-code-cicd — the analytic rule (ARM + Terraform), the validation scripts, and the GitHub Actions pipeline from this article, ready to run in your own lab. Try it now: python3 scripts/lint_rule.py rules/*.json