Microsoft Intune Audit Logs Hunting With KQL

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 👇🏾

  1. How to Create, Connect, Stream
  2. Audit Logs
  3. A Graphical Representation of Who Did Policy Changes During the Given Time Range and How Many Time
  4. Find Settings Changes in Policies
  5. Hunt a Specific Policy for Group Assignment Changes
  6. A Caveat with Endpoint Security Policies Group Assignments
  7. Create a Workbook
  8. 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!

Advertisement

4 thoughts on “Microsoft Intune Audit Logs Hunting With KQL

  1. 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

    Like

    1. 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

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.