Azure Logs: Breaking Through the Cloud Cover
Hear Ye, Hear Ye
Subscribe to Cloud Chronicles for the latest in cloud security!
Permiso consistently observes that engineers and analysts often struggle with interpreting Azure Monitor Activity Logs, facing confusion and achieving only a partial understanding even after gaining experience. To address this, Permiso aims to level the playing field, offering deeper and more practical insights into these logs. In this blog, you’ll find an invaluable reference tool and guide designed to demystify Azure’s logging complexities.
Here’s what we’ll be exploring:
- `In-Depth Primer on Azure Log Structure
- We’ll start with a comprehensive primer on the log structure of Azure Monitor’s Administrative Activity Logs. While we will revisit information available in Microsoft’s documentation, we will be adding a layer of analysis and insights.
- We’ll highlight Microsoft’s redundancy in log entries, pointing out how it can both aid but largely complicate log analysis.
- 2. Key Log Fields to Focus On:
- A callout of the most critical fields in Azure logs will be provided, helping to reduce looking at redundancy and focus on the necessary. We’ll explain why these fields are important, how to interpret them correctly, and what pitfalls to avoid. This part of the discussion will help you quickly identify the most pertinent information in a log entry.
- Navigating Microsoft’s Complex Log Structure:
- One of the unique challenges with Azure logs is their complexity and the nuances in their structure. We’ll delve into why understanding and using
correlationId
andoperationId
is crucial for accurately sequencing and making sense of operations within Azure’s environment. - We’ll also discuss how to effectively gain the necessary metadata from operations and the need to look across all logs that make up a single operation.
- One of the unique challenges with Azure logs is their complexity and the nuances in their structure. We’ll delve into why understanding and using
Azure Monitor Activity logs
Azure Monitor Activity logs (referred to going forward as “activity logs”), are similar to the management plane logs available in AWS CloudTrail. Activity logs are themselves management plane actions taken on Azure resources as viewed at the subscription layer. As Microsoft states, “Use the Activity log to determine what, who, and when for any write operation (PUT, POST, DELETE) executed on the resources in your subscription.” If you take a moment and read into this, you will likely notice the reference to “write operation”. Note that Microsoft does not log read level events, for this reason you won’t find logs of someone reading a key in Key Vault or viewing settings across resources.
You might be thinking at this point, “Wow, they’re nuts.”, but really go look, we’ll wait. Using the Key Vault example, a secret and the ability to view it in the portal is a client-side button and Microsoft themselves will make sure to show you a message on the Key Vault overview for “Enable logging to monitor how, when and by whom your key vaults are accessed.” That final note from Microsoft refers to Azure Monitor Resource logs, data plane logs at the resource layer. You will see certain events generated by Microsoft, in part from authentication such as when systems that use a Storage Account list out storage account keys, but not much more. If you want more proof or understanding of how to access those deeper resource logs, consult this Key Vault documentation. Note that this is consistent across Microsoft Azure services.
💡 A quick aside, as we mentioned that the activity logs are considered by Microsoft to be at the subscription layer, that’s not entirely true and most toolsdon’t pick up on this. If you want to review and track edits, additions, deletions of management groups, even with a scope at the management root group,you're unlikely to see these logs unless you pull them from each management group's activity log.
Primer: Log Structure
There are various categories of activity logs, each with their own varying schema. You can find each with samples here: Activity Log Category Schemas. As the most common category reviewed is “Administrative”, this will be our focus.
You can review many of the fields in the log in the previously linked Microsoft Learn doc. We will ensure referenced links are summarized at the end of the blog as well. If you're short on time, we recommend focusing on the starred (⭐) fields to minimize redundancy. Below, we've included a sample log for review.
-
authorization: “Blob of Azure RBAC properties of the event. Usually includes the “action”, “role” and “scope” properties.”
Repeat References
Action:
- operationName.value
- properties.message
Scope
Part of: httpRequest.url Part of: id resourceId propeties.entity
-
⭐ caller: The acting/source identity.
-
channels: Operation or Admin. Admin will most commonly come from service health events.
-
⭐ claims: “The JWT token used by Active Directory to authenticate the user or application to perform this operation in Resource Manager.”
-
appid: “The application ID of the client using the token. The application can act as itself or on behalf of a user.”
-
appidacr: “Indicates authentication method of the client. For a public client, the value is
0
. When you use the client ID and client secret, the value is1
. When you use a client certificate for authentication, the value is2
.” -
idtyp: “This claim is the most accurate way for an API to determine if a token is an app token or an app+user token.”
-
uti: “Token identifier claim, equivalent to
jti
in the JWT specification. Unique, per-token identifier that is case-sensitive.”
💡 An important piece of information as it’s unique per-token identifier. This could be used to monitor
against replay attacks, for example.
-
authnmethodsreferences: “Identifies the authentication method of the subject of the token.” A list of the possible methods is included in the Access Token Claims documentation.
-
ipaddr: Client source authentication IP.
💡The existence of this claim is not consistent, we recommend a coalesce-style function to be used
along
with
the httpRequest.clientIpAddress
field.Repeat References
httpRequest.clientIpAddress
-
-
⭐ correlationId: “Usually a GUID in the string format. Events that share a
correlationId
belong to the same uber action.”
💡In simple terms, this is Microsoft’s primary grouping mechanism, typically per acting identity and type of operation. correlationId is a key piece of data. See
operationId to continue grouping.
-
description: “Static text description of an event.”
-
⭐ eventDataId: The real id of the event. Don’t use
id
.
Repeat References
Part ofid
-
⭐ eventName: Friendly name of the type of event. We were unsure why Microsoft thought to name this “friendly”.
💡 Most of the time the value will be along the lines of “BeginRequest” and “EndRequest”, which can help when ordering and grouping the logs of a single
operation. When eventName is “EndRequest”, this should be the only time the subStatus field has a value.
-
⭐ category: We mentioned category previously as we review logs from the “Administrative” category, this field separates the types of activity logs.
Repeat References
properties.eventCategory
-
httpRequest: Describes the HTTP request. (⭐ clientIpAddress)
💡 The existence of this field is not consistent, we recommend a coalesce-style function to be used along with the
claims.ipaddr
field.
Repeat References
clientIpAddress:
- claims.ipaddr
uri
Part of: authorization.scope Part of: id- Part of: resourceId
- Part of: properties.entity
- id: “Unique resource identifier of the security event.” Or in simpler terms, a confusingly named field combining two ids.
Repeat References
Part of: eventDataId Part of: resourceID
-
⭐ level: A severity level (Informational, Warning, Error, Critical) to describe the event. Most administrative logs are Informational.
-
⭐ resourceGroupName: Resource group of the affected resource.
-
⭐ resourceProviderName: Resource provider or service of the affected resource.
-
⭐ resourceId: Id of the affected resource.
- ⭐ resourceType: The type of resource affected.
Repeat References
Resource fields repeat often, these are the most direct references:
resourceId
- authorization.scope
- properties.entity
resourceType
Start of: authorization.scope Start of: operationName Start of: properties.message
-
⭐ operationId: “A GUID shared among the events that correspond to a single operation.”
💡 This is Microsoft’s secondary grouping mechanism. operationId breaks down correlationId when Microsoft groups operations.
-
⭐ operationName: The operationName.value is the URL based name of the operation. The operationName.localizedValue is the friendly name.
Repeat References
operationName.value
- authorization.action
- properties.message
-
⭐ properties: Describes the details of the operation. You will likely find important metadata as various key/value pairs as part of properties. Unfortunately as Microsoft breaks a single operation into multiple logs, properties are not consistent between those logs.
Repeat References
properties.eventCategory:
- category
properties.entity
authorization.scope resourceId properties.message
authorization.action operationName.value properties.statusCode:
subStatus.value
-
⭐ status: “String describing the status of the operation. Some common values are: Started, In Progress, Succeeded, Failed, Active, Resolved.”
-
⭐ subStatus: “Usually the HTTP status code of the corresponding REST call, but can also include other strings describing a subStatus…”
💡 These status codes can describe what is occurring within an operation. For example, you may have an
operationName
such as
“Create/Update Storage Account” but a subStatus of “OK” or “Created” (we will look the other way on the laziness that is writing a single event for both
Create & Update). “Created” in this instance represents when the resource is created and “OK” represents an Update.
-
⭐ eventTimestamp: “Timestamp when the event was generated by the Azure service processing the request corresponding the event.” This is the timestamp you use to reference the event.
-
submissionTimestamp: “Timestamp when the event became available for querying.”
-
⭐ subscriptionId: Id of the Azure subscription.
-
{
"authorization": {
"action": "Microsoft.Storage/storageAccounts/blobServices/containers/delete",
"scope": "/subscriptions/230fbe8e/resourceGroups/rg-Example/providers/Microsoft.Storage/storageAccounts/cloudsecurityteststeststorage/blobServices/default/containers/test"
},
"caller": "nathan.eades@cloudsecuritytests.com",
"channels": "Operation",
"claims": {
"aud": "<https://management.core.windows.net/>",
"iss": "<https://sts.windows.net/646f3b8e/>",
"iat": "1698706365",
"nbf": "1698706365",
"exp": "1698710529",
"aio": "AVQAq/8UAAAAc3CpkVytEcGUtCPZlsDucXhFQExqbdt/oHcX7UDIpk5zqZlK7o8EYXKl5a2HWWvlYwZQpDxuHE8lmvU/uPnMDBZhk4fWvlAmKQ63nStwMFU=",
"appid": "c44b4083-3bb0-49c1-b47d-974e53cbdf3c",
"appidacr": "2",
"groups": "37cdbc6e-2057-40f6-bb59-9d19c3876793,967504ec-5027-4fe3-8b10-108f76962d66,60f48621-0fe6-4794-a96a-8b32f7db50b6,c7071c53-7bae-4cdb-a3ba-34b7f84fff4a,b924f26f-8ce1-4371-905d-78d767cbe0dd",
"idtyp": "user",
"<http://schemas.microsoft.com/identity/claims/objectidentifier>": "bcd83aa2-84c9-422e-9ff6-b1fcef6fc783",
"rh": "0.AX0AjjtvZL4RmkW3Fa75NSUXyEZIf3kAutdPukPawfj2MBN9AOg.",
"<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier>": "MC7_bJ6mc40Sy2cuD1pEu_tzN0cRVRNb42Njig2Y8oY",
"<http://schemas.microsoft.com/identity/claims/tenantid>": "646f3b8e",
"uti": "2gpCthjjiU6ry221VsYQAA",
"ver": "1.0",
"xms_cae": "1",
"xms_tcdt": "1620435213",
"<http://schemas.microsoft.com/claims/authnclassreference>": "1",
"<http://schemas.microsoft.com/claims/authnmethodsreferences>": "pwd,mfa",
"<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname>": "Eades",
"<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname>": "Nathan",
"ipaddr": "65.191.130.5",
"name": "Nathan Eades",
"puid": "100320013DD959E5",
"<http://schemas.microsoft.com/identity/claims/scope>": "user_impersonation",
"<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name>": "nathan.eades@cloudsecuritytests.com",
"<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn>": "nathan.eades@cloudsecuritytests.com",
"wids": "cf1c38e5-3621-4004-a7cb-879624dced7c,62e90394-69f5-4237-9190-012177145e10"
},
"correlationId": "d03fbd1d-b921-4bb1-ac8c-eb0ab93f8a67",
"description": "",
"eventDataId": "e25ae69b-52a3-4347-9989-8b7a30caf861",
"eventName": {
"value": "EndRequest",
"localizedValue": "End request"
},
"category": {
"value": "Administrative",
"localizedValue": "Administrative"
},
"httpRequest": {
"clientRequestId": "6ecc6617-e01a-463e-875a-72df2dd7605a",
"clientIpAddress": "65.191.130.5",
"method": "DELETE",
"uri": "<https://management.azure.com/subscriptions/230fbe8e/resourceGroups/rg-Example/providers/Microsoft.Storage/storageAccounts/cloudsecurityteststeststorage/blobServices/default/containers/test?api-version=2022-05-01&_1698708436631>"
},
"id": "/subscriptions/230fbe8e/resourceGroups/rg-Example/providers/Microsoft.Storage/storageAccounts/cloudsecurityteststeststorage/blobServices/default/containers/test/events/e25ae69b-52a3-4347-9989-8b7a30caf861/ticks/638343052370210144",
"level": "Informational",
"resourceGroupName": "rg-Example",
"resourceProviderName": {
"value": "Microsoft.Storage",
"localizedValue": "Microsoft.Storage"
},
"resourceId": "/subscriptions/230fbe8e/resourceGroups/rg-Example/providers/Microsoft.Storage/storageAccounts/cloudsecurityteststeststorage/blobServices/default/containers/test",
"resourceType": {
"value": "Microsoft.Storage/storageAccounts/blobServices/containers",
"localizedValue": "Microsoft.Storage/storageAccounts/blobServices/containers"
},
"operationId": "c2fda3bd-4a8c-41e4-8620-707305e5f8a2",
"operationName": {
"value": "Microsoft.Storage/storageAccounts/blobServices/containers/delete",
"localizedValue": "Delete blob container"
},
"properties": {
"eventCategory": "Administrative",
"entity": "/subscriptions/230fbe8e/resourceGroups/rg-Example/providers/Microsoft.Storage/storageAccounts/cloudsecurityteststeststorage/blobServices/default/containers/test",
"message": "Microsoft.Storage/storageAccounts/blobServices/containers/delete",
"hierarchy": "646f3b8e/230fbe8e",
"statusCode": "OK",
"serviceRequestId": null
},
"status": {
"value": "Succeeded",
"localizedValue": "Succeeded"
},
"subStatus": {
"value": "OK",
"localizedValue": "OK (HTTP Status Code: 200)"
},
"eventTimestamp": "2023-10-30T23:27:17.0210144Z",
"submissionTimestamp": "2023-10-30T23:29:04.0000000Z",
"subscriptionId": "230fbe8e",
}
Complexities
An understanding of the log structure is necessary to begin to breakdown the complexity Microsoft introduces. Especially for users accustomed to AWS CloudTrail. While AWS rolls up events, such as a bucket being created, into a single log. Microsoft will provide two or more logs for the same overall operationName
. For example, you might have Started, Accepted, and Succeeded status logs, each being an individual log but part of a single operation.
And so it begins. The above example in particular, is straight forward, as we can get to the container name (a few times over due to Microsoft redundancy), we know the operation itself and we know the status was successful.
Here is a snippet of another log (removed redundancies):
-
{
"caller": "nathan.eades@cloudsecuritytests.com",
"category": {
"value": "Administrative"
},
"claims": {
"appid": "c44b4083-3bb0-49c1-b47d-974e53cbdf3c",
"appidacr": "2",
"http://schemas.microsoft.com/claims/authnmethodsreferences": "pwd,mfa",
"http://schemas.microsoft.com/identity/claims/scope": "user_impersonation",
"http://schemas.microsoft.com/identity/claims/tenantid": "240559de",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "nathan.eades@cloudsecuritytests.com",
"idtyp": "user",
"ipaddr": "65.191.130.5"
},
"correlationId": "d064d67e-05d4-4a80-8729-1eb13ade9499",
"eventDataId": "ebcf3336-d9c7-4014-99da-96ec399f4a7d",
"eventName": {
"value": "EndRequest"
},
"eventTimestamp": "2023-10-26T20:44:16.6633063Z",
"httpRequest": {
"clientIpAddress": "65.191.130.5"
},
"operationId": "bd0ebefc-a11f-47a1-99f7-5cf183eade5b",
"operationName": {
"localizedValue": "Create role assignment",
"value": "Microsoft.Authorization/roleAssignments/write"
},
"properties": {
"entity": "/subscriptions/5fc5ae1e/resourceGroups/rg-threatResearch-adx/providers/Microsoft.Logic/workflows/prodAdxArbiterGenericBaselines/providers/Microsoft.Authorization/roleAssignments/b40160a5-1490-47ac-b555-b1670e2990eb",
"eventCategory": "Administrative",
"hierarchy": "240559de/5fc5ae1e",
"message": "Microsoft.Authorization/roleAssignments/write",
"statusCode": "Created"
},
"resourceGroupName": "rg-threatResearch-adx",
"resourceId": "/subscriptions/5fc5ae1e/resourceGroups/rg-threatResearch-adx/providers/Microsoft.Logic/workflows/prodAdxArbiterGenericBaselines/providers/Microsoft.Authorization/roleAssignments/b40160a5-1490-47ac-b555-b1670e2990eb",
"resourceProviderName": {
"value": "Microsoft.Authorization"
},
"resourceType": {
"value": "Microsoft.Authorization/roleAssignments"
},
"status": {
"localizedValue": "Succeeded",
"value": "Succeeded"
},
"submissionTimestamp": "2023-10-26T20:45:57.0000000Z",
"subscriptionId": "5fc5ae1e",
"subStatus": {
"localizedValue": "Created (HTTP Status Code: 201)",
"value": "Created"
}
}
A role was assigned to a user and it was successful, sweet. Anyone want to let us know what role was added, and to which user? Sure, you could say someone was assigned an admin role, maybe you even want to alert off of this. However, if we reviewed an alert that comes through without mention of the type of role and target user, we wouldn’t be happy.
So what now? Do we just settle for 'good enough'? At Permiso, we take a deeper dive. Let's group logs by correlationId
and status
to uncover the full story. We will find that by grouping by correlationId, we have “Started” and “Succeeded” status
logs for this operation, potentially with another status in between (based on eventTimestamp
). Let's examine the “Started” status
event.
-
{
"caller": "nathan.eades@cloudsecuritytests.com",
"category": {
"value": "Administrative"
},
"claims": {
"appid": "c44b4083-3bb0-49c1-b47d-974e53cbdf3c",
"appidacr": "2",
"http://schemas.microsoft.com/claims/authnclassreference": "1",
"http://schemas.microsoft.com/claims/authnmethodsreferences": "pwd,mfa",
"idtyp": "user",
"ipaddr": "65.191.130.5",
"iss": "https://sts.windows.net/240559de/",
"name": "Nathan Eades"
},
"correlationId": "d064d67e-05d4-4a80-8729-1eb13ade9499",
"eventDataId": "c617b29d-dc0a-473a-b008-e6d4f74044b7",
"eventName": {
"value": "BeginRequest"
},
"eventTimestamp": "2023-10-26T20:44:14.1161615Z",
"httpRequest": {
"clientIpAddress": "65.191.130.5"
},
"operationId": "bd0ebefc-a11f-47a1-99f7-5cf183eade5b",
"operationName": {
"localizedValue": "Create role assignment",
"value": "Microsoft.Authorization/roleAssignments/write"
},
"properties": {
"entity": "/subscriptions/5fc5ae1e/resourceGroups/rg-threatResearch-adx/providers/Microsoft.Logic/workflows/prodAdxArbiterGenericBaselines/providers/Microsoft.Authorization/roleAssignments/b40160a5-1490-47ac-b555-b1670e2990eb",
"eventCategory": "Administrative",
"hierarchy": "240559de/5fc5ae1e",
"message": "Microsoft.Authorization/roleAssignments/write",
"requestbody": "{\"Id\":\"b40160a5-1490-47ac-b555-b1670e2990eb\",\"Properties\":{\"PrincipalId\":\"a5551dd8-961a-407d-9ad1-6cf30e11fa8c\",\"PrincipalType\":\"User\",\"RoleDefinitionId\":\"/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e\",\"Scope\":\"/subscriptions/5fc5ae1e/resourceGroups/rg-threatResearch-adx/providers/Microsoft.Logic/workflows/prodAdxArbiterGenericBaselines\",\"Condition\":null,\"ConditionVersion\":null}}"
},
"resourceGroupName": "rg-threatResearch-adx",
"resourceId": "/subscriptions/5fc5ae1e/resourceGroups/rg-threatResearch-adx/providers/Microsoft.Logic/workflows/prodAdxArbiterGenericBaselines/providers/Microsoft.Authorization/roleAssignments/b40160a5-1490-47ac-b555-b1670e2990eb",
"resourceProviderName": {
"localizedValue": "Microsoft.Authorization",
"value": "Microsoft.Authorization"
},
"resourceType": {
"value": "Microsoft.Authorization/roleAssignments"
},
"status": {
"localizedValue": "Started",
"value": "Started"
},
"submissionTimestamp": "2023-10-26T20:45:57.0000000Z",
"subscriptionId": "5fc5ae1e",
"subStatus": {
"localizedValue": "",
"value": ""
}
}
And there we have our missing puzzle pieces, albeit not without some extra work. Tucked into properties in this case we can see that there is now a poorly formatted, escape character laden properties.requestbody
that comes complete with the user principal Id (a5551dd8-961a-407d-9ad1-6cf30e11fa8c) of the target identity and the role Id (87a39d53-fc1b-424a-814c-f7e04687dc9e) to be assigned. For simplicity, here is the formatted version of properties.requestbody
:
-
{
"requestbody": {
"Id": "b40160a5-1490-47ac-b555-b1670e2990eb",
"Properties": {
"PrincipalId": "a5551dd8-961a-407d-9ad1-6cf30e11fa8c",
"PrincipalType": "User",
"RoleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e",
"Scope": "/subscriptions/5fc5ae1e/resourceGroups/rg-threatResearch-adx/providers/Microsoft.Logic/workflows/prodAdxArbiterGenericBaselines",
"Condition": null,
"ConditionVersion": null
}
}
}
What does this mean for building detections and fully understanding a single complete operation? In short, you need to examine several logs to get the full picture. In this case, we require the “Started” status
log to determine the finer details, but also the final log to ascertain the outcome, as this sequence could have just as easily ended in failure.
There is also no consistency. Here we wanted requestbody
, however, the next operation may be another field depending on what information we want.
All about the correlationId?
Grouping the logs by correlationId for a specific operation would be ideal if it solved all aggregation requirements. Unfortunately, even with this grouping by correlationId, we don’t always get a clear breakdown of an operation from start to finish. Instead, you might get a few similar operations if ran at or around the same time as part of an operation by the identity. Great, now what, we call up Microsoft and ask them to log better? We’re game, but in the mean time let’s review the operationId
.
Continuing to use our last example of the “Create role assignment” operationName
. What happens if an admin assigns the same role to multiple identities at once? Microsoft will not group the principal IDs into the same log. Instead you will see a log per principal or to be clear, a few logs per principal and grouped by operationId
within a correlationId
.
Here we can see the breakdown and can understand a little more clearly the grouping mechanisms of activity logs. Use of correlationId
and operationId
are consistent across Azure Monitor activity logs and is your best bet to ensuring a proper view of operations.
Microsoft & Consistency
While you should be using correlationId
and operationId
to give you the most consistent breakdown of operations, it doesn’t mean Microsoft will perfectly follow that rule and log in a way that makes sense.
While in our previous example we shared that the needed metadata could be obtained on the “Started” status log, this too is not consistent. Instead, you could find metadata scattered with different parts of the overall operation, with statuses such as “Accepted”, or when we're lucky, on the final operation log.
The operation of “Create or Update Virtual Machine run command”, or specifically “Managed Run Commands” which is a newer API still not available in the Azure console as of writing, gives us a great example. We can again find a properties.responseBody
field, though if you’re paying attention then even this has changed, now with a capital “B”.
💡 Microsoft doesn’t enjoy consistent casing. It will likely be worth forcing a certain case or ensuring case insensitivity when building detections to avoid missing
something.
The properties.responseBody
field can be found associated with the “Accepted” status
log. The same log has the eventName
of “EndRequest”, which tells us the substatus
field will also be associated to this part of the operation sequence (shown below). Use of correlationId
and operationId
was also broken with this operation. In part due to how the operation works, but it can certainly add confusion to the log structure.
Why this occurs in the case of the operation should come down to how it was built and its function. The log with the “EndRequest” eventName
on the “Accepted” status
did create the command, the subsequent “Succeeded” statuses should be executing the created command on the VMs. While this adds understanding, it doesn’t aid in the usability of the activity log.
Wrapping Up
We hope you walk away with an appreciation for more consistent logging. Of course, we hope you now also feel more comfortable reviewing Azure Monitor Activity logs, knowing you have this resource for future reference. Feel free to review the appendix for a quick rehash on some of our callouts and referenced links. We also encourage you to reach out and let us know your own experiences or frustrations reviewing Azure Monitor Activity logs.
Stay tuned for similar analysis on Entra ID and ongoing updates, including instances where we've observed outright missing metadata.
Appendix
Tips & Key Takeaways:
correlationId
, Microsoft’s primary grouping mechanism, typically per acting identity and type of operation, this is a key piece of data in structuring and sequencing of an operation in activity logs, of which typically involves several logs.operationId
, Microsoft’s secondary grouping mechanism.operationId
breaks downcorrelationId
when Microsoft groups operations.eventName
when equal to “EndRequest”, thesubStatus
field should exist to be used in identifying the type of operation.status
andsubStatus
, in combination witheventName
form an important trio.eventName
andstatus
let us know where in the sequence of an operation we are and the overall outcome of that sequence.eventName
of “EndRequest” tells us thesubStatus
should provide us important details, especially with Microsoft’s choice to combine what should be separate operations, such as Update and Creation into one named operation.- Microsoft doesn’t carry metadata of an operation across each log from start to finish, understanding where that metadata is and how to build the operation sequence is key.
- The claims.uti field is a
jti
in the JWT specification and unique per-token identifier. This field can allow you to monitor against replay attacks. claims.ipaddr
andhttpRequest.clientIpAddress
have overlap, but neither are consistent, we recommend a coalesce style function to be used between the fields.- Microsoft is not good about consistent casing, this is true across field values, but has been observed in the key of key/value pair dynamic objects.
- Microsoft notes that activity logs are considered to be at the subscription layer, that’s not entirely true and most tools don’t pick up on this. If you want to review and track edits, additions, deletions of management groups, even with a scope at the management root group, you're unlikely to see these logs unless you pull them from each management group's activity log.
Links:
- Azure Monitor platform logs: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/platform-logs-overview
- Azure activity log schema: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log-schema#severity-level
- Access token claims: https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference
- Optional claims: https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference
- Additional claim reference: https://learn.microsoft.com/en-us/entra/identity-platform/reference-claims-mapping-policy-type
- Key Vault Security: https://learn.microsoft.com/en-us/azure/key-vault/general/security-features