Azure Activity Logs: Who Did What and When in Your Subscription
Azure Activity Logs record every control plane operation in your Azure subscription — resource creation, deletion, role assignment changes, policy assignment updates, and anything else performed through the Azure Resource Manager API. They are the first place to look when investigating unexpected changes, security incidents, or compliance questions.
What activity logs capture
Every time something happens to an Azure resource at the management level — not inside the resource, but to the resource itself — an activity log entry is written. The types of events captured include:
- Resource creation: “Who created this virtual machine, and when?”
- Resource deletion: “Who deleted this resource group?”
- Resource modification: “Who changed the SKU of this App Service plan?”
- Role assignment changes: “Who was granted Owner on this subscription?”
- Policy assignment changes: “Who assigned this Azure Policy?”
- Secret writes to Key Vault: Only the metadata (who called
SetSecret), not the secret value. - Network security group rule changes: “Who added this inbound allow rule?”
- Subscription-level events: “Who registered this resource provider?”
Each entry records:
- operationName: The Azure Resource Manager operation that was called (e.g.,
Microsoft.Resources/resourceGroups/delete). - status: Whether the operation succeeded, failed, or was accepted.
- caller: The identity that performed the operation — a user UPN, a service principal ID, or a managed identity.
- eventTimestamp: When the operation occurred.
- resourceId: The full ARM resource ID of the affected resource.
- subscriptionId, resourceGroupName, resourceType, resourceName: Parsed components of the resource ID.
- correlationId: A GUID linking related operations (useful for multi-step deployments).
- httpRequest: Client IP address, method, and request URI.
What activity logs do NOT capture
Understanding the boundary of activity logs is as important as understanding what they capture. Activity logs record control plane operations only. They do not record:
- Data plane operations: Reading a blob from storage, writing a row to SQL, sending a message to a queue. These are operations on the data inside the resource, not on the resource itself.
- What happened inside a VM: File access, process execution, network connections from inside the OS.
- Application logs: HTTP requests to your App Service, function invocations, application errors.
- Guest OS metrics: CPU, memory, disk I/O from inside a VM.
- Key Vault secret values: The activity log records that someone called
GetSecret, but not the value returned. - Microsoft Entra ID sign-in and audit events: These go to Entra ID audit logs, not Azure Activity Logs.
For data plane auditing (who read which secret, who accessed which blob), see the log types in Azure page, which covers resource diagnostic logs that capture data plane events for specific services.
Querying activity logs with the Azure CLI
The az monitor activity-log list command retrieves activity log entries. The most useful filters are time range, caller identity, resource group, and operation name.
Basic query: recent activity in a subscription
# List all activity log events in the last 24 hours
az monitor activity-log list \
--start-time $(date -u -d '24 hours ago' '+%Y-%m-%dT%H:%M:%SZ') \
--output tableFilter by resource group
az monitor activity-log list \
--resource-group my-rg \
--start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--query "[].{Time:eventTimestamp, Caller:caller, Operation:operationName.value, Status:status.value}" \
--output tableFilter by operation type (deletions only)
# List all delete operations in the last 7 days
az monitor activity-log list \
--start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--query "[?contains(operationName.value, 'delete') || contains(operationName.value, 'Delete')].{Time:eventTimestamp, Caller:caller, Operation:operationName.value, Resource:resourceId}" \
--output tableReal-world query: who deleted a resource group in the last 7 days
This is a common incident response scenario. A resource group is gone and nobody admits to deleting it. The activity log has the answer.
# Find resource group deletion events in the last 7 days
az monitor activity-log list \
--start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--query "[?operationName.value=='Microsoft.Resources/resourceGroups/delete' && status.value=='Succeeded']" \
--output jsonThis returns JSON entries. The key fields to examine in each entry are:
caller: The identity that performed the deletion. This will be a user UPN (alice@contoso.com), a service principal object ID, or a managed identity identifier.eventTimestamp: The exact time the deletion completed (UTC).resourceGroupName: The name of the deleted resource group.httpRequest.clientIpAddress: The IP address from which the API call was made — useful for confirming whether it was from a known corporate IP, a CI/CD system, or an unexpected origin.correlationId: If the deletion was part of a larger automation run (Terraform destroy, deployment pipeline), multiple related operations will share this ID.
To narrow the search to a specific deleted resource group name (if you know the name):
# Find deletion of a specific resource group
az monitor activity-log list \
--start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--query "[?operationName.value=='Microsoft.Resources/resourceGroups/delete' && resourceGroupName=='my-deleted-rg' && status.value=='Succeeded'].{Time:eventTimestamp, Caller:caller, IP:httpRequest.clientIpAddress, CorrelationID:correlationId}" \
--output tableIf you see Succeeded in the status, the deletion completed. If you see Failed, someone attempted the deletion but was denied (perhaps by a lock or insufficient permissions) — that is also worth investigating.
If the caller field shows a GUID rather than a UPN, it is a service principal or managed identity. Run az ad sp show —id <GUID> to resolve the GUID to a service principal name, or az ad user show —id <GUID> to check if it is a user object ID.
Finding suspicious role assignment changes
Unexpected privilege escalation — someone granting themselves Owner or a highly privileged role — is a common attack pattern. Activity logs capture every role assignment creation and deletion.
# Find all role assignment creation events in the last 30 days
az monitor activity-log list \
--start-time $(date -u -d '30 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--query "[?operationName.value=='Microsoft.Authorization/roleAssignments/write' && status.value=='Succeeded'].{Time:eventTimestamp, Caller:caller, Resource:resourceId, IP:httpRequest.clientIpAddress}" \
--output tableLook for:
- Role assignments made outside business hours.
- Role assignments made by accounts that should not be making them (developers granting themselves Owner).
- Role assignments at subscription scope (the
resourceIdwill containMicrosoft.Authorization/roleAssignmentsat the subscription level). - Multiple role assignments in quick succession from the same caller (bulk privilege escalation).
For more advanced detection of suspicious patterns, including KQL queries in Log Analytics, see detecting suspicious activity in Azure.
Exporting activity logs to Log Analytics
The 90-day native retention limit means that for long-term compliance, historical investigation, or KQL-based alert rules, you need to export activity logs to a Log Analytics workspace.
# Get the Log Analytics workspace resource ID
WORKSPACE_ID=$(az monitor log-analytics workspace show \
--workspace-name my-workspace \
--resource-group my-rg \
--query id \
--output tsv)
# Get the subscription resource ID
SUBSCRIPTION_RESOURCE_ID="/subscriptions/$(az account show --query id --output tsv)"
# Create a diagnostic setting to export activity logs to Log Analytics
az monitor diagnostic-settings create \
--name "activity-logs-to-law" \
--resource "$SUBSCRIPTION_RESOURCE_ID" \
--workspace "$WORKSPACE_ID" \
--logs '[
{
"category": "Administrative",
"enabled": true
},
{
"category": "Security",
"enabled": true
},
{
"category": "Alert",
"enabled": true
},
{
"category": "Policy",
"enabled": true
}
]'Activity log categories available for export:
- Administrative: CRUD operations on all resources. This is the most important category.
- Security: Azure Security Center alerts.
- Alert: Azure Monitor alert activations.
- Autoscale: Autoscale engine actions.
- Policy: Policy evaluation results (compliance state changes).
- Recommendation: Advisor recommendations.
Once exported to Log Analytics, events are queryable using KQL in the AzureActivity table. For an overview of all Azure log types and where they go, see log types in Azure.
Using activity logs to investigate resource lock bypasses
Azure resource locks (ReadOnly and CanNotDelete) prevent accidental deletion. When a lock is deleted, the activity log captures it, giving you an audit trail if someone removed a lock before deleting a resource.
# Find lock deletions in the last 30 days
az monitor activity-log list \
--start-time $(date -u -d '30 days ago' '+%Y-%m-%dT%H:%M:%SZ') \
--query "[?operationName.value=='Microsoft.Authorization/locks/delete' && status.value=='Succeeded'].{Time:eventTimestamp, Caller:caller, Resource:resourceId}" \
--output tableIf you see a lock deletion immediately followed by a resource deletion (close timestamps, same caller), that is a pattern worth investigating. The correlationId may link the two events if they were part of the same automation run.
Common mistakes with activity log investigations
- Not setting up log export before an incident. The 90-day retention window means that if an incident is discovered three months after it happened, the evidence is gone. Set up export to Log Analytics or a Storage Account as part of every subscription’s baseline configuration, not reactively.
- Assuming the caller field is always a human. Many operations in Azure are performed by service principals, managed identities, and automation accounts. A GUID in the caller field does not mean an anonymous action — it means you need to look up the service principal. Most “suspicious” actions turn out to be from legitimate automation when investigated.
- Only looking at Succeeded events. Failed operations are also worth examining. A Failed
Microsoft.Authorization/roleAssignments/writeevent could indicate an attacker probing for privilege escalation opportunities. Review both succeeded and failed events when investigating an incident. - Querying too broad a time range via CLI without filtering. The CLI query returns all events in the time range, which can be thousands of entries for active subscriptions. Always add filters (resource group, operation name, caller) to narrow results. For large-scale analysis, use Log Analytics with KQL rather than the CLI.
Summary
- Activity logs capture every control plane operation in Azure — resource creation, deletion, modification, and RBAC changes.
- Default retention is 90 days; export to Log Analytics or Storage to keep logs longer.
- Key fields per log entry:
caller,eventTimestamp,operationName,status,resourceId, andhttpRequest.clientIpAddress. - Use
az monitor activity-log listwith filters for time range, operation name, and resource group to investigate incidents. - To find who deleted a resource group: filter on
operationName.value==‘Microsoft.Resources/resourceGroups/delete’andstatus.value==‘Succeeded’. - Activity logs do NOT capture data plane operations (reading blobs, querying databases, or what happens inside a VM).
Frequently asked questions
How long are activity logs retained by default and can I extend that?
Activity logs are retained for 90 days in the Azure Monitor activity log store. You cannot extend the native retention beyond 90 days. To keep logs longer, export them to a Log Analytics workspace (where you control the retention period, up to 730 days or more with archive tiers), a Storage Account (indefinite retention, pay per GB), or an Event Hub (for integration with SIEM tools). Set up a diagnostic setting on the subscription to configure this export.
Do activity logs capture what happened inside a virtual machine (file reads, process executions)?
No. Activity logs only capture control plane operations — actions taken against Azure resources through the Azure Resource Manager API. What happens inside a VM (file access, network connections, running processes) is captured by different systems: Azure Monitor VM Insights, Defender for Endpoint, or the Log Analytics agent collecting Windows Security Event Logs or Linux syslog. The activity log answers "who created this VM", not "what happened inside it".
Are activity logs available for all Azure subscriptions automatically?
Yes. Activity logs are enabled automatically for every Azure subscription at no extra cost. There is no agent to install and no configuration needed to start capturing events. The 90-day retention and the ability to view logs in the portal or query them via the CLI are available by default. Exporting to Log Analytics or Storage requires creating a diagnostic setting, but collection itself is always on.