Getting data into Azure Monitor — diagnostic settings, data collection rules, Azure Monitor Agent and the Logs Ingestion API

How platform logs, VM data and your own records reach a workspace — diagnostic settings (limits, destinations, category groups, resource-specific tables, errors) in Bicep; data collection rules and Azure Monitor Agent; the Logs Ingestion API with a custom table, a Direct DCR, TypeScript and its 403, 413 and 429 errors.

markdownreferencev1030M-7PH3stage: buildtrust: officialservice: monitorinterface: cliinterface: iacinterface: sdkdiagnostic settingsfive diagnostic settings per resourcedestination must already existEvent Hubs same regioncategory group allLogscategory group auditSQL auditing not enabled by audit categoryresource-specific tablesAzureDiagnostics tablelogAnalyticsDestinationType Dedicatedexport-to-resource-specificAllMetrics onlyMetric category is not supportedactivity log diagnostic setting subscriptionaz monitor diagnostic-settings createaz monitor diagnostic-settings categories listdata collection ruleDCR limits10 data sources20 data flowstransformation 15360 charactersDCR same region as workspacedata collection rule associationdata collection endpoint when neededAzure Monitor AgentAzureMonitorLinuxAgentAzureMonitorWindowsAgentAzure Monitor Agent managed identityLogs Ingestion APIkind DirectlogsIngestion endpointcustom table _CLstreamDeclarations Custom-transformKql@azure/monitor-ingestionLogsIngestionClient uploadisAggregateLogsUploadErrorLogs Ingestion 403 permissions 30 minutesLogs Ingestion 413 ReadyBody_ClientConnectionAbortLogs Ingestion 429 Retry-After1 MB per call

Created 21 Oct 2026 · Modified 23 Oct 2026 · 1 version

Getting data into Azure Monitor

Diagnostic settings (platform logs and metrics)

Resource logs aren't collected until a diagnostic setting sends them somewhere; metrics and the activity log are collected anyway.

RuleDetail
CountFive settings per resource (fixed)
DestinationsLog Analytics, storage, Event Hubs, partner solutions; at most one of each type per setting (two workspaces need two settings); the destination must already exist; storage and Event Hubs must be in the resource's region; other subscriptions are fine with RBAC on both
CategoriesPick categories, or a category group: allLogs or audit (not both needed; groups follow Microsoft's changes). audit on SQL Database doesn't turn on SQL auditing
Collection modeResource-specific tables (logAnalyticsDestinationType: 'Dedicated', CLI --export-to-resource-specific true) where the service supports them, rather than the shared AzureDiagnostics table
MetricsOnly AllMetrics for most services; metrics with dimensions arrive flattened; some metrics aren't exportable
CostIngestion is billed, and so is export of some categories. Collect only needed categories; skip metrics unless you query them in logs
Activity logA subscription-level setting: az monitor diagnostic-settings subscription create (or ARM), not the resource command
// Send a Key Vault's resource logs and metrics to a workspace, into resource-specific tables
param keyVaultName string
param workspaceId string

resource kv 'Microsoft.KeyVault/vaults@2026-05-15' existing = {
  name: keyVaultName
}

resource toWorkspace 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  scope: kv
  name: 'to-log-analytics'
  properties: {
    workspaceId: workspaceId
    logAnalyticsDestinationType: 'Dedicated' // resource-specific tables, not AzureDiagnostics
    logs: [
      {
        categoryGroup: 'allLogs'
        enabled: true
      }
    ]
    metrics: [
      {
        category: 'AllMetrics'
        enabled: true
      }
    ]
  }
}

az monitor diagnostic-settings categories list --resource <id> shows what a resource offers. Diagnostic settings have only the 2021-05-01-preview API version.

SymptomCause → fix
"Metric category 'xxxx' is not supported"Use AllMetrics
The setting disappearsNon-ASCII characters in the resource ID (not supported): re-create the resource or move it to a group without them
No data yetAllow up to 90 minutes; the table appears with the first record. Nothing after 24 hours: the resource may emit no logs; disable and re-enable the setting
Duplicate Application Insights rowsIts diagnostic setting can't target the workspace the resource is based on

Data collection rules (DCRs)

A DCR says what to collect, how to transform it (KQL transformKql) and where to send it. Create it in the destination workspace's region. Limits per DCR: 10 data sources, 20 data flows and 20 streams, 10 workspaces, 15,360 characters per transformation.

Azure Monitor Agent (extension AzureMonitorLinuxAgent or AzureMonitorWindowsAgent, publisher Microsoft.Azure.Monitor) needs a managed identity on the VM: user-assigned for fleets, system-assigned for tests; Arc servers use system-assigned only. Collection starts when a data collection rule association links the machine to a DCR (many to many). In CLI, DCRs and associations come from the monitor-control-service extension (the monitor data-collection command group), not core. A data collection endpoint is needed only for private link, or for DCRs created before March 31, 2024. The old Log Analytics agent (MMA) and the Azure Diagnostics extension are retired: reference/monitor/what-to-use.md.

Logs Ingestion API (your own records)

It replaces the HTTP Data Collector API (end of support September 14, 2026). You need a custom table (_CL), a DCR whose stream matches what you send, and Monitoring Metrics Publisher on the DCR for the sender. A Direct DCR gets its own logsIngestion endpoint (an existing DCR can't gain one). Keep the DCR, workspace and any DCE in one region.

// A custom table and a Direct DCR for the Logs Ingestion API, with an app identity allowed to send
param location string = resourceGroup().location
param workspaceName string
param senderPrincipalId string // the app's managed identity (object ID)

resource ws 'Microsoft.OperationalInsights/workspaces@2026-03-01' existing = {
  name: workspaceName
}

resource table 'Microsoft.OperationalInsights/workspaces/tables@2026-03-01' = {
  parent: ws
  name: 'AuditEvents_CL' // custom tables must end in _CL
  properties: {
    schema: {
      name: 'AuditEvents_CL'
      columns: [
        { name: 'TimeGenerated', type: 'datetime' }
        { name: 'Computer', type: 'string' }
        { name: 'Action', type: 'string' }
        { name: 'Outcome', type: 'string' }
      ]
    }
  }
}

resource dcr 'Microsoft.Insights/dataCollectionRules@2024-03-11' = {
  name: 'dcr-audit-events'
  location: location // same region as the workspace
  kind: 'Direct' // gives the DCR its own logsIngestion endpoint
  properties: {
    streamDeclarations: {
      'Custom-AuditEvents': {
        columns: [
          { name: 'TimeGenerated', type: 'datetime' }
          { name: 'Computer', type: 'string' }
          { name: 'Action', type: 'string' }
          { name: 'Outcome', type: 'string' }
        ]
      }
    }
    destinations: {
      logAnalytics: [
        { name: 'ws', workspaceResourceId: ws.id }
      ]
    }
    dataFlows: [
      {
        streams: ['Custom-AuditEvents']
        destinations: ['ws']
        transformKql: 'source | where Action != "heartbeat"'
        outputStream: 'Custom-AuditEvents_CL'
      }
    ]
  }
  dependsOn: [table]
}

var monitoringMetricsPublisher = '3913510d-42f4-4e42-8a64-420c390055eb'

resource canSend 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  scope: dcr
  name: guid(dcr.id, senderPrincipalId, monitoringMetricsPublisher)
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', monitoringMetricsPublisher)
    principalId: senderPrincipalId
    principalType: 'ServicePrincipal'
  }
}

output immutableId string = dcr.properties.immutableId
output logsIngestionEndpoint string = dcr.properties.endpoints.logsIngestion
import { DefaultAzureCredential } from "@azure/identity";
import { LogsIngestionClient, isAggregateLogsUploadError } from "@azure/monitor-ingestion";

const credential = new DefaultAzureCredential();

// Needs Monitoring Metrics Publisher on the data collection rule.
export async function sendAuditRows(): Promise<void> {
  const ingest = new LogsIngestionClient(process.env.DCR_LOGS_INGESTION_ENDPOINT ?? "", credential); // the DCR's logsIngestion endpoint, or a DCE
  const rows = [{ TimeGenerated: new Date().toISOString(), Computer: "app-01", Action: "login", Outcome: "success" }];
  try {
    await ingest.upload(process.env.DCR_IMMUTABLE_ID ?? "", "Custom-AuditEvents", rows); // dcr-…, stream from streamDeclarations
  } catch (e) {
    if (isAggregateLogsUploadError(e)) {
      for (const f of e.errors) console.error(f.cause, f.failedLogs.length); // batches that failed after the SDK's retries
    }
    throw e;
  }
}
ErrorCause → fix
403The sender lacks Monitoring Metrics Publisher on the DCR, or the assignment is new: wait up to 30 minutes
413, or TimeoutExpired with ReadyBody_ClientConnectionAbortOver 1 MB per call: send smaller batches
429Over the per-DCR limits (2 GB per minute, 12,000 requests per minute): wait for Retry-After
No rowsThe first send to a new table can take up to 15 minutes
Log Analytics doesn't suggest the new tableIntelliSense can lag 24 hours; the query still works

The hub's reading: a stream or column mismatch between the payload, streamDeclarations and the table is the usual cause when calls succeed but columns stay empty.

Read this document as markdown — the same bytes an agent receives.