Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.
🚀 Launch ToolkitEver had a ticket like this land on your desk?
In Microsoft Entra ID (Azure AD), almost every admin action is logged. The only real problem is finding the right event fast.
That’s where Directory Audit Logs help. And with Microsoft Graph PowerShell, you can query them in seconds using:
This guide shows you the exact filters, scripts, and investigation patterns admins use to answer “who changed what” without opening the portal 50 times.
Directory Audit Logs are Entra’s built-in change history. They capture actions across:
Each audit record typically includes:
Which means your three investigation questions are always answerable:
✅ Who did it?
✅ What did they do?
✅ When + to what object?
Install-Module Microsoft.Graph.Reports -Scope CurrentUser
Import-Module Microsoft.Graph.Reports
You need the Graph permission:
Connect-MgGraph -Scopes "AuditLog.Read.All"
Using app-only auth? Grant AuditLog.Read.All (Application) to your Entra app and admin-consent it.
Get-MgAuditLogDirectoryAudit
That returns the most recent logs (paged).
In small tenants it’s fine — in large tenants you’ll want filters immediately.
Get-MgAuditLogDirectoryAudit -Top 5 |
Select-Object Id, ActivityDisplayName, Category, ActivityDateTime, LoggedByService, Result, InitiatedBy, TargetResources |Format-List
Here are the filters admins actually use in real investigations.
$UserId = "00000000-0000-0000-0000-000000000000"
Get-MgAuditLogDirectoryAudit -Filter "initiatedBy/user/id eq '$UserId'"
Perfect for “what did this admin do today?”
Scheduled automation, provisioning agents, sync apps, etc.
Get-MgAuditLogDirectoryAudit -Filter "initiatedBy/app/displayName eq 'Azure AD Cloud Sync'"
Get-MgAuditLogDirectoryAudit -Filter "category eq 'GroupManagement'"
Useful categories you’ll see often:
$Since = (Get-Date).AddDays(-7).ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogDirectoryAudit -Filter "activityDateTime gt $Since"
$Since = (Get-Date).AddDays(-3).ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogDirectoryAudit -Filter "
initiatedBy/user/userPrincipalName eq 'admin@contoso.com' and
category eq 'RoleManagement' and
activityDateTime gt $Since"
This narrows to role changes by one admin in the last 3 days.
$GroupId = "11111111-1111-1111-1111-111111111111"
$Since = (Get-Date).AddDays(-14).ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogDirectoryAudit -Filter "
activityDisplayName eq 'Add member to group' and
activityDateTime gt $Since" -All |
Where-Object {
$_.TargetResources.Id -contains $GroupId
} |
Select-Object ActivityDateTime, ActivityDisplayName,
@{n="InitiatedBy";e={$_.InitiatedBy.User.UserPrincipalName}},
@{n="TargetGroup";e={$_.TargetResources.DisplayName}},
TargetResources
Use this when: “Who touched this group?” tickets come in.
$UserUPN = "jane@contoso.com"
$Since = (Get-Date).AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogDirectoryAudit -Filter "
category eq 'UserManagement' and
activityDateTime gt $Since" -All |
Where-Object {
$_.TargetResources.UserPrincipalName -contains $UserUPN
} |
Select-Object ActivityDateTime, ActivityDisplayName,
@{n="InitiatedBy";e={$_.InitiatedBy.User.UserPrincipalName}},
Result, ResultReason
Use this when: Users mysteriously “stop working.”
$Since = (Get-Date).AddDays(-7).ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogDirectoryAudit -Filter "
category eq 'RoleManagement' and
activityDateTime gt $Since" -All |
Select-Object ActivityDateTime, ActivityDisplayName,
@{n="InitiatedBy";e={$_.InitiatedBy.User.UserPrincipalName}},
TargetResources
Use this when: Someone suddenly has Global Admin rights.
$Since = (Get-Date).AddDays(-7).ToString("yyyy-MM-ddTHH:mm:ssZ")
$Logs = Get-MgAuditLogDirectoryAudit -Filter "activityDateTime gt $Since" -All
$Logs |
Select-Object ActivityDateTime, Category, ActivityDisplayName, Result,
@{n="InitiatedByUPN";e={$_.InitiatedBy.User.UserPrincipalName}},
@{n="InitiatedByApp";e={$_.InitiatedBy.App.DisplayName}},
@{n="Targets";e={($_.TargetResources | ForEach-Object {$_.DisplayName}) -join "; "}} |
Export-Csv ".\Entra-Audit-Last7Days.csv" -NoTypeInformation -Encoding UTF8
$Since = (Get-Date).AddDays(-1).ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogDirectoryAuditCount -Filter "activityDateTime gt $Since"
Use this first in large tenants to avoid surprise 20k pulls.
| Error | Cause | Solution |
|---|---|---|
| Insufficient privileges to complete the operation | Missing AuditLog.Read.All scope. | Connect-MgGraph -Scopes "AuditLog.Read.All" Ensure the admin consent is granted. |
| Request_UnsupportedQuery / BadRequest | Filter syntax wrong or unsupported property. |
|
| Empty results even though changes happened |
|
Pull by category/time first: Get-MgAuditLogDirectoryAudit -Filter "category eq 'GroupManagement'" -Top 50 Then locate the right operation label. |
Directory audit logs + Graph PowerShell help you:
If you’re serious about tenant governance, this is a must-have reporting block.
With just one cmdlet, you can answer the most common Entra investigation questions:
If you’ve been depending on portal searches until now, this is your upgrade.
Did You Know? Managing Microsoft 365 applications is even easier with automation. Try our Graph PowerShell scripts to automate tasks like generating reports, cleaning up inactive Teams, or assigning licenses efficiently.
Ready to get the most out of Microsoft 365 tools? Explore our free Microsoft 365 administration tools to simplify your administrative tasks and boost productivity.
© Your Site Name. All Rights Reserved. Design by HTML Codex