Symptom
You open Log Analytics to investigate an incident and the logs for a Key Vault, SQL database, App Gateway or storage account aren't there. Or they are there twice, and the ingestion bill doubled. Or the team's saved queries return nothing because the data is going to AzureDiagnostics while the queries read a resource-specific table like AGWAccessLogs.
All three come down to the diagnostic setting on the resource. Platform logs are not collected by default; each resource needs a setting that names the categories and the destination.
Quick checks
| Symptom | Likely cause | Where to look |
|---|---|---|
| No logs at all for the resource | No diagnostic setting, or setting points at another workspace | az monitor diagnostic-settings list |
| Some categories missing | Setting lists categories one by one and misses new ones | Compare with categories list |
| Every row appears twice | Two settings send the same category to the same workspace, or two workspaces are unioned | Count settings, check _ResourceId and TenantId |
Data in AzureDiagnostics, queries expect a dedicated table | Setting uses Azure diagnostics mode | logAnalyticsDestinationType on the setting |
Columns missing in AzureDiagnostics | 500-column limit reached, extra fields moved to AdditionalFields | Parse AdditionalFields or switch to resource-specific |
| Logs stopped after resource was recreated | Old setting reapplied or removed by deployment | Activity Log for diagnosticSettings writes |
| Logs late by minutes | Normal ingestion latency | ingestion_time() vs TimeGenerated |
Step 1: see what the resource is actually sending
RID=$(az keyvault show -n kv-prod-weu --query id -o tsv)
# Settings on the resource
az monitor diagnostic-settings list --resource "$RID" \
--query "[].{name:name, workspace:workspaceId, mode:logAnalyticsDestinationType, logs:logs[?enabled].[category, categoryGroup]}" -o jsonc
# Categories the resource supports
az monitor diagnostic-settings categories list --resource "$RID" \
--query "[].{name:name, type:categoryType}" -o table
What to look for:
- Empty list: nothing is configured. Go to step 3.
workspaceIdis a different workspace than the one you are querying. This is common after a workspace consolidation.modeis null: data goes toAzureDiagnostics.Dedicatedmeans resource-specific tables.- Categories listed individually: any category the service added later is not collected. Category groups (
allLogs,audit) pick up new categories automatically.
Step 2: check what arrives in the workspace
// Resource-specific and AzureDiagnostics data for one resource in the last 24 hours
union withsource=TableName *
| where TimeGenerated > ago(24h)
| where _ResourceId has "/vaults/kv-prod-weu"
| summarize rows = count(), last = max(TimeGenerated) by TableName
| order by rows desc
// What is landing in AzureDiagnostics, by provider and category
AzureDiagnostics
| where TimeGenerated > ago(24h)
| summarize rows = count(), last = max(TimeGenerated) by ResourceProvider, Category, Resource
| order by rows desc
// Duplicate check: same record ingested more than once
AzureDiagnostics
| where TimeGenerated > ago(1h) and Resource =~ "KV-PROD-WEU"
| summarize copies = count() by CorrelationId, OperationName, TimeGenerated
| where copies > 1
| take 20
The union * query scans every table, so keep the time range short. Use _ResourceId rather than resource name; names are upper-cased in AzureDiagnostics and _ResourceId works across tables.
Latency check, so you don't chase data that is just late:
AzureDiagnostics
| where TimeGenerated > ago(2h)
| extend delayMin = datetime_diff('minute', ingestion_time(), TimeGenerated)
| summarize avg(delayMin), percentile(delayMin, 95) by ResourceProvider
Step 3: create or fix the setting
One setting per resource per destination, category groups instead of lists, resource-specific tables where the service supports them:
WS=$(az monitor log-analytics workspace show -g rg-monitor -n law-prod-weu --query id -o tsv)
az monitor diagnostic-settings create \
--name diag-to-law-prod \
--resource "$RID" \
--workspace "$WS" \
--export-to-resource-specific true \
--logs '[{"categoryGroup":"allLogs","enabled":true}]' \
--metrics '[{"category":"AllMetrics","enabled":true}]'
Remove the duplicate or stale setting once the new one is confirmed:
az monitor diagnostic-settings delete --name old-diag-setting --resource "$RID"
Warning
Switching an existing setting from
AzureDiagnosticsto resource-specific tables changes where new data lands. Saved queries, workbooks, alert rules and SIEM content that readAzureDiagnosticsstop matching new rows. Inventory those before you switch, and run both for a short overlap if needed, accepting the double ingestion cost for that window.
Not every service supports resource-specific mode. If --export-to-resource-specific true has no effect and data still lands in AzureDiagnostics, the service doesn't offer dedicated tables yet; check the service's monitoring reference.
The Activity Log is a subscription-level setting
The Activity Log isn't covered by resource settings:
az monitor diagnostic-settings subscription list -o table
az monitor diagnostic-settings subscription create \
--name activity-to-law \
--workspace "$WS" \
--logs '[{"category":"Administrative","enabled":true},{"category":"Security","enabled":true},{"category":"ServiceHealth","enabled":true},{"category":"Alert","enabled":true},{"category":"Policy","enabled":true}]'
Check the exact parameters with az monitor diagnostic-settings subscription create --help; depending on your CLI version it may also ask for --location.
Errors you may hit
| Error or behaviour | Cause | Fix |
|---|---|---|
| Setting can't be created, limit reached | A resource allows a small fixed number of settings (five at the time of writing) | Consolidate; one setting can send to a workspace, storage and Event Hubs together |
| Rejected because the category already goes to that destination | Two settings sending the same category to the same sink | Remove the category from one of them |
| Storage destination accepted, no blobs written | Storage firewall blocks Azure Monitor | Allow trusted Microsoft services on the storage account |
| Storage or Event Hubs destination rejected | Destination in a different region from the resource | Use a destination in the same region; Log Analytics workspaces can be in any region |
MissingSubscriptionRegistration | Microsoft.Insights provider not registered | az provider register --namespace Microsoft.Insights |
| Workspace in another tenant not selectable | Cross-tenant requires Azure Lighthouse delegation | Set up Lighthouse or use a workspace in the same tenant |
Find resources with no diagnostic setting
Resource Graph doesn't index diagnostic settings, so either check Azure Policy compliance (below) or loop through a resource type:
for id in $(az resource list --resource-type Microsoft.KeyVault/vaults --query "[].id" -o tsv); do
n=$(az monitor diagnostic-settings list --resource "$id" --query "length(@)" -o tsv)
[ "$n" = "0" ] && echo "NO DIAG: $id"
done
Get-AzResource -ResourceType Microsoft.Sql/servers/databases | ForEach-Object {
if (-not (Get-AzDiagnosticSetting -ResourceId $_.ResourceId -ErrorAction SilentlyContinue)) {
"NO DIAG: $($_.ResourceId)"
}
}
Prevent it next time
- Assign the built-in Azure Policy initiatives that deploy
allLogs(oraudit) category group settings to a Log Analytics workspace, withdeployIfNotExistsand a remediation task for existing resources. Names vary as Microsoft updates them; search the policy definitions for "category group". - Use category groups, not category lists, so new categories are picked up.
- Standardise one setting name per destination (for example
diag-to-law-prod) so duplicates are easy to spot. - Prefer resource-specific tables for new deployments; they are easier to query and support table plans for cost control.
- Add a daily query that compares expected resources against
_ResourceIdvalues seen in the last 24 hours, and alert on gaps. - When a resource is deleted and recreated with the same name, check the diagnostic setting again; earlier settings can be reapplied to the new resource.