SIEM Integration

Microsoft Sentinel

Ship Arbiter authentication rejects and security events into Microsoft Sentinel for SOC monitoring, retention and correlation with your other security telemetry. End-to-end setup, no agent on either side.

How it fits together

Arbiter ships events into Sentinel via the Azure Monitor Logs Ingestion API. The chain looks like this:

Your Azure subscriptionArbiter CoreSIEM forwarder(every 10 s)HTTPSDCEEndpointDCRStream + transformCustom TableArbiterAuthLog_CLEntra ID appOAuth client credentialsSentinel / KQLreads
Arbiter Core authenticates with the Entra ID app, batches events and POSTs them to the DCE. The DCR validates the schema and writes into the Custom Table. Sentinel and any KQL consumer read from there.

Two unfamiliar Azure terms in there. Both are configuration objects, not running services:

  • DCE (Data Collection Endpoint): the public HTTPS URL Arbiter posts JSON to. One per region is plenty; shared across streams.
  • DCR (Data Collection Rule): the schema definition that says which columns the stream has and which Custom Table to write into. One DCR per stream.

Available streams

Each Arbiter SIEM target ships exactly one stream. To ship more than one, create one target per stream. Multiple targets share the same DCE; each points at its own Custom Table.

StreamPurposeDefault cadenceDefault rate capCustom Table
Security eventsRule-based detections (SOC alerting and triage)Real-time (10 s)1,000 / hArbiterSecurityEvents_CL
Auth log rejectsFailed authentication attempts (forensics, compliance)Every minute10,000 / hArbiterAuthLog_CL
Auth log (all)Permits and rejects, full RADIUS verdict feedEvery minute30,000 / hArbiterAuthLogAll_CL
Accounting eventsAcct-Start / Stop / Interim with session lifetime, bytesEvery minute20,000 / hArbiterAccounting_CL
Audit logOperator actions (policy edits, RBAC, settings touches)Hourly1,000 / hArbiterAuditLog_CL
Endpoints snapshotFull device inventory (asset-management overlay)DailyNone (bounded by table size)ArbiterEndpoints_CL

Append-only. A tick only fires if there is something new since the last successful tick (per-stream cursor on the Arbiter side). The endpoints snapshot is the exception by design: it emits the current inventory once per cadence window.

Cadence and rate cap are per-target and editable.Defaults are sized for an SME tenant (50 to 2,000 endpoints). For larger estates or stricter alerting SLAs, change the cadence from the SIEM target editor. The rate cap is a hard ceiling: when hit, forwarding pauses for the rest of the rolling hour and a warning surfaces on the target row.

Security events

Rule-based detections fired by Arbiter: suspicious traffic on a guest VLAN, unknown MAC bursts, expired certificate attempts. Negligible ingest cost for SME tenants. The one stream that runs at full real-time cadence by default because detector hits should land in your SOC fast.

Auth log rejects

Every rejected authentication attempt: failed 802.1X, unknown MAC, policy-denied, expired cert. Includes the matched policy and auth policy so denial reasons are correlatable.

Auth log (all)

Permits and rejects together. Useful when you want a complete authentication audit in Sentinel rather than just the failure feed. Order of magnitude higher volume than rejects only.

Accounting events

RADIUS Acct-Start / Stop / Interim with session duration and byte counters. Useful for session-lifetime forensics, user attribution to network flows and licence usage reporting.

Audit log

Operator-action audit trail: who edited which policy, who rotated which secret, when settings changed. Low volume, highest compliance value. Pairs naturally with Sentinel Workbooks for an evidence-ready change history.

Endpoints snapshot

Daily full inventory of every endpoint the tenant has seen. Each tick emits the entire current endpoints table as a series of point-in-time rows so you can diff for new or disappearing devices in Sentinel.

Before you start

Prerequisites

  • An Azure subscription where you can create a resource group, a Log Analytics workspace and an Entra ID app registration.
  • Microsoft Sentinel enabled on that workspace (optional: without it the data still lands and is queryable, Sentinel just adds the SOC features on top).
  • An Arbiter tenant where you are signed in as an admin.
  • Permission to grant the Monitoring Metrics Publisherrole on the DCE and DCR. Contributor or Owner on the resource group is enough.
Allow about 30 minutes end-to-end. The least obvious step is locating the DCR immutable ID. The remaining steps are mostly Azure portal configuration.

Create a resource group and Log Analytics workspace

Why: the workspace is where Sentinel queries the data from. Everything else in this guide lives inside the same resource group.

  • Resource group: e.g. rg-sentinel-lab, region North Europe.
  • Log Analytics workspace: e.g. law-arbiter-sentinel, same region as the resource group.
  • Pricing tier: start on Pay-as-you-go. Move to a Sentinel Commitment Tier later if your ingest grows.
Keep the workspace and the DCE in the same region. Cross-region ingest works but is more expensive and adds latency.

Microsoft Sentinel features (Workbooks, Hunting, Analytics Rules) are optional. Enable Sentinel on the workspace from its blade if you want them. Without it, data still lands and is queryable from the workspace via plain KQL.

Create a Data Collection Endpoint (DCE)

Why: the DCE is the public URL Arbiter POSTs to. One DCE is enough; you will reuse it across both streams.

Portal search bar → Data Collection Endpoints+ Create:

  • Endpoint name: dce-arbiter-sentinel
  • Subscription and resource group: same as the workspace
  • Region: same as the workspace

Review and create. About 30 seconds. Once provisioned, open the DCE and copy the Logs Ingestion URL from the Overview tab:

https://dce-arbiter-sentinel-XXXX.northeurope-1.ingest.monitor.azure.com

Strip any trailing slash. Save this URL for Step 7.

Create the Custom Table and its DCR

Why: the Custom Table is the destination Sentinel queries from. The DCR sits in front of it and enforces the schema. The wizard creates both in one flow.

One Custom Table per stream. The walkthrough below covers auth log rejects; the security events flow is identical except for the table name and the sample file.

a. Grab the sample JSON

The wizard infers the table's column types from a sample file. Save the block below as arbiter_authlog_rejects_sample.json:

arbiter_authlog_rejects_sample.json

Auth log rejects stream. Wizard uses this to build the column schema.

[
  {
    "TimeGenerated": "2026-05-21T14:30:12.000123Z",
    "event_id": 90342,
    "tenant_id": 201,
    "mac_address": "aabbccddeeff",
    "user_name": null,
    "nas_ip": "10.0.0.5",
    "nas_name": "core-sw-01",
    "nas_port": "GigabitEthernet0/1",
    "vlan_id": 30,
    "ssid": null,
    "ap_mac": null,
    "auth_method": "mab",
    "eap_type": null,
    "reason": "unknown MAC: no matching endpoint",
    "policy_name": "Default Deny",
    "auth_policy_name": "Wired MAB",
    "access_profile_name": "Quarantine",
    "monitor_mode": false,
    "monitor_coerced": false,
    "client_cert_cn": null,
    "client_cert_serial": null,
    "client_cert_issuer": null,
    "client_cert_issuer_org": null,
    "source": "arbiter.auth_log_rejects"
  }
]

For the security events stream, use this sample instead:

[
  {
    "TimeGenerated": "2026-05-21T14:30:00.123456Z",
    "event_id": 1042,
    "tenant_id": 201,
    "rule_name": "ssh-on-guest-vlan",
    "severity": "high",
    "mac_address": "aabbccddeeff",
    "nas_ip": "10.0.0.5",
    "nas_port_id": "GigabitEthernet0/1",
    "summary": "SSH (TCP/22) seen from a guest-VLAN endpoint",
    "details": {
      "flow_src": "192.168.30.42",
      "flow_dst_port": 22,
      "flow_proto": "tcp"
    },
    "source": "arbiter.security_events"
  }
]

b. Run the table-creation wizard

In the workspace blade, left nav → Tables+ CreateNew custom log (DCR-based). Fill in:

  • Table name: ArbiterAuthLog(Azure appends _CL; do not type it yourself).
  • Data collection rule: Create new, same resource group, name dcr-arbiter-authlog.
  • Data collection endpoint: pick dce-arbiter-sentinel from Step 2.

Click Next. On the schema page, Browse for files and upload the sample JSON. The preview should show TimeGenerated as datetime and the rest as string / int / dynamic as appropriate.

Leave the Transformation editor on the default source query. Arbiter ships TimeGenerated directly so no KQL rename is needed.

Click NextCreate. Allow 1 to 2 minutes for the table to provision.

c. Copy the DCR identifiers

Once created, open dcr-arbiter-authlog and grab two values for Step 7:

  • DCR immutable ID. Top toolbar → JSON View. Search for "immutableId"; the value looks like dcr-64c60e809eca44d1af23031db751157c.
  • Stream name. Left nav → Data sources. The row is named Custom-ArbiterAuthLog_CL.

Register an Entra ID application

Arbiter authenticates as a confidential client application using OAuth 2.0 client credentials. Create one app registration; both streams can share it.

  1. Search bar: Microsoft Entra ID → left nav App registrations + New registration.
  2. Name: Arbiter Sentinel Ingest (label only; pick whatever helps you).
  3. Supported account types: Accounts in this organisational directory only (Single tenant).
  4. Redirect URI: leave blank.
  5. Register.

On the Overview tab, copy two values you will paste into Arbiter later:

  • Application (client) ID: a GUID.
  • Directory (tenant) ID: a different GUID (this is your Entra tenant identifier).

Create a client secret

In the same app registration:

  1. Left nav: Certificates & secrets.
  2. Client secrets tab → + New client secret.
  3. Description: arbiter-core (label only).
  4. Expires: 24 months recommended. Azure emails the registration owner ahead of expiry. Shorter lifetimes are fine for tighter rotation policies; longer is unsupported.
  5. Add.

The secret value appears once in the Value column, formatted like Aa1Bb2~Cc3Dd4.... Copy it immediately. If you navigate away without copying, the value is unrecoverable: delete the secret and create a new one.

Treat the client secret like a password. Arbiter encrypts it at rest with AES-256-GCM and never returns it on the wire, but you should still avoid copy-pasting it through chat or email.

Grant Monitoring Metrics Publisher

Why: the Logs Ingestion API checks RBAC at two scopes. A missing role assignment is the most common reason a Sentinel target shows HTTP 403.

  • DCE needs the role for the connection.
  • DCR needs the role for the stream write. This is the strict check.

Do the steps below twice: once on dce-arbiter-sentinel, once on dcr-arbiter-authlog.

  1. Open the resource.
  2. Left nav → Access control (IAM).
  3. + Add Add role assignment.
  4. Role: Monitoring Metrics PublisherNext.
  5. Members: User, group, or service principal+ Select members.
  6. Search for Arbiter Sentinel Ingest. Pick the result with the service-principal icon (small blue square, not a user avatar). Click Select.
  7. Review + assign.
RBAC propagation lag: newly granted role assignments on resource-scoped objects can take 5 to 15 minutes to take effect even though the portal shows them as live immediately. If the Test request in Step 7 returns 403, wait 10 minutes and try again before troubleshooting.

Configure the SIEM target in Arbiter

Why: the SIEM target row is what tells the Arbiter forwarder where to ship events.

In the Arbiter admin portal, open the tenant you want shipping to Sentinel. Switch to the SIEM tab → + Add target.

FieldValue
NameSentinel - auth rejects (free text)
DestinationMicrosoft Sentinel
StreamAuth log rejects
DCE URLfrom Step 2
DCR immutable IDfrom Step 3c
Stream nameCustom-ArbiterAuthLog_CL
Azure tenant IDfrom Step 4
Client IDfrom Step 4
Client secretfrom Step 5

Save the target, then click the Test paper-plane icon next to the row. The status flips to OK (green tick) within about 30 seconds on success.

If it stays red, hover the Error icon. The tooltip shows the exact HTTP response from Azure:

  • 401: AAD credentials wrong (client ID, client secret or tenant ID).
  • 403: role assignment missing or not yet propagated. See Troubleshooting below.
  • 400: stream name typo or schema mismatch.

Test it's working

Why: verify the pipeline end-to-end in two stages before you trust it for production alerting.

a. Canary check (Test button)

In Arbiter, on the SIEM tab, click the paper-plane icon next to the target row. The forwarder posts a single synthetic event matching the stream's schema; the status flips to OK within about 30 seconds if everything is wired correctly.

Confirm it landed in Sentinel: open the workspace → Logs → paste:

ArbiterAuthLog_CL
| where TimeGenerated > ago(5m)
| project TimeGenerated, mac_address, reason

You should see one row with mac_address = 000000000000 and a reason starting with "Arbiter SIEM test event…".

b. Real data check

Real rejects from the tenant start flowing on the next 10-second cursor tick. Filter the canary out so you only see live data:

ArbiterAuthLog_CL
| where TimeGenerated > ago(15m)
| where mac_address != "000000000000"
| project TimeGenerated, mac_address, nas_name, auth_method, reason, auth_policy_name
| order by TimeGenerated desc

If your tenant has any failing auths (blocked MABs, expired certs, default-deny matches), rows appear here within a minute or two of the auth happening. If nothing's appearing, check the Auth log tab in the Arbiter portal first to confirm rejects are actually being produced.

Expected latency

StageTime
Endpoint fails auth, row written to auth_logt + 0 s
Arbiter forwarder picks the row up on next tickt + 0 to 10 s
POST accepted by the Azure DCEt + 10 to 15 s
Row queryable from KQLt + 30 to 90 s
Canary delivered and real rejects visible in KQL? You're done. The integration runs unattended from here.

Build dashboards and alerts (optional)

Why: Sentinel's value over a passive log store is that you can render the data into Workbooks and trigger Analytics Rules. Starter queries below.

// Failed-auth rate by hour, useful for a Workbook tile
ArbiterAuthLog_CL
| where TimeGenerated > ago(7d)
| summarize rejects = count() by bin(TimeGenerated, 1h)
| render timechart

// Top reject reasons over the last 24 hours
ArbiterAuthLog_CL
| where TimeGenerated > ago(24h)
| summarize count() by reason
| order by count_ desc
| take 10

// Endpoints producing the most rejects (potential misconfig or attack)
ArbiterAuthLog_CL
| where TimeGenerated > ago(24h)
| summarize attempts = count(), reasons = make_set(reason) by mac_address
| order by attempts desc
| take 20

To turn one of these into an alert, go to Sentinel → Analytics+ CreateScheduled query rule. Paste the query, set a threshold (e.g. "trigger when more than 20 rejects from the same MAC in 5 minutes") and an action (email, Teams, ServiceNow ticket, etc.).

Troubleshooting

HTTP 403 from the DCE

By far the most common failure. The AAD token issued cleanly (no 401) but the DCR refused the principal. Two causes:

Cause 1: propagation lag

Resource-scoped role assignments take 5 to 15 minutes to take effect. Wait 10 minutes and click Test again. The forwarder also retries every 10 seconds in the background, so the status flips to OK on its own once Azure catches up.

Cause 2: wrong principal assigned

Azure lists an Entra app twice in the member picker: the Application object and the Service Principal. The role must be on the Service Principal.

  • Check the IAM → Role assignments tab. Your row's Type column should read Service principal, not App.
  • If wrong, remove that row and add a new one. Fastest way to disambiguate: paste the Service Principal Object ID (visible from the Enterprise Applications view of the app) into the member search box.

HTTP 401 from AAD

Wrong client ID, wrong client secret or wrong Azure tenant ID. Edit the SIEM target row in Arbiter and re-paste the values from the Entra app's Overview tab. Missing or expired client secrets are the usual culprit.

HTTP 400 from the DCE

The request reached the DCR but failed validation. Almost always one of:

  • Stream name typo. Case sensitive, must start with Custom-.
  • Schema mismatch: a field Arbiter sends isn't in the table. Recreate the table using the latest sample JSON from Step 3.

Status flips between OK and Error

Intermittent connectivity from Arbiter Core to ingest.monitor.azure.com, or a transient AAD token issue. No data is lost: the forwarder retries every 10 seconds and the cursor only advances on success, so rows are held until the next successful tick.

Cost and volume

Sentinel charges per GB of data ingested. Order of magnitude at the time of writing: around €2.30 per GB on the Pay-as-you-go tier; Commitment Tiers reduce this if you ingest steadily above 100 GB per day.

For typical SME deployments the cost split is heavily skewed:

  • Security events are sparse. A few dozen events per tenant per day at most: negligible cost.
  • Auth log rejects scale with how noisy your network is. The stream filters to result = 'reject' on the Arbiter side, so the typical volume is 5 to 15% of total auth events. Each row is under a kilobyte. A 500-endpoint SME tenant typically ships well under 100 MB per month.
  • Auth log (all) and accounting events are higher volume. Each row is around 500 bytes; at the default rate cap of 30,000 events/h that's roughly 360 MB per day per tenant worst case.
  • Audit log is sparse (operator actions only): negligible cost.
  • Endpoints snapshot is one full inventory per day. A 2,000-endpoint tenant ships around 1 MB per day on this stream.

The per-target hourly rate cap (visible on every SIEM target row in the Arbiter portal) is the safety net. When hit, forwarding pauses for the rest of the rolling hour and the row turns red with a warning so the operator notices instead of getting a surprise Sentinel bill. Defaults are sized at roughly 3x the realistic SME peak; raise them per target if your subscription is sized for more.

Need help?

Stuck on a 403 or want us to look at your Sentinel target config? Email support@arbiter.ie with a screenshot of the SIEM tab and the error tooltip. We usually reply same business day.

More guidesIntegrations overview