In this blog article, I want to discuss the power of KQL again and do a bit of a deep dive. I’ve written a few blog posts about getting started with KQL and using some basic queries that can make your tech life more effortless. I want to specifically focus on the IntuneAuditLogs KQL table in this article and write a few simple queries around it.
What I have in this post 👇🏾
- How to Create, Connect, Stream
- Audit Logs
- A Graphical Representation of Who Did Policy Changes During the Given Time Range and How Many Time
- Find Settings Changes in Policies
- Hunt a Specific Policy for Group Assignment Changes
- A Caveat with Endpoint Security Policies Group Assignments
- Create a Workbook
- Wrapping Up
How to Create, Connect, Stream
If you are wondering how to connect to the Azure Log Analytics Workspace, please refer to the article I wrote some time ago.
Audit Logs
KQL table to use – IntuneAuditLogs
Microsoft Intune Audit Logs are mainly to investigate the Policies and Settings and their changes. As an example, if you have privileged users who have access to the Microsoft Intune portal to make changes, as management or from a security perspective you might need to monitor who did what and when. Unfortunately, in-built records are not that user-friendly, but with a little bit of KQL magic, you can dive into the deep ends of the logs and get a better view of those changes.
What do you need to Stream the logs?
- An Intune License
- An Azure Subscription
- A Log Analytics workspace created in the Azure subscription
Log Analytics Pricing and More about Log Analytics charging
What I noticed with the Audit Logs table is you get a lot of information within the log itself but chances are you don’t need everything.
Few use cases I would like to explore as below that can be useful
A Graphical Representation of Who Did Policy Changes During the Given Time Range and How Many Times
IntuneAuditLogs
| project-rename User=Identity, Change=OperationName
| project TimeGenerated, Change, User
| summarize count() by User
| render columnchart

Find Settings Changes in Policies
IntuneAuditLogs
| where TimeGenerated >= ago (30d)
| where OperationName !contains “Assignment”
| parse Properties with * ‘,”TargetDisplayNames”:[“‘ Object ‘”],’ *
| parse Properties with * ‘”TargetDisplayNames”:[“‘IntuneProperty'”]’ * ‘,”Targets”:[{“ModifiedProperties”:[{“‘ ModifiedProperties ‘],’*
| project TimeGenerated, Identity, Object, OperationName, ModifiedProperties
If you check the ModifiedProperties column closely, you will see the changes that it took place. Like I said before, it’s not that straight-forward to get the information as you need to use KQL to trim it and expose the data you need.


Hunt a Specific Policy for Group Assignment Changes
I’ve written the below policy to understand the group assignment changes which took place in the Domain Join policy (DJ-1).
If you look at the Change column against the Operation column you will see the Create (add) and Delete (remove) of the group assignments.
IntuneAuditLogs
| where OperationName contains “Assignment”
| parse Properties with * ‘”TargetDisplayNames”:[“‘IntuneProperty'”‘ * ‘Target.GroupId”,”‘ GroupAssignmentChanges ‘(‘ *
| where IntuneProperty == “DJ-1”
| parse GroupAssignmentChanges with * ‘New”:”‘ Change
| project TimeGenerated, Identity, Policy=IntuneProperty, Operation=OperationName, Change

Make sure your time zone is local to you. This can make an impact on the timelines.

A Caveat with Endpoint Security Policies Group Assignments
Thought I would mention this as well. I tried to play with the Security Baseline Policies and get the Audit logs via KQL and I could not use the same query as I used for the other config policies.
What I noticed, both add and remove will go with Patch DeviceManagementIntentAssignment and you have to dig deeper to find out more. However, I still could not find the group changes that took place.
Create a Workbook
If you fancy, you can grab all your Audit Log KQL parts and create a Workbook so rather than running the queries everytime, you can let it refresh by itself.
If you know enough KQL, chances are you can also come up with your own queries so you can easily execute them against your environment to get more insights about the Audit Logs.
Wrapping Up
KQL is interesting and it helps you to understand your environment in a good way. You can use GitHub repos to find out and re-use the queries or come up with your own. Be mindful about spending on Azure Log Analytics as bigger the environment is larger your log streams will be. I hope you got some idea now on how to do KQL and I will see you with my next blog post soon!
Is there a way to include UPN of an enrolled device to see the user: Example query:
How to get UPN or user ID to return in the query to see who enrolled
IntuneOperationalLogs
| where TimeGenerated > ago(1d)
| extend DeviceId = tostring(todynamic(Properties).IntuneDeviceId)
| extend OS = tostring(todynamic(Properties).Os)
| where Result == “Success”
| where OperationName has “Enrollment”
| where OS == “iOS”
| join kind=leftouter IntuneDevices on DeviceId
| project TimeGenerated, DeviceId, DeviceName, Result, OperationName, OS
| summarize TimeGenerated = max(TimeGenerated) by DeviceId, DeviceName, Result, OperationName, OS
| sort by TimeGenerated desc
LikeLike
I did a bit of a change in the above query. IntuneUserID is showing up with the AzureAD ObjectID.
I projected that below. However you can join another table to see the UPN or simply check it in AzurAD Portal or using PowerShell.
IntuneOperationalLogs
| where TimeGenerated > ago(1d)
| extend DeviceId = tostring(todynamic(Properties).IntuneDeviceId)
| extend UserID = tostring(todynamic(Properties).IntuneUserId)
| extend OS = tostring(todynamic(Properties).Os)
| where Result == “Success”
| where OperationName has “Enrollment”
| where OS == “iOS”
| join kind=leftouter IntuneDevices on DeviceId
| project TimeGenerated, DeviceId, DeviceName, Result, OperationName, OS, UserID
| summarize TimeGenerated = max(TimeGenerated) by DeviceId, DeviceName, Result, OperationName, OS,UserID
| sort by TimeGenerated desc
LikeLike
Yes I found that same issue and trying to figure out the table to join to show the UPN.
LikeLiked by 1 person