Back to script library
Entra / Microsoft 365 · Compliance & audit

Find audit events for a user

Find and report unified audit log events for actions taken by a user over a specified period across Exchange, Teams, SharePoint, and compliance workloads.

Connect & set up

Run these once per session. All scopes are read-only unless the script makes changes.

Connect-ExchangeOnline -ShowBanner:$False

Run it

The main script. Copy it, or download the .ps1 and run it from your console.

param(
[string] $StartDate = "Read-Host "Enter the start date (dd-mm-yyyy)",
[string] $EndDate = (Get-Date $StartDate).AddDays(1)
)
[array]$Modules = Get-Module | Select-Object -ExpandProperty Name
If ($Modules -notcontains "ExchangeOnlineManagement") {
Write-Host "Connecting to Exchange Online..."
Connect-ExchangeOnline -ShowBanner:$False
}
$User = Read-Host "Enter the user's UPN"
If (!(Get-ExoMailbox -Identity $User)) {
Write-Host "User $User not found"
Exit
}
Write-Host ("Searching the audit log for actions for {0} between {1} and {2}" -f $User, $StartDate, (Get-Date $EndDate -format 'dd-MMM-yyyy'))
[array]$Records = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -UserIds $User -ResultSize 5000 -SessionCommand ReturnLargeSet
If ($Records.Count -eq 0) {
Write-Host "No audit records found for $User between $StartDate and $EndDate"
Exit
}
$Records = $Records | Sort-Object Identity -Unique
$Records = $Records | Sort-Object {$_.CreationDate -as [datetime]}
Write-Host ("Found {0} audit records for {1}. First record from {2}. Last record at {3}" -f $Records.Count, $User, $Records[0].CreationDate, $Records[-1].CreationDate)
# Overall summary
$RecordsSummary = $Records | Group-Object Operations -NoElement | Sort-Object Count -Descending
Write-Host "Summary of actions taken by $User"
$RecordsSummary | Format-Table Name, Count -AutoSize
# Remove UserLoggedIn events
[array]$Records1 = $Records | Where-Object {$_.Operations -ne "UserLoggedIn"}
$Report = [System.Collections.Generic.List[Object]]::new()
Write-Host ("Processing {0} audit records for {1} between {2} and {3}" -f $Records1.Count, $User, $StartDate, $EndDate)
# Process each audit record and attempt to make sense of the audit data payload
ForEach ($Rec in $Records1) {
$Action = $null; $Object = $null; $Location = $null; $Workload = $null
$AuditData = $Rec.AuditData | ConvertFrom-Json
Switch ($Rec.Operations) {
"Create" {
$Action = 'Item created in mailbox'
$Object = $AuditData.Item.Subject
$Location = ($AuditData.MailboxOwnerUPN + $AuditData.Item.ParentFolder.Path)
$Workload = "Exchange Online"
}
"CopilotInteraction" {
$Action = 'Copilot interaction'
$Object = $AuditData.CopilotEventData.ThreadId
$Location = $AuditData.CopilotEventData.AppHost
$Workload = "Copilot"
}
"FileAccessed" {
$Action = 'File accessed'
$Object = $AuditData.SourceFileName
$Location = $AuditData.ObjectId
$Workload = "SharePoint Online"
}
"FileModified" {
$Action = 'File modified'
$Object = $AuditData.SourceFileName
$Location = $AuditData.ObjectId
$Workload = "SharePoint Online"
}
"FileRenamed" {
$Action = 'File renamed'
$Object = $AuditData.SourceFileName
$Location = $AuditData.ObjectId
$Workload = "SharePoint Online"
}
"FolderCreated" {
$Action = 'Folder created'
$Object = $AuditData.SourceFileName
$Location = $AuditData.ObjectId
$Workload = "SharePoint Online"
}
"FolderModified" {
$Action = 'Folder modified'
$Object = $AuditData.SourceFileName
$Location = $AuditData.ObjectId
$Workload = "SharePoint Online"
}
"FileModifiedExtended" {
$Action = 'File modified'
$Object = $AuditData.SourceFileName
$Location = $AuditData.ObjectId
$Workload = "SharePoint Online"
}
"FileSensitivityLabelApplied" {
$Action = 'Sensitivity label applied to file'
$Object = $AuditData.SourceFileName
$Location = $AuditData.ObjectId
$Workload = "SharePoint Online"
}
"MailItemsAccessed" {
$Action = 'Mail item accessed'
If ($AuditData[0].Item.ParentFolder.Name) {
$Object = $AuditData[0].Item.ParentFolder.Name
} Else {
$Object = 'Message Bind'
}
$Location = ($AuditData[0].MailboxOwnerUPN + $AuditData.Item.ParentFolder.Path)
$Workload = "Exchange Online"
}
"MoveToDeletedItems" {
$Action = 'Item moved to Deleted Items'
$Object = $Auditdata[0].AffectedItems.Subject
$Location = ($AuditData[0].MailboxOwnerUPN + $AuditData[0].AffectedItems.ParentFolder.Path)
$Workload = "Exchange Online"
}
"HardDelete" {
$Action = 'Item purged from mailbox'
$Object = $Auditdata.AffectedItems[0].Subject
$Location = ($AuditData.MailboxOwnerUPN + $AuditData.AffectedItems[0].ParentFolder.Path)
$Workload = "Exchange Online"
}
"SoftDelete" {
$Action = 'Item removed to Recoverable Items'
$Object = $Auditdata.AffectedItems[0].Subject
$Location = ($AuditData.MailboxOwnerUPN + $AuditData.AffectedItems[0].ParentFolder.Path)
$Workload = "Exchange Online"
}
"Send" {
$Action = 'Message Sent'
$Object = $AuditData[0].Item.Subject
$Location = ($AuditData[0].MailboxOwnerUPN + $AuditData[0].Item.ParentFolder.Path)
$Workload = "Exchange Online"
}
"SendAs" {
$Action = 'Message sent as another user'
$Object = $AuditData[0].Item.Subject
$Location = ($AuditData[0].MailboxOwnerUPN + $AuditData[0].Item.ParentFolder.Path)
$Workload = "Exchange Online"
}
"MessageSent" {
$Action = 'Teams message sent'
$Object = $AuditData.MessageId
$Location = ("Posted to {0} in {1}" -f $AuditData.ChannelName, $AuditData.TeamName)
$Workload = "Microsoft Teams"
}
"MessageCreatedHasLink" {
$Action = 'Teams message created with link'
$Object = $AuditData.MessageId
$Location = ("Posted to {0} in {1}" -f $AuditData.ChannelName, $AuditData.TeamName)
$Workload = "Microsoft Teams"
}
"TaskCreated" {
$Action = 'Task created'
$Object = $AuditData.ObjectId
$Location = $AuditData.Workload
$Workload = "Planner"
}
"TaskModified" {
$Action = 'Task modified'
$Object = $AuditData.ObjectId
$Location = $AuditData.Workload
$Workload = "Planner"
}
"TaskRead" {
$Action = 'Task read'
$Object = $AuditData.ObjectId
$Location = $AuditData.Workload
$Workload = "Planner"
}
"TaskListRead" {
$Action = 'Task list read'
$Object = $AuditData.ObjectId
$Location = $AuditData.Workload
$Workload = "Planner"
}
"PlanRead" {
$Action = 'Plan read'
$Object = $AuditData.ObjectId
$Location = $AuditData.Workload
$Workload = "Planner"
}
"NewRetentionCompliancePolicy" {
$Action = 'Retention policy created'
$Object = $Auditdata.Parameters.Value.Split("-A")[0].Split('"')[1]
$Location = $AuditData.Workload
$Workload = "Data lifecycle management"
}
"NewRetentionComplianceRule" {
$Action = 'Retention rule created'
$Object = $Auditdata.Parameters.value[0].Split("-Expiration")[1]
$Location = $Auditdata.Parameters.Value.Split("-A")[0].Split('"')[1]
$Workload = "Data lifecycle management"
}
"Get-ComplianceSearch" {
$Action = 'Compliance search retrieved'
If ($AuditData.Parameters -eq '-ResultSize "Unlimited"' ) {
$Object = "All results"
} Else {
Try {
$Object = $AuditData.Parameters.Split('"')[1]
} Catch {
$Object = "Unknown"
}
}
$Location = $AuditData.Workload
$Workload = 'eDiscovery'
}
"Set-ComplianceSearch" {
$Action = 'Compliance search updated'
$Object = $Auditdata.Parameters.Split("-D")[0].Split('"')[1]
$Location = $AuditData.Workload
$Workload = 'eDiscovery'
}
"Start-ComplianceSearch" {
$Action = 'Compliance search started'
$Object = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String( $AuditData.Parameters.Split('"')[1].SubString(0,32)))
$Location = $AuditData.Workload
$Workload = 'eDiscovery'
}
"New-ComplianceSearchAction" {
$Action = 'Compliance search action created'
$Object = $AuditData.Parameters
$Location = $AuditData.Workload
$Workload = 'eDiscovery'
}
"Get-ComplianceCase" {
$Action = 'Compliance case retrieved'
$Object = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String( $AuditData.Parameters.Split('"')[1].SubString(0,48)))
$Location = $AuditData.Workload
$Workload = "eDiscovery"
}
"Get-ComplianceCaseMember" {
$Action = 'Compliance case membership retrieved'
$Object = $AuditData.Parameters
$Location = $AuditData.Workload
$Workload = "eDiscovery"
}
"Set-Mailbox" {
$Action = 'Mailbox settings updated'
$Object = $AuditData.Parameters.value[0]
$Location = $AuditData.Workload
$Workload = 'Exchange Online PowerShell'
}
"Search" {
$Action = 'Search performed'
$Object = $AuditData.DataType
$Location = $AuditData.DatabaseType
$Workload = "Data Insights"
}
"SearchQueryInitiatedSharePoint" {
$Action = 'SharePoint Search performed'
$Object = $AuditData.QueryText
$Location = $AuditData.ScenarioName
$Workload = "SharePoint Online"
}
"SearchQueryInitiatedExchange" {
$Action = 'Exchange Search performed'
$Object = $AuditData.QueryText
$Location = $AuditData.ScenarioName
$Workload = "Exchange Online"
}
"SearchViewed" {
$Action = 'Search results viewed'
$Object = $AuditData.ObjectId
$Location = $AuditData.Query
$Workload = "eDiscovery"
}
"QueryUpdate" {
$Action = 'Search query updated'
$Object = $AuditData.QueryText
$Location = $AuditData.CaseId
$Workload = "eDiscovery"
}
"Validate" {
$Action = 'Search query validated'
$Object = $AuditData.DataType
$Location = $AuditData.RelativeURL
$Workload = "eDiscovery"
}
"GATFRTokenIssue" {
$Action = 'Get Access Token for Resource'
$Object = $AuditData.ResourceURL
$Location = "Outlook Web Access"
$Workload = "Exchange Online"
}
Default {
$Action = $Rec.Operations
$Object = $AuditData.ObjectId
$Location = $AuditData.Workload
$Workload = $AuditData.Workload
}
}
$ReportLine = [pscustomobject]@{
CreationDate = $Rec.CreationDate
Operation = $Rec.Operations
User = $Rec.UserIds
Record = $Rec.RecordType
Action = $Action
Object = $Object
Location = $Location
Workload = $Workload
}
$Report.Add($ReportLine)
}
# Show what we've done...
$Report | Out-GridView -Title "Audit events for $User between $StartDate and $EndDate"

Parameters

ParameterDefaultNotes
-StartDateRead-Host "Enter the start date (dd-mm-yyyy)"Start date for the audit log search (dd-mm-yyyy); prompted interactively if omitted.
-EndDate(Get-Date $StartDate).AddDays(1)End of the audit log search window.
Attribution