Skip to content
downpipes docs

Forward the downpipes audit trail to Microsoft Sentinel

Microsoft Sentinel collects the downpipes audit trail by pulling it, not by receiving a push. Sentinel’s Logs Ingestion API wants Entra OAuth2 and a Data Collection Rule rather than a static auth header, so a header-based push cannot reach it. Instead you deploy the downpipes Codeless connector, an ARM template shipped in the engine repo under integrations/microsoft-sentinel/, into your own Azure workspace. It polls the engine’s audit feed at /support/audit-feed with a bearer token and pages forward using the feed’s nextAfterSeq cursor, landing events in a custom DownpipesAudit_CL table. No CEF is involved.

Because Sentinel’s poller dials in to your engine, a Cloudflare Access perimeter on your console hostname does block it until you add an exemption for the feed path. For Sentinel specifically, that exemption must be a narrow path Bypass, not an Access service token, because Sentinel’s RestApiPoller connector has one credential slot, already spent on the feed bearer, and the service token’s two extra headers would have to sit in a part of the connector definition Azure does not redact on read. This is the opposite of the push connectors, where the engine dials out and Access never sees the request. For the cursor, chain-continuity checks and the full Access explanation, see wiring the audit feed into your SIEM.

What you need

  • An Azure Log Analytics workspace with Microsoft Sentinel already enabled on it.
  • The Azure CLI, logged in, with the target subscription selected.
  • Enough RBAC on the resource group to create a Data Collection Endpoint, a Data Collection Rule, a custom table and the Sentinel connector resources.
  • An owner-minted audit-feed bearer credential from the downpipes console.

Set it up

  1. In the downpipes console, mint the feed credential: open Integrations, choose the Microsoft Sentinel tile, and mint the audit-feed credential there (owner-only). Copy the bearer value shown once; the engine keeps only a hash of it.
  2. Get the connector package from integrations/microsoft-sentinel/ in the engine repo: the ARM template mainTemplate.json, and two example parameters files, parameters.example.json (the bearer token as a plain inline value) and parameters.keyvault.example.json (the token pulled live from an Azure Key Vault secret).
  3. From that directory, deploy with the Azure CLI. Put the bearer token in one of the example parameters files rather than on the command line, because a token passed as an argument lands in your shell history and in the process list where anyone on the machine can read it:
az deployment group create \
  --resource-group <rg> \
  --template-file mainTemplate.json \
  --parameters @parameters.example.json

Swap in parameters.keyvault.example.json instead if you are pulling the token from Key Vault.

For a one-off test you can pass the three values inline instead, accepting that the token is then in your history and worth re-minting afterwards:

az deployment group create \
  --resource-group <your-resource-group> \
  --template-file mainTemplate.json \
  --parameters workspaceName="<your-sentinel-workspace>" \
  --parameters apiHost="<engine-hostname-no-scheme>" \
  --parameters bearerToken="<paste-the-minted-bearer-token>"
  1. If your engine hostname is fronted by Cloudflare Access, add a Bypass exemption scoped to /support/audit-feed, which is the only path the poller reads. Do not use an Access service token for this connector: Sentinel’s RestApiPoller has one credential slot, already spent on the feed bearer, and Azure does not redact the extra headers a service token needs the way it redacts that slot, so the token would be readable in clear text by anyone with read access to the workspace’s data connectors. Do not bypass /support/* either: that prefix also covers /support/diagnostics, the sealed support-bundle pull, and an exemption written that wide opens it too. Note where the credential mint is not, because it is the thing people reach to protect: minting a new pull bearer is POST /admin/support/credentials, which sits behind /admin and behind step-up, so a /support/* rule never touches it and whatever rule covers /admin governs it. The connector README explains the Bypass-only requirement and why the service token form does not apply here; the feed page carries the same warning for every SIEM this feed serves.
  2. Give the connector a few minutes for its first poll, then confirm events are landing (below).

What lands in Sentinel

Events land in a custom Log Analytics table, DownpipesAudit_CL, with the audit fields as columns (seq, ts mapped to TimeGenerated, actorEmail, actorSubject, actorMethod, sourceIp, action, outcome, target, prevHash, hash, advisory). Confirm with a query:

DownpipesAudit_CL
| take 10

You should see rows with the audit fields and a populated TimeGenerated. Back in the console, the SIEM audit feed card’s pull trail records each poll, so you can see Sentinel reaching the engine.

Good to know

  • This is pull, so Cloudflare Access matters. Unlike the push connectors, Sentinel dials in to your engine; if Access fronts the hostname, add a path Bypass for /support/audit-feed alone, or the poll is turned away before the bearer is even checked. Do not use an Access service token for this connector: see step 4 above for why.
  • You choose the bearer’s term when you mint it. Set Expires after (days, optional) to a whole number of days from 1 to 365. If you leave it blank, the bearer expires after 90 days. Re-minting replaces it at once and breaks the connector until you redeploy or patch it with the new value, so treat re-minting as a planned change. The feed carries operator identity (member emails, source IPs, roles and approver emails); no backup data and no secret values. For the cursor and chain-continuity mechanics in full, see wiring the audit feed into your SIEM.
  • A structurally malformed record is accepted, not rejected. Azure Monitor’s Data Collection Rule does not reject a row whose seq is not a number or whose ts is not a date; it accepts it (HTTP 204), coerces seq to null and falls back TimeGenerated to the ingestion time. Nothing surfaces this on the Sentinel or downpipes side: the connector health tile, the engine log and the downpipes console all show nothing wrong. The real engine never emits a malformed record, so this only matters if something else holding a valid bearer posts one.
  • A malformed record still throws off the parser and the silence rule. The DownpipesAuditFeed parser’s arg_max(TimeGenerated, *) by seq dedup groups every malformed row under the same null seq, so only the most recent of them survives. The audit-feed-silent analytic rule fires on max(TimeGenerated) going stale, and the ingestion-time fallback reads as a fresh event to it, so it will not fire even if the real engine has actually gone silent.

Last updated .