Sentinel Table and Rule Schema
What the Microsoft Sentinel destination sends and the Azure resources that receive it. For the setup steps, see Export security incidents to Microsoft Sentinel.
What is posted
POST <dceUrl>/dataCollectionRules/<dcrImmutableId>/streams/<streamName>?api-version=2023-01-01
Authorization: Bearer <Microsoft Entra token for https://monitor.azure.com/.default>
Content-Type: application/json
[ <one OCSF 1.9.0 Detection Finding> ]
The body is the same event as the OCSF JSON format, in an array of one. Optional fields that are empty are absent, and the stream declares them as nullable columns. A successful request returns 204.
Columns
Each top-level field of the event is one column. Nested objects and lists are dynamic columns, so a field added inside one needs no schema change. The table has one more column, TimeGenerated (datetime), which the rule fills from time.
| Column | Type | Content |
|---|---|---|
activity_id | int | OCSF activity: 1 Create, 2 Update, 3 Close. |
activity_name | string | Create, Update or Close. Close is sent when an incident already exported is resolved. |
actor | dynamic | The user or agent the incident is about. Omitted when the destination redacts actors. |
assignee | dynamic | The incident assignee. Omitted when the destination redacts actors. |
attacks | dynamic | MITRE ATLAS and ATT&CK techniques. |
category_name | string | Findings. |
category_uid | int | 2. |
class_name | string | Detection Finding. |
class_uid | int | 2004. |
count | long | Number of findings in the incident. |
end_time | long | Last finding time, epoch milliseconds. |
evidences | dynamic | The agent involved. Never evidence content. Omitted when the destination redacts actors. |
finding_info | dynamic | Title, description, uid, first and last seen, finding types and related analytics. |
is_alert | boolean | True for high and critical incidents. |
message | string | The incident title. |
metadata | dynamic | Product, profiles, event uid and version 1.9.0. |
remediation | dynamic | The recommended action. |
resources | dynamic | Affected resources, such as an MCP server. |
severity | string | Severity name. |
severity_id | int | OCSF severity 1 to 5. |
src_url | string | Link to the incident in AIControls. Omitted when actors are redacted. |
start_time | long | First finding time, epoch milliseconds. |
status | string | Incident status name. |
status_id | int | OCSF status. |
tickets | dynamic | Jira or ServiceNow tickets made for the incident. |
time | long | Event time, epoch milliseconds. The rule sets TimeGenerated from it. |
type_name | string | Detection Finding: Create, Update or Close. |
type_uid | int | class_uid * 100 + activity_id. |
unmapped | dynamic | Fields OCSF has no place for. |
verdict | string | Triage verdict name. |
verdict_id | int | OCSF verdict. |
Create the table
az monitor log-analytics workspace table create \
--resource-group YOUR_RESOURCE_GROUP \
--workspace-name YOUR_WORKSPACE \
--name AIControlsFindings_CL \
--columns TimeGenerated=datetime activity_id=int activity_name=string actor=dynamic assignee=dynamic attacks=dynamic category_name=string category_uid=int class_name=string class_uid=int count=long end_time=long evidences=dynamic finding_info=dynamic is_alert=boolean message=string metadata=dynamic remediation=dynamic resources=dynamic severity=string severity_id=int src_url=string start_time=long status=string status_id=int tickets=dynamic time=long type_name=string type_uid=int unmapped=dynamic verdict=string verdict_id=int
Data collection rule
Create the rule in the same region as the workspace, with your endpoint and workspace IDs. The rule must use the data collection endpoint you give AIControls.
{
"location": "eastus",
"properties": {
"dataCollectionEndpointId": "/subscriptions/YOUR_SUBSCRIPTION/resourceGroups/YOUR_RESOURCE_GROUP/providers/Microsoft.Insights/dataCollectionEndpoints/YOUR_DCE",
"streamDeclarations": {
"Custom-AIControlsFindings_CL": {
"columns": [
{ "name": "activity_id", "type": "int" },
{ "name": "activity_name", "type": "string" },
{ "name": "actor", "type": "dynamic" },
{ "name": "assignee", "type": "dynamic" },
{ "name": "attacks", "type": "dynamic" },
{ "name": "category_name", "type": "string" },
{ "name": "category_uid", "type": "int" },
{ "name": "class_name", "type": "string" },
{ "name": "class_uid", "type": "int" },
{ "name": "count", "type": "long" },
{ "name": "end_time", "type": "long" },
{ "name": "evidences", "type": "dynamic" },
{ "name": "finding_info", "type": "dynamic" },
{ "name": "is_alert", "type": "boolean" },
{ "name": "message", "type": "string" },
{ "name": "metadata", "type": "dynamic" },
{ "name": "remediation", "type": "dynamic" },
{ "name": "resources", "type": "dynamic" },
{ "name": "severity", "type": "string" },
{ "name": "severity_id", "type": "int" },
{ "name": "src_url", "type": "string" },
{ "name": "start_time", "type": "long" },
{ "name": "status", "type": "string" },
{ "name": "status_id", "type": "int" },
{ "name": "tickets", "type": "dynamic" },
{ "name": "time", "type": "long" },
{ "name": "type_name", "type": "string" },
{ "name": "type_uid", "type": "int" },
{ "name": "unmapped", "type": "dynamic" },
{ "name": "verdict", "type": "string" },
{ "name": "verdict_id", "type": "int" }
]
}
},
"destinations": {
"logAnalytics": [
{
"name": "workspace",
"workspaceResourceId": "/subscriptions/YOUR_SUBSCRIPTION/resourceGroups/YOUR_RESOURCE_GROUP/providers/Microsoft.OperationalInsights/workspaces/YOUR_WORKSPACE"
}
]
},
"dataFlows": [
{
"streams": ["Custom-AIControlsFindings_CL"],
"destinations": ["workspace"],
"transformKql": "source | extend TimeGenerated = unixtime_milliseconds_todatetime(time)",
"outputStream": "Custom-AIControlsFindings_CL"
}
]
}
}
The stream name in the rule is the Stream name of the destination. It must start with Custom-; a built-in Microsoft- stream is refused because its schema is not this one.
Destination fields
Destinations are saved as the siem managed integration. The Sentinel settings are in the sentinel object of a destination with "format": "sentinel". The credential is clientSecret or a certificate with its privateKey, never both. The secret and the private key are masked when the configuration is read and redacted in the configuration audit. The webhookUrl, token, secret, securityLake and Splunk fields are refused for this format.
| Field | Meaning |
|---|---|
sentinel.tenantId | Required. The directory (tenant) ID, a GUID. |
sentinel.clientId | Required. The application (client) ID, a GUID. |
sentinel.clientSecret | The client secret value, at most 256 characters. Required unless a certificate is set. |
sentinel.certificate | PEM certificate registered on the app. Public, so not masked. Needs privateKey. |
sentinel.privateKey | PEM private key of the certificate: unencrypted PKCS#8 or PKCS#1 RSA, 2048 bits or more, matching the certificate. Masked and redacted. The pair is checked on save and an expired certificate is refused. |
sentinel.certificateExpires | Read-only. The certificate expiry, added when the configuration is read; refused on save. |
sentinel.dceUrl | Required. The endpoint's Logs Ingestion URI: https://<name>.<region>.ingest.monitor.azure.com, with no path, port or query. |
sentinel.dcrImmutableId | Required. The rule's immutable ID, dcr- and 32 hex digits. |
sentinel.streamName | Required. Custom- and the table name, for example Custom-AIControlsFindings_CL. |
Routing and payload options (minSeverity, eventTypes, autoExport, redactActors, includeSummaries) work as for the other formats.
Limits and failure reasons
| Item | Value |
|---|---|
| Request body | At most 1,000,000 bytes. Larger events fail with payload_too_large. |
| Attempts | 3 in total per delivery. An attempt is a token lookup (a request to Entra only when no valid token is cached) and one post. |
| Backoff | 1 s, then 2 s, or Retry-After up to 5 s. A longer Retry-After ends the delivery with the reason of that response (http_429, or token_http_429 or token_<error> from Entra) and the next automatic run retries. |
| Token cache | Until 5 minutes before expiry, or half the lifetime for a token under 10 minutes. A 401 from Logs Ingestion replaces it once per delivery. |
| Cloud | Public Azure only. The authority is login.microsoftonline.com. |
Failure reasons recorded in the audit trail: token_<error> or token_<error>_<AADSTS number> (for example token_invalid_client_7000215), token_http_<status>, invalid_certificate, token_network_error, token_timeout, token_invalid_response, http_<status>, network_error, timeout, payload_too_large and invalid_destination. The Microsoft error text is never copied.