How to export the Azure Activity Log and resource logs
Export the Azure Activity Log, Key Vault, storage and flow logs for an investigation: portal, az CLI, Log Analytics and storage exports, and the traps.
TL;DR. For an investigation, prefer JSON over the portal CSV. Use az monitor activity-log list with an explicit --start-time, --end-time and --max-events — the defaults are a 6-hour window and 50 events. Export the tenant-level (Directory Activity) log separately with az rest: elevateAccess is recorded there. If diagnostic settings already send logs to a storage account, copy the insights-* containers as-is. Keep originals untouched; never open and re-save them in Excel.
An Azure investigation is only as good as its export. I have seen more cases stall on a truncated CSV than on a clever attacker. This is the practical checklist, source by source, with what each format keeps and loses.
Pick the right source for each log
| Log | Where it lives | Best export for forensics | What you lose with the "easy" export |
|---|---|---|---|
| Activity Log (subscription) | Azure Monitor, 90 days | az monitor activity-log list JSON, or the storage / Log Analytics copy | Portal CSV: display names, no request bodies, less detail on the caller |
| Activity Log (tenant / Directory Activity) | Azure Monitor, tenant scope | az rest on /providers/Microsoft.Insights/eventtypes/management/values | Not in the subscription export at all |
Key Vault AuditEvent | Only where a diagnostic setting sends it | Storage container insights-logs-auditevent, or AzureDiagnostics / AZKVAuditLogs | — (nothing exists if logging was off) |
| Storage blob logs | Only where a diagnostic setting sends them | insights-logs-storageread / storagewrite / storagedelete, or StorageBlobLogs | — |
| NSG / VNet flow logs | Storage account chosen in Network Watcher | insights-logs-networksecuritygroupflowevent, insights-logs-flowlogflowevent | Traffic analytics summaries lose individual tuples |
| Defender for Cloud alerts | Defender for Cloud | REST Microsoft.Security/alerts JSON, or SecurityAlert table | Portal CSV is fine for a first look |
Activity Log with the Azure CLI
The CLI returns the REST schema: claims, httpRequest.clientIpAddress, properties.requestbody when present. That is what you want.
az login
az account set --subscription <subscription-id>
az monitor activity-log list \
--start-time 2026-08-01T00:00:00Z \
--end-time 2026-09-15T00:00:00Z \
--max-events 100000 \
> activity-<subscription-id>.json
Two defaults deserve a warning. According to the az monitor activity-log reference, --max-events defaults to 50 and --offset to 6h. Run the command without them and you get a neat, valid JSON file containing almost nothing. Always check the first and last eventTimestamp of the output against the window you asked for.
Repeat per subscription. The CLI lists newest first; order does not matter to the analyzer, which sorts and de-duplicates.
The tenant-level log
Some of the most important events are not in any subscription log. Elevate access, for example, is recorded at tenant scope. Microsoft documents retrieving it with az rest against the tenant-level endpoint (Elevate access log entries):
az rest --method get \
--url "https://management.azure.com/providers/Microsoft.Insights/eventtypes/management/values?api-version=2015-04-01&\$filter=eventTimestamp ge '2026-08-01T00:00:00Z'" \
> activity-tenant.json
The same page notes that elevate-access entries also appear in the Microsoft Entra audit logs (a preview capability at the time of writing).
Activity Log from the portal
Monitor → Activity log, set Timespan to the incident window, remove the default filters, then Download as CSV. Microsoft warns that large exports are slow and suggests narrowing the timespan (Export the activity log to a file). The CSV is useful as a quick look but loses detail compared with JSON; the analyzer reads it on a best-effort basis, matching display names.
Log Analytics exports
If diagnostic settings already route logs into a workspace, it is the most convenient source because retention can be much longer than 90 days.
AzureActivity
| where TimeGenerated between (datetime(2026-08-01) .. datetime(2026-09-15))
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where TimeGenerated between (datetime(2026-08-01) .. datetime(2026-09-15))
StorageBlobLogs
| where TimeGenerated between (datetime(2026-08-01) .. datetime(2026-09-15))
Export all columns as CSV, or use az monitor log-analytics query -w <workspace-id> --analytics-query "…" > out.json. Microsoft notes that values in AzureActivity can differ in case, so use case-insensitive comparisons if you filter in KQL. The analyzer's matching is case-insensitive.
Storage-account exports (diagnostic settings)
A diagnostic setting that archives to a storage account writes one blob per hour, in a documented tree. For the Activity Log:
insights-activity-logs/resourceId=/SUBSCRIPTIONS/{sub}/y=2026/m=09/d=14/h=02/m=00/PT1H.json
Resource logs follow the same pattern under their own container, for example insights-logs-auditevent for Key Vault (Key Vault logging). Copy whole containers with Azure Storage Explorer or AzCopy:
azcopy copy "https://<account>.blob.core.windows.net/insights-activity-logs?<SAS>" ./export --recursive
Keep the folder structure. The analyzer reads the resourceId=/…/PT1H.json tree directly, from a folder or a ZIP. If the same day exists in both the storage export and a CLI export, duplicates are merged.
One caution when you are in the victim subscription: the SAS you create for the copy is itself an event (listAccountSas or listServiceSas if generated through ARM). Note the time and your IP so you do not investigate yourself later.
Key Vault and storage resource logs
Both are opt-in. Key Vault → Diagnostic settings must send the AuditEvent category somewhere; storage accounts need StorageRead, StorageWrite and StorageDelete on the blob sub-resource. Microsoft says Key Vault logs are available at most ten minutes after the operation.
If a diagnostic setting did not exist before the incident, enabling it now does not recover past reads. Enable it anyway for what comes next, and say so in your report. The Key Vault article and the storage article explain what these logs prove.
NSG and VNet flow logs
Network Watcher → Flow logs shows which storage account receives them. Download only the relevant hours and subnets; flow logs are large. NSG flow logs write to insights-logs-networksecuritygroupflowevent, VNet flow logs to insights-logs-flowlogflowevent. Microsoft has announced that NSG flow logs retire on September 30, 2027 and no longer supports creating new ones; VNet flow logs are the replacement. The flow logs article covers the tuple formats.
Defender for Cloud alerts
Either Security alerts → select → Download CSV report, or the REST API:
az rest --method get \
--url "https://management.azure.com/subscriptions/<id>/providers/Microsoft.Security/alerts?api-version=2022-01-01" \
> defender-alerts.json
Formats the analyzer reads (and does not)
The Azure Forensics analyzer accepts JSON Lines, {"records": […]} wrappers, JSON arrays, CSV with quoted newlines, .gz files, folders and ZIPs. It explains every skipped file. Two formats are not supported: Avro (Event Hubs Capture) and Parquet — export JSON or CSV instead. UTF-16 files (sometimes produced by PowerShell redirection on Windows) must be re-saved as UTF-8.
Chain of custody, briefly
- Hash every exported file (
sha256sum/Get-FileHash) and record who exported it, when, from which account. - Never edit the originals. Work on copies.
- Excel silently rewrites timestamps and long numbers. Do not open-and-save a log CSV.
- Record the time range and filters of every export: "no events" means nothing if the filter was wrong.