Runbooks / Siem Integration

SIEM Integration

IdentityMesh’s compliance and operational signals are designed to flow into a SIEM without code changes. This document maps every emitted surface to where your SIEM picks it up, with concrete ingest recipes for the common platforms and sample detection queries to start with.

If your SIEM isn’t listed below: it almost certainly has a file / Windows Event Log / OTLP / database connector that fits one of the patterns here. The IdentityMesh side is already producing the data — pick the surface that matches your SIEM’s strengths.

Emitted surfaces — the field map

1. Application log files

ComponentFile pathFormat
Admin API<install dir>\logs\identitymesh-api-YYYYMMDD.logSerilog rolling, daily, 30-day retain, JSON-friendly text template
Sync Engine<install dir>\logs\identitymesh-YYYYMMDD.logSame template
Relay Agent<install dir>\logs\identitymesh-relay-YYYYMMDD.logSame template

Per-line shape (one example):

2026-04-25 11:32:18.421 +00:00 [INF] [12] {RequestId} {RequestPath} permission=connectors.write granted to alice@corp

The interesting fields enriched by Serilog scopes:

FieldMeaning
RequestIdASP.NET Core trace identifier — pivots to/from IM_AdminAudit.CorrelationId
ConnectionIdKestrel connection
RequestPathConcrete URL
RequestMethodHTTP verb
StatusCodeFinal response status
ElapsedRequest duration ms
SourceContextLogger category — identifies the producing component
MachineNameStamped via Enrich.WithMachineName()
ThreadIdStamped via Enrich.WithThreadId()
CycleId, InstanceNameSync engine — present on every line during a connector run

To switch from the default text template to JSON for easier SIEM parsing, swap the Console/File Serilog sink config:

"WriteTo": [
  {
    "Name": "File",
    "Args": {
      "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
      "path": "logs/identitymesh-api-.log",
      "rollingInterval": "Day",
      "retainedFileCountLimit": 30
    }
  }
]

The package Serilog.Formatting.Compact is already a transitive dependency; no new install needed.

2. Windows Event Log

Warning+ Serilog events are also written to the Windows Event Log on each host:

ComponentEventLog channelSource name
Admin APIApplicationIdentityMesh.Admin.Api
Sync EngineApplicationIdentityMesh
Relay AgentApplicationIdentityMesh.Relay.Agent

Configured via Serilog.Sinks.EventLog with restrictedToMinimumLevel: Warning so the Event Log gets the “something requires attention” subset, not every INFO line.

3. Audit tables (SQL)

Two append-only tables holding compliance-grade evidence. Either poll directly via your SIEM’s database connector or proxy through the REST endpoints (which are gated on audit.read).

IM_ObjectAudit — identity-data changes

ColumnTypeMeaning
AuditIdbigintSurrogate key, monotonic
MeshObjectIduniqueidentifierAffected mesh object
CSObjectIduniqueidentifierConnector-space object id (or 00000000-… for non-CS changes)
ChangeTypenvarchar(50)Create / RefreshAttributes / Link / connector change types
Sourcenvarchar(200)Connector name or JoinEngine for join-time mutations
ChangeJsonnvarchar(max)Mutation payload (object type, attributes, etc.)
ChangedOndatetime2UTC
ActorUpnnvarchar(256)UPN (or instance name on engine-initiated rows)
ActorSidnvarchar(128)Windows SID or Entra oid
ActorSourcenvarchar(32)Api / Engine / Relay / System

REST equivalent: GET /api/audit?meshObjectId=…&since=…&take=… (filtered) or GET /api/audit/by-object/{meshObjectId} (all history for one object).

IM_AdminAudit — admin-surface mutations

ColumnTypeMeaning
AuditIdbigintSurrogate key
WhenUtcdatetime2UTC
ActorUpnnvarchar(256)UPN
ActorSidnvarchar(128)Windows SID or Entra oid
Actionnvarchar(200)HTTP method + route template (e.g. POST /api/connectors)
TargetKindnvarchar(64)Stable name of target type (Connector, Role, …) — populated for high-value mutations
TargetIdnvarchar(128)Specific id (GUID, role name) extracted from route values
StatusCodeintHTTP response status — denied 403s recorded too
CorrelationIdnvarchar(128)Pivots to/from log RequestId
BeforeJsonnvarchar(max)Pre-mutation snapshot for high-value handlers (sensitive fields scrubbed)
AfterJsonnvarchar(max)Post-mutation snapshot

REST equivalent: GET /api/admin-audit with filters for actorUpn, targetKind, targetId, sinceUtc, untilUtc, paged via skip / take.

Indexes for SIEM polling:

Retention is governed by license tier (see audit-retention.md). Don’t size your SIEM ingest assuming infinite back-history — a Standard-tier deployment keeps 30 days, Enterprise keeps unlimited.

4. Metrics — OpenTelemetry

When Telemetry:Otlp:Enabled is true, both the Admin API and the Sync Engine emit OTLP metrics + traces to the configured exporter. Counters worth alerting on:

MetricMeterWhat it tells you
identitymesh.runs.failedIdentityMesh.EngineSync run failures (with connector_type, outcome tags)
identitymesh.exports.objects{outcome="Failed"}samePer-object export failures
identitymesh.license.fallbackssameLicense → Starter trial fallback (reason tag)
identitymesh.audit.purgedsameRetention sweep volume (per table tag) — sudden spike → investigate

These flow to whatever your OTel Collector forwards them to — Datadog, Honeycomb, Grafana Cloud, Azure Monitor.

Per-SIEM ingestion recipes

Splunk

Pattern: Universal Forwarder → log files + Windows Event Log → HEC / indexers.

# inputs.conf on each IdentityMesh host
[monitor://C:\Program Files\IdentityMesh\logs\*.log]
sourcetype = identitymesh:json     # if you flipped to CompactJsonFormatter
disabled = false
index = identitymesh

[monitor://C:\Program Files\IdentityMeshRelayAgent\logs\*.log]
sourcetype = identitymesh:relay
index = identitymesh

[WinEventLog://Application]
disabled = false
whitelist = IdentityMesh.Admin.Api,IdentityMesh,IdentityMesh.Relay.Agent
index = identitymesh

For the audit tables: a dbx database connector polling IM_AdminAudit filtered by WhenUtc > "@last_seen".

Microsoft Sentinel

Pattern: Azure Monitor Agent (AMA) → Data Collection Rule (DCR) → Log Analytics custom table.

  1. Install AMA on each IdentityMesh host (Admin API, Sync Engine, each Relay).
  2. Author a DCR pointing at C:\Program Files\IdentityMesh\logs\*.log (file-based) and the Application event log filtered to the three IdentityMesh sources.
  3. The audit tables flow via SQL data connector — Sentinel workbook polls IM_AdminAudit directly.

A future direct-Logs-Ingestion-API mode is on the roadmap so AMA isn’t required.

Elastic / Logstash

Pattern: Filebeat → log files + Winlogbeat → Event Log → Logstash → Elasticsearch.

# filebeat.yml
filebeat.inputs:
  - type: filestream
    paths:
      - C:\Program Files\IdentityMesh\logs\*.log
    parsers:
      - ndjson:
          target: ""        # if CompactJson formatter
          add_error_key: true
    fields:
      app: identitymesh
      component: admin-api
# winlogbeat.yml
winlogbeat.event_logs:
  - name: Application
    providers:
      - IdentityMesh.Admin.Api
      - IdentityMesh
      - IdentityMesh.Relay.Agent

Audit tables: standard JDBC input plugin polling IM_AdminAudit.

Datadog

Pattern: Datadog Agent on each host → log file + Windows Event Log integrations → Datadog.

# C:\ProgramData\Datadog\conf.d\identitymesh.d\conf.yaml
logs:
  - type: file
    path: C:\Program Files\IdentityMesh\logs\*.log
    service: identitymesh-admin-api
    source: csharp
  - type: windows_event
    channel_path: Application
    source: windows.event_log
    service: identitymesh
    log_processing_rules:
      - type: include_at_match
        name: identitymesh_only
        pattern: '^IdentityMesh'

Metrics flow via OTLP to the Datadog Agent if you’ve enabled the OTLP receiver. The audit tables ride a SQL Server integration.

Sample detection queries

Examples that work against IM_AdminAudit directly. Translate to your SIEM’s query language as needed; the field names are stable.

-- "Privilege escalation" — role mutations grouped by actor
SELECT ActorUpn, COUNT(*) AS RoleEdits, MIN(WhenUtc) AS First, MAX(WhenUtc) AS Last
FROM   dbo.IM_AdminAudit
WHERE  WhenUtc > DATEADD(DAY, -7, SYSUTCDATETIME())
  AND  Action LIKE '%/api/roles/%'
GROUP BY ActorUpn
ORDER BY RoleEdits DESC;

-- "Connector deletes" — high-blast-radius operations
SELECT WhenUtc, ActorUpn, TargetId, StatusCode
FROM   dbo.IM_AdminAudit
WHERE  Action = 'DELETE /api/connectors/{id}'
ORDER BY WhenUtc DESC;

-- "Off-hours admin activity"
SELECT WhenUtc, ActorUpn, Action, TargetKind, TargetId
FROM   dbo.IM_AdminAudit
WHERE  Action LIKE '%/api/%'
  AND  Action NOT LIKE 'GET %'
  AND  (DATEPART(WEEKDAY, WhenUtc) IN (1, 7)
        OR DATEPART(HOUR, WhenUtc) NOT BETWEEN 7 AND 19)
ORDER BY WhenUtc DESC;

-- "Denied admin attempts" — who tried and got blocked
SELECT WhenUtc, ActorUpn, Action, TargetId, StatusCode
FROM   dbo.IM_AdminAudit
WHERE  StatusCode IN (401, 403)
  AND  WhenUtc > DATEADD(DAY, -1, SYSUTCDATETIME())
ORDER BY WhenUtc DESC;

-- "License upload events" — every license rotation
SELECT WhenUtc, ActorUpn, StatusCode, CorrelationId
FROM   dbo.IM_AdminAudit
WHERE  Action = 'POST /api/license/upload'
ORDER BY WhenUtc DESC;

For IM_ObjectAudit (identity-data changes):

-- "Mass mesh-object deletion" — single actor / engine instance
-- removing more than 100 objects in 5 minutes.
SELECT ActorUpn, ActorSource, COUNT(*) AS Deletes,
       MIN(ChangedOn) AS First, MAX(ChangedOn) AS Last
FROM   dbo.IM_ObjectAudit
WHERE  ChangeType = 'Delete'
  AND  ChangedOn > DATEADD(MINUTE, -5, SYSUTCDATETIME())
GROUP BY ActorUpn, ActorSource
HAVING COUNT(*) > 100;

Operational notes