Runbooks / Siem Sentinel

Microsoft Sentinel direct ingestion

Operator runbook for shipping IdentityMesh log events directly to a Log Analytics workspace — the data plane Microsoft Sentinel queries via KQL. This sidesteps the Azure Monitor Agent (AMA) + Data Collection Rule (DCR) hop described in siem-integration.md for deployments that want fewer moving parts on the host.

When to choose direct ingestion

Pick direct ingestion when:

Pick AMA + DCR (the siem-integration.md recipe) instead when:

Pick the syslog forwarder (siem-syslog.md) into a Linux Sentinel forwarder when neither outbound HTTPS nor AMA is permitted off the IdentityMesh host.

Two API paths — modern vs legacy

Microsoft has two ingestion APIs for Log Analytics workspaces:

  1. Logs Ingestion API (modern, current). Authenticated via Microsoft Entra (managed identity / service principal), schema-bound to a DCR + custom table, supports stream-based transformations. The Microsoft-recommended path.
  2. HTTP Data Collector API (legacy). Authenticated via the workspace shared key, posts to <workspaceId>.ods.opinsights.azure.com, creates a *_CL custom table on first write. Deprecated by Microsoft in 2024 with a transition window through 14 September 2026, after which new posts will be rejected.

The Serilog sink ecosystem at the time of writing ships Serilog.Sinks.AzureAnalytics (mihaichivu), which targets the legacy HTTP Data Collector API. The example config below uses that path because it works today and lets a customer get running with three config values. The runbook flags the deprecation timeline so operators can plan the migration.

A future revision of this sample will switch to the Logs Ingestion API once a stable Serilog sink is published. In the meantime, customers who must use the modern API today should:

The remainder of this document assumes the legacy HTTP Data Collector path unless explicitly noted.

Workspace setup (legacy HTTP Data Collector)

You need three values to configure the sink. All three come from the workspace’s “Agents” page in the Azure portal:

ValueWhere to find it
Workspace IDAzure portal -> Log Analytics workspace -> Agents -> Workspace ID. A GUID.
Primary keySame page, “Primary key”. Base64-encoded HMAC secret.
Custom log nameOperator-chosen. Convention: IdentityMeshLogs -> Sentinel auto-suffixes _CL.

The first POST creates the IdentityMeshLogs_CL table in the workspace and provisions the columns from the JSON shape — no upfront schema management required. Indexing kicks in within a few minutes.

Sample config

The Sync Engine ships with a sample at:

<install dir>\appsettings.Sentinel.example.json

Copy the Serilog.Using array entry and the WriteTo block into the live appsettings.json (or, preferred for a host-specific override, appsettings.Production.json). Restart the IdentityMesh service.

{
  "Serilog": {
    "Using": [
      "Serilog.Sinks.Console",
      "Serilog.Sinks.File",
      "Serilog.Sinks.EventLog",
      "Serilog.Sinks.AzureAnalytics"
    ],
    "WriteTo": [
      {
        "Name": "AzureAnalytics",
        "Args": {
          "workspaceId": "<log-analytics-workspace-guid>",
          "authenticationId": "<base64-shared-key>",
          "logName": "IdentityMeshLogs",
          "restrictedToMinimumLevel": "Information",
          "batchSize": 100
        }
      }
    ]
  }
}

Key knobs:

FieldMeaning
workspaceIdWorkspace GUID.
authenticationIdBase64-encoded primary or secondary key.
logNameCustom log table prefix. IdentityMeshLogs -> table IdentityMeshLogs_CL.
restrictedToMinimumLevelSeverity floor for the workspace specifically. Information catches admin-audit + run summaries; Warning is the safe minimum on a noisy host.
batchSizeEvents per upload. 100 is a reasonable default; cap is 30 MB / 30k events per request.

Authentication options

Workspace shared key (legacy)

Simplest. The key is embedded in appsettings.json. Treat as a secret — back it with Azure Key Vault per secrets-keyvault.md rather than shipping plaintext, and rotate via the workspace’s “Regenerate key” button.

The shared key authenticates the workspace, not the identity. Anyone with the key can write any custom log to the workspace. Production deployments should plan migration to managed identity (modern API) before the September 2026 cutoff.

Managed identity (modern API path)

Requires the Logs Ingestion API. The IdentityMesh host runs under an Entra-attached identity (system-assigned managed identity if Azure-hosted, service principal + cert if hybrid / on-prem) granted the Monitoring Metrics Publisher role on the DCR. The sink pulls the bearer token via DefaultAzureCredential.

This path requires a Serilog sink targeting the modern API — not yet shipped in this release. See “Modern API migration” below.

Service principal (hybrid)

A registered Entra application with a client cert, granted Monitoring Metrics Publisher on the DCR. Use this when the host is on-prem (no managed identity available) but you want to avoid shared keys.

Modern API migration (planned)

When IdentityMesh ships a sink targeting the Logs Ingestion API, the migration shape will be:

  1. Create a Data Collection Endpoint (DCE) in the same region as the workspace.
  2. Define a custom table schema for IdentityMeshLogs_CL — the JSON column shape Serilog emits.
  3. Author a Data Collection Rule (DCR) tied to the DCE, referencing the custom table.
  4. Grant the IdentityMesh identity (managed identity or service principal) Monitoring Metrics Publisher on the DCR.
  5. Swap the appsettings.Sentinel.example.json block for the modern variant (DCE endpoint URI + DCR immutable ID + table stream name).
  6. The legacy Serilog.Sinks.AzureAnalytics block can stay in place during the cutover window — both will write to the same *_CL table.

The September 2026 deprecation date is firm; plan the migration window in advance.

Sample KQL queries

Once events arrive in IdentityMeshLogs_CL, the JSON shape Serilog emits surfaces as <key>_s (string), <key>_d (double), <key>_b (bool) columns. Examples:

// Recent admin-audit rows (last 24 h)
IdentityMeshLogs_CL
| where TimeGenerated > ago(1d)
| where SourceContext_s endswith "SqlAdminAuditStore"
| project TimeGenerated, ActorUpn_s, Action_s, TargetKind_s, TargetId_s, StatusCode_d
| order by TimeGenerated desc
// Privilege escalation — role mutations grouped by actor
IdentityMeshLogs_CL
| where TimeGenerated > ago(7d)
| where Action_s startswith "POST /api/roles" or Action_s startswith "PUT /api/roles"
| summarize Edits = count(), First = min(TimeGenerated), Last = max(TimeGenerated)
        by ActorUpn_s
| order by Edits desc
// Connector deletes — high-blast-radius operations
IdentityMeshLogs_CL
| where Action_s == "DELETE /api/connectors/{id}"
| project TimeGenerated, ActorUpn_s, TargetId_s, StatusCode_d, MachineName_s
| order by TimeGenerated desc
// Off-hours admin activity
IdentityMeshLogs_CL
| where Action_s startswith "POST /api/" or Action_s startswith "PUT /api/" or Action_s startswith "DELETE /api/"
| where dayofweek(TimeGenerated) in (0d, 6d)
        or hourofday(TimeGenerated) !between (7 .. 19)
| project TimeGenerated, ActorUpn_s, Action_s, TargetKind_s, TargetId_s
| order by TimeGenerated desc
// Sync engine run failures over time
IdentityMeshLogs_CL
| where SourceContext_s contains "IdentityMesh.Engine"
| where Level_s == "Error"
| summarize Failures = count() by bin(TimeGenerated, 1h), MachineName_s
| render timechart

Failure modes

To surface the sink’s own diagnostics, enable Serilog SelfLog once at startup or temporarily point SELF_LOG at a writable file.

Cost considerations

Log Analytics ingestion is billed per GB. Watch for:

Operational notes

Configurable fields cheat-sheet

ConcernWhere to set it
Severity floorSerilog.WriteTo[AzureAnalytics].Args.restrictedToMinimumLevel
Workspace targetworkspaceId
AuthauthenticationId (legacy shared key) — secret-backed
Custom log table namelogName -> <logName>_CL in the workspace
Batch sizebatchSize