This article explains how to search Microsoft 365 Unified Audit Logs using the Search-UnifiedAuditLog PowerShell cmdlet (including filtering by record type, operations, user, and IP address, and handling large result sets).
Microsoft 365 Unified Audit Logs
The Microsoft 365 Unified Audit Logs (aka. Office 365 Audit Logs) serve as a valuable resource for organizations using Microsoft 365. These logs consolidate data from multiple services within the Microsoft 365 suite, providing a unified view of activities and events.
Accessing and analyzing the Microsoft 365 audit logs can be done through various methods, including the Microsoft Purview portal and PowerShell cmdlets. PowerShell, in particular, offers a powerful and efficient way to query these logs, making it easier to extract and analyze specific audit log data.
Requirements
- Exchange Online PowerShell module (`ExchangeOnlineManagement`) 3.x or later.
- PowerShell 7.x is recommended for new automation and administrative scripts.
- Windows PowerShell 5.1 is supported for environments where PowerShell 7 is not yet deployed.
- An appropriate Microsoft Purview Audit role is required to search audit records. You should use the least-privileged role available for the required audit operations.
Before running the examples, you need to check if the Exchange Online PowerShell module is installed:
Get-Module ExchangeOnlineManagement -ListAvailable |
Sort-Object Version -Descending |
Select-Object -First 1 Name, Version
If the module is not installed, you need to install the current version from the PowerShell Gallery:
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Then you need to import the module:
Import-Module ExchangeOnlineManagement
And finally, connect to Exchange Online using the command:
Connect-ExchangeOnline
Microsoft Purview Audit in Modern Microsoft 365
Microsoft Purview Audit provides several ways to search and retrieve audit data:
- Microsoft Purview portal โ interactive searches and investigation workflows.
- Audit Search Graph API โ programmatic access to Microsoft 365 audit search through Microsoft Graph. It is designed for app-based and automated audit queries.
- Search-UnifiedAuditLog โ Exchange Online PowerShell cmdlet for interactive searches and PowerShell automation.
- Office 365 Management Activity API โ programmatic access to audit content through the Management Activity API, particularly useful for continuous ingestion into SIEM and other monitoring systems.
For PowerShell-based administration and ad-hoc investigations, Search-UnifiedAuditLog remains a supported option. For app-based automation and large-scale integration, you should also consider the Audit Search Graph API.
In case your organization has Microsoft 365 E5 licenses, you can also use Audit Premium features (including longer retention periods, high-value event tracking, and advanced investigation capabilities).
Note. Although Search-UnifiedAuditLog remains widely used, Microsoft continues to expand Microsoft Purview Audit capabilities. For large-scale investigations and advanced audit cases, you should also review the latest Microsoft Purview Audit search features and APIs.
What Data Do Microsoft 365 Audit Logs Include?
Microsoft 365 audit logs record various types of events, including:
- User and administrator activities: This includes actions such as file access, document sharing, email activities, user sign-in events, changes to user roles and permissions, and other user-related activities.
- Exchange Online events: These cover activities related to Exchange Online, such as email sends and receives, mailbox access, message deletions, and changes to mailbox configurations.
- SharePoint Online and OneDrive for Business events: It tracks activities within SharePoint Online and OneDrive for Business, including file uploads, downloads, sharing, and modifications.
- Microsoft Entra ID events: This includes user and group management, password resets, role assignments, Conditional Access policy changes, and other directory and admin activities.
- Microsoft Purview Information Protection events: It logs activities related to data protection and classification, including document labeling, encryption, and other security-related actions.
Audit Retention and Licensing
Before searching Microsoft 365 audit logs, you should understand that audit log retention depends on your licensing level. Microsoft provides 2 audit tiers:
- Audit Standard (included with most Microsoft 365 subscriptions);
- Audit Premium retention depends on the license assigned to the user who generated the audit event. Having an E5 tenant does not automatically provide extended retention for all users.
One of the most common troubleshooting cases occurs when you cannot find older audit events. In many cases, the events have already exceeded the retention period available for the tenant’s licensing level.
Typical retention periods include:
| Audit level | Default retention | Notes |
|---|---|---|
| Audit Standard | 180 days | Default retention for audit records generated on or after October 17, 2023. Records generated before this date follow the previous 90-day retention period. |
| Audit Premium | 1 year by default | Available with Microsoft 365 E5 and supported compliance add-ons |
| Extended retention | Up to 10 years | Requires Audit retention policies and appropriate add-on licensing |
In case your organization requiring long-term audit retention, you can config Microsoft Purview Audit retention policies. Up to 10 years of retention requires appropriate licensing, including the Microsoft Purview Audit (10-year retention) add-on.
Important. Audit retention is controlled by Microsoft Purview Audit policies and depends on user licensing, workload, and configured retention policies. You should always verify current Microsoft licensing requirements before designing compliance retention strategies.
Keep in mind that retention periods may vary depending on the Microsoft 365 workload, licensing, and Microsoft service changes.
In case expected audit records are missing, you should verify:
- If audit logging is enabled;
- If the search date range is correct;
- If the tenant licensing supports the required retention period;
- If the specific workload supports the desired audit retention period.
First, verify that auditing is enabled
Keep in mind that Microsoft 365 audit logging is enabled by default for most Microsoft 365 organizations. However, we recommend you to verify the auditing status before troubleshooting missing audit records/running an investigation. Some Microsoft 365 Business subscriptions and certain unmanaged trial tenants may require auditing to be enabled manually.
Connect to Exchange Online PowerShell and run the command:
Get-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabled

If UnifiedAuditLogIngestionEnabled is True, auditing is enabled and you can proceed with Search-UnifiedAuditLog.
If the value is False, auditing is not enabled for the organization. In that case, an administrator with the Audit Logs role can enable it by running:
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true
You can also enable auditing from the Microsoft Purview portal. But note that after enabling auditing, it can take up to 60 minutes before the change takes effect, and audit events may take additional time to become searchable.
Important. You should not assume that auditing must be manually enabled in every tenant. For most Microsoft 365 organizations, auditing is already enabled by default. The purpose of this step is to verify the current state, not automatically enable auditing.
Required Permissions
In order to query Microsoft 365 Unified Audit Logs, your account must have appropriate permissions in Microsoft Purview/Exchange Online. Common roles include:
- Audit Logs;
- View-Only Audit Logs;
- Compliance Administrator;
- Global Administrator.
If Search-UnifiedAuditLog returns permission-related errors, you need to verify the assigned Microsoft 365 roles before troubleshooting the audit config.
Querying Microsoft 365 Unified Audit Logs with PowerShell
A powerful cmdlet in PowerShell that enables you to search and retrieve information from the Microsoft 365 Unified Audit Logs is Search-UnifiedAuditLog.
This cmdlet allows you to specify various search criteria, such as date ranges, specific activities, users, or even IP addresses, to narrow your search and retrieve the relevant audit log entries.
Search-UnifiedAuditLog Limitations
Although Search-UnifiedAuditLog is one of the most commonly used tools for Microsoft 365 audit investigations, you should be aware of several limitations. So, keep in mind that:
- Audit events may not appear immediately after an activity occurs because audit data ingestion can take time;
- Search results depend on the audit retention period available in your Microsoft 365 licensing plan;
- Large searches may require paging with SessionId and ReturnLargeSet;
- Some workloads generate very large volumes of audit records (it makes broad searches slow and difficult to analyze);
- Search-UnifiedAuditLog is primarily intended for investigation and reporting scenarios rather than continuous monitoring.
For large-scale reporting, SIEM integration, or automated audit collection, Microsoft 365 Management Activity API and Microsoft Graph-based solutions may provide more flexibility.
Syntax, RecordType, and Operations
Below is the syntax for the Search-UnifiedAuditLog cmdlet. As you can see, only the StartDate and EndDate are required. But in most cases, narrowing down your search by filtering the RecordType and Operations is recommended.
Search-UnifiedAuditLog
-EndDate <ExDateTime>
-StartDate <ExDateTime>
[-Formatted]
[-FreeText <String>]
[-HighCompleteness]
[-IPAddresses <String[]>]
[-LegacyFlow]
[-LongerRetentionEnabled <String>]
[-ObjectIds <String[]>]
[-Operations <String[]>]
[-RecordType <AuditRecordType>]
[-ResultSize <Int32>]
[-SessionCommand <UnifiedAuditSessionCommand>]
[-SessionId <String>]
[-SiteIds <String[]>]
[-UserIds <String[]>]
[<CommonParameters>]
RecordType โ this is the service-specific record type that you wish to filter. For example, ExchangeAdmin is the record type containing the admin-related operations performed in Exchange Online. Another example is the SharePointFileOperation record type, which consists of file-related operations or events in SharePoint Online.
Operations โ the events or actions that happened. One example operation is FileAccessed for the SharePointFileOperation record, an entry for when a file was accessed from a SharePoint Online library. Under the ExchangeAdmin record type, you will see the cmdlet name executed, like Set-TransportConfig, Set-Mailbox, and Set-UnifiedGroup.
The following sections demonstrate examples of using the Search-UnifiedAuditLog cmdlet. Letโs dive in.
Refer to the Microsoft Graph Audit Search API documentation for the current list of supported audit record types and operations. The Graph API exposes these through the recordTypeFilters and operationFilters properties of the auditLogQuery resource.
Search SharePoint Site Administration Audit Events
Suppose you need to report how many SharePoint Online sites were created in the last 90 days. To do that, weโll use the SharePoint record type.
Copy the code below and run it in your Exchange Online PowerShell session.
$startDate = (Get-Date).AddDays(-90).ToUniversalTime()
$endDate = (Get-Date).ToUniversalTime()
$result = Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-RecordType SharePoint `
-Operations SiteCollectionCreated `
-ResultSize 5000 `
-Formatted
$result | Group-Object -Property Operations
Note that the -Formatted switch ensures that the RecordType and Operations attributes in the results are returned as names, not integers.
The command above returns up to 5,000 matching records. If more than 5,000 events exist in the selected period, you need to use the paging approach described in the โHandling Large Audit Log Resultsโ section to retrieve additional records.
According to the result, there have been three SiteCollectionCreated events in the last 90 days.

The details of each audit log are in the AuditData property in JSON format. Now letโs inspect one of the results.
if ($result.Count -gt 0) {
$result[0].AuditData | ConvertFrom-Json
}
else {
Write-Host "No audit records found for the specified criteria."
} 
In order to search SharePoint File Activities, run the following command:
$result = Search-UnifiedAuditLog `
-StartDate (Get-Date).AddDays(-7).ToUniversalTime() `
-EndDate (Get-Date).ToUniversalTime() `
-RecordType SharePointFileOperation `
-Operations FileAccessed `
-Formatted
Search Exchange Online Audit Logs
One area in Exchange Online that admins tend to monitor is the actions executed by administrators. In this example, we can run the Search-UnifiedAuditLog and specify the ExchangeAdmin record type.
For example, the following command retrieves up to 5,000 Exchange admin audit events from the last 90 days and groups them by operation. For a complete result set, use the paging example below:
$startDate = (Get-Date).AddDays(-90).ToUniversalTime()
$endDate = (Get-Date).ToUniversalTime()
$result = Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-RecordType ExchangeAdmin `
-ResultSize 5000 `
-Formatted
$result | Group-Object -Property Operations | Select-Object Count, Name

But how do we know who did what? Letโs inspect one log entry:
if ($result.Count -gt 0) {
$result[0].AuditData | ConvertFrom-Json
}
else {
Write-Host "No audit records found for the specified criteria."
} The result shows that the Set-AdminAuditLogConfig command was executed by the NT AUTHORITY\SYSTEM (Microsoft.Exchange.ServiceHost) account.

How about listing all events? For that, we can apply an iteration like the foreach statement.
foreach ($item in $result) {
$audit = $item.AuditData | ConvertFrom-Json
[PSCustomObject]@{
TimeStamp = $audit.CreationTime
UserId = $audit.UserId
Operation = $audit.Operation
Parameters = [string](
$audit.Parameters | ForEach-Object {
"$($_.Name):$($_.Value);"
}
)
}
} 
There can be irrelevant log entries. Perhaps youโd want to ignore the events created by the NT AUTHORITY\SYSTEM (Microsoft.Exchange.ServiceHost) and NT AUTHORITY\SYSTEM (w3wp) accounts? We can add an array of user Ids to ignore.
# List of UserIds to ignore $ignoreEventsBy = @( 'NT AUTHORITY\SYSTEM (Microsoft.Exchange.ServiceHost)', 'NT AUTHORITY\SYSTEM (w3wp)' )
And insert an if statement that checks if the UserId is listed in the $ignoreEventsBy array. If so, the foreach loop will skip this event.
foreach ($item in $result) {
$audit = $item.AuditData | ConvertFrom-Json
if ($ignoreEventsBy -contains $audit.UserId) {
# skip this event
continue
}
[PSCustomObject]@{
TimeStamp = $audit.CreationTime
UserId = $audit.UserId
Operation = $audit.Operation
Parameters = [string](
$audit.Parameters | ForEach-Object {
"$($_.Name):$($_.Value);"
}
)
}
} This time, only the events from actual users are included in the report.

Audit Search Graph API
The Microsoft Purview Audit Search Graph API provides you with programmatic access to Microsoft 365 audit searches through Microsoft Graph. Unlike Search-UnifiedAuditLog, which is a PowerShell cmdlet, the Graph API creates an audit query and then retrieves the records associated with that query.
For example, an app can create an audit query using the Microsoft Graph auditLogQueries resource and then retrieve its records.
Note that the API requires Microsoft Graph permissions such as AuditLogsQuery.Read.All, or workload-specific permissions when access needs to be more narrowly scoped.
You should use the Microsoft Graph API documentation in order to determine the required permissions and supported query parameters.
Query Microsoft 365 Audit Logs with Microsoft Graph
For programmatic access to the Microsoft 365 unified audit log, you can use the Microsoft Graph Audit Search API.
The API uses the auditLogQuery resource. First, you need to create an audit query with the required filters, then retrieve the records associated with the query. Microsoft Graph supports filters (such as date range, record type, service, operation, user principal name, IP address, and object ID.
For example, the following request creates an audit query for Exchange admin events:
POST https://graph.microsoft.com/v1.0/security/auditLog/queries
Authorization: Bearer <access-token>
Content-Type: application/json
{
"displayName": "Exchange admin audit",
"filterStartDateTime": "2026-09-01T00:00:00Z",
"filterEndDateTime": "2026-09-29T23:59:59Z",
"recordTypeFilters": [
"exchangeAdmin"
]
}
The response contains an audit query ID. Use that ID to retrieve the matching audit records:
GET https://graph.microsoft.com/v1.0/security/auditLog/queries/{auditLogQueryId}/records
Authorization: Bearer <access-token> Note that for Exchange audit data, an app/delegated permission (such as AuditLogsQuery-Exchange.Read.All) is required. In case the app needs to query multiple workloads, you need to grant only the workload-specific permissions it actually requires, or use the broader AuditLogsQuery.Read.All permission when justified.
For production automation, you should use the Microsoft Graph Audit Search API when you need app-based access/integration with custom tools. For interactive PowerShell investigations, Search-UnifiedAuditLog remains a useful option.
Search Microsoft Entra ID Audit Events
Microsoft Entra ID (formerly Azure AD) generates a large number of audit events related to user authentication, password management, role assignments, and directory changes. For example, you can search Entra ID audit events recorded during the last week (7 days):
$startDate = (Get-Date).AddDays(-7).ToUniversalTime()
$endDate = (Get-Date).ToUniversalTime()
Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-RecordType AzureActiveDirectory `
-Formatted
You can inspect the resulting audit records via converting the AuditData property from JSON format:
$result = Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-RecordType AzureActiveDirectory `
-ResultSize 100
if ($result.Count -gt 0) {
$result[0].AuditData | ConvertFrom-Json
}
else {
Write-Host "No audit records found for the specified criteria."
}
Here are the things that common Microsoft Entra ID investigation scenarios include:
- User password changes and resets;
- Admin role assignments;
- User and group management actions;
- Conditional Access policy changes;
- Authentication-related events;
- Identity governance activities.
Search Audit Events for a Specific User
Below you will find the example that searches audit events associated with a specific Microsoft Entra user account:
Search-UnifiedAuditLog `
-StartDate (Get-Date).AddDays(-30).ToUniversalTime() `
-EndDate (Get-Date).ToUniversalTime() `
-UserIds admin@contoso.com `
-ResultSize 5000
This is useful when investigating admin activity, account compromise incidents, or insider threat cases.
Search Specific Operations
You can also filter audit logs by operation name. For example, to find SharePoint file access events, use the following command:
Search-UnifiedAuditLog `
-StartDate (Get-Date).AddDays(-7).ToUniversalTime() `
-EndDate (Get-Date).ToUniversalTime() `
-Operations FileAccessed `
-ResultSize 5000
The approach shown above is useful when investigating:
- File access activity;
- User creation events;
- Mailbox permission changes;
- Conditional Access modifications;
- Admin actions.
Search Audit Events by IP Address
When investigating suspicious sign-ins or compromised accounts, you can search audit records associated with a specific IP address. Use the following command:
Search-UnifiedAuditLog `
-StartDate (Get-Date).AddDays(-30).ToUniversalTime() `
-EndDate (Get-Date).ToUniversalTime() `
-IPAddresses 203.0.113.50 `
-ResultSize 5000
This can help correlate audit activity with VPN logs, firewall logs, or Microsoft Entra sign-in investigations.
Handling Large Microsoft 365 Audit Log Results
The Search-UnifiedAuditLog command returns 100 items by default, which is not a lot. When you run the below command, it returns 100 audit logs entries.
$startDate = (Get-Date).AddDays(-10).ToUniversalTime()
$endDate = (Get-Date).ToUniversalTime()
$result = Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-RecordType SharePoint `
-Formatted
$result.Count

Important. Search-UnifiedAuditLog returns a maximum of 5,000 records per request. For larger result sets, you should use paging with SessionId and ReturnLargeSet.
You can increase the number of results returned by a single request to a maximum of 5,000 by specifying -ResultSize 5000:
$result = Search-UnifiedAuditLog ` -StartDate $startDate ` -EndDate $endDate ` -RecordType SharePoint ` -SessionCommand ReturnLargeSet ` -ResultSize 5000 ` -Formatted $result.Count

What if there are more than 5,000 audit log items? You can retrieve up to 50,000 records for a Search-UnifiedAuditLog query by using paging with SessionId and ReturnLargeSet. This limit applies to the Search-UnifiedAuditLog query and you should not treat it as a scalable bulk-collection mechanism.
For investigations/reports that may exceed this limit, you need to split the search into smaller date ranges/other meaningful filters. For recurring automated collection or large-scale audit ingestion, you need to use the Microsoft 365 Management Activity API or Microsoft Graph Audit Search API instead.
First, create a unique session ID, this can be any value, but I prefer using a GUID. Next, weโll enclose the Search-UnifiedAuditLog command in a do-while loop. The command will run until there are no more results. Each iteration will use the same SessionId value.
Note that starting from May 2026, Microsoft changed the behavior of Search-UnifiedAuditLog paging. The ResultCount property now shows a running count during retrieval, and you should use AuditSearchRequestMetadata.moreRecordsAvailable in your scripts in order to determine whether additional pages exist.
$sessionID = (New-Guid).Guid
$startDate = (Get-Date).AddDays(-4).ToUniversalTime()
$endDate = (Get-Date).ToUniversalTime()
$result = [System.Collections.ArrayList]@()
$moreRecords = $false
try {
do {
$temp = Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-RecordType SharePoint `
-SessionId $sessionID `
-SessionCommand ReturnLargeSet `
-ResultSize 5000 `
-Formatted `
-ErrorAction Stop
if ($null -eq $temp -or $temp.Count -eq 0) {
break
}
foreach ($item in $temp) {
$audit = $item.AuditData | ConvertFrom-Json
$null = $result.Add(
[PSCustomObject]@{
Identity = $item.Identity
TimeStamp = $audit.CreationTime
UserId = $audit.UserId
Operation = $audit.Operation
ObjectID = $audit.ObjectId
}
)
}
$moreRecords = ($temp[-1].AuditSearchRequestMetadata |
ConvertFrom-Json).moreRecordsAvailable
} while ($moreRecords -eq $true -and $result.Count -lt 50000)
$hitLimit = $result.Count -ge 50000
# Remove duplicate audit records and sort chronologically
$result = $result |
Group-Object Identity |
ForEach-Object { $_.Group[0] } |
Sort-Object TimeStamp
if ($hitLimit -and $moreRecords) {
Write-Warning @"
The query returned the maximum of 50,000 records, but more records are available.
The result set is incomplete. Split the search into smaller date ranges or add
more specific filters and run the query again.
"@
}
}
catch {
Write-Error "Failed to retrieve Microsoft 365 audit logs: $($_.Exception.Message)"
}
In our example above, the query returns over 41,000 audit records. The script removes duplicate records by their Identity value and sorts the final result by timestamp.
Important. Search-UnifiedAuditLog can return a maximum of 50,000 records when using SessionCommand ReturnLargeSet. If moreRecordsAvailable is still true when the 50,000-record limit is reached, the result set is incomplete. You need to split the search into smaller date ranges/apply additional filters and run the query again. Do not treat a 50,000-record result as proof that all matching events were retrieved.

You can export the result to a CSV file using the Export-Csv cmdlet if needed.
$result | Export-Csv -NoTypeInformation .\auditlog.csv -Encoding utf8

Wrapping Up
Querying Microsoft 365 audit logs using PowerShell is a powerful and efficient way to gain valuable insights into your organizationโs activities, security, and compliance. By leveraging the capabilities of PowerShell and the Search-UnifiedAuditLog, you can easily retrieve and analyze the information stored in your audit logs.
Keep in mind that Search-UnifiedAuditLog is excellent for ad-hoc investigations and admin reporting . But organizations with large audit volumes often supplement it with Microsoft 365 Management Activity API, Microsoft Graph integrations, or SIEM platforms (such as Microsoft Sentinel).
What is the Microsoft 365 Unified Audit Log?
The Microsoft 365 Unified Audit Log is a centralized audit repository that collects events and activities from Microsoft 365 services, including Exchange Online, SharePoint Online, OneDrive, Microsoft Entra ID, and other workloads. It provides a single location for investigating user actions, administrative changes, and security events.
What PowerShell cmdlet is used to search Microsoft 365 audit logs?
The primary PowerShell cmdlet for querying Microsoft 365 audit logs is Search-UnifiedAuditLog. It allows administrators to search audit records by date range, user, operation, record type, IP address, and other criteria.
What permissions are required to use Search-UnifiedAuditLog?
Your account must be assigned one of the appropriate Microsoft 365 roles, such as:
- Audit Logs
- View-Only Audit Logs
- Compliance Administrator
- Global Administrator
Without the required permissions, audit log searches may fail.
What types of events are stored in Microsoft 365 audit logs?
Microsoft 365 audit logs can include:
- User sign-ins and authentication events
- Admin actions
- Exchange Online mailbox activities
- SharePoint and OneDrive file operations
- Microsoft Entra ID directory changes
- Role assignments and permission changes
- Data protection and compliance events
Why can’t I find older audit events?
Missing audit records are often caused by:
- Audit logs exceeding the retention period
- Incorrect search date ranges
- Audit logging not being enabled
- Licensing limitations
- Workload-specific retention restrictions
Always verify retention policies before assuming audit collection has failed.
