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 Serilog.Formatting.Compact package already ships with IdentityMesh; there is nothing to install.

2. Windows Event Log

Warning+ events are also written to the Windows Event Log on each host, in a dedicated IdentityMesh log with one Source per component (see event-log.md):

ComponentEvent logSource name
Admin APIIdentityMeshIdentityMesh Admin API
Sync EngineIdentityMeshIdentityMesh Sync Engine
Relay AgentIdentityMeshIdentityMesh Relay Agent
Web UIIdentityMeshIdentityMesh Web UI
ConnectIdentityMeshIdentityMesh Connect
InstallerIdentityMeshIdentityMesh Installer

The API, engine and relay agent write through Serilog.Sinks.EventLog with restrictedToMinimumLevel: Warning, so the Event Log gets the “something requires attention” subset, not every INFO line. The Web UI source carries browser-side errors reported to the API. Connect writes to the log only on workstations where an administrator registered its source.

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
CSObjectIduniqueidentifierManagement Space object id (or 00000000-… for changes not tied to a Management Space row)
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\IdentityMesh Relay Agent\logs\*.log]
sourcetype = identitymesh:relay
index = identitymesh

[WinEventLog://IdentityMesh]
disabled = false
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 IdentityMesh event log (no source filter needed; the log holds only IdentityMesh events).
  3. The audit tables flow via SQL data connector — Sentinel workbook polls IM_AdminAudit directly.

For direct ingestion without AMA, see siem-sentinel.md.

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: IdentityMesh

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: IdentityMesh
    source: windows.event_log
    service: 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