The unified audit log works out of the box, but the defaults don't always answer what an investigation asks: which messages did the attacker open, what did they search for, and will the records still exist next year? In this post I'll set up the parts of Microsoft Purview Audit that need deliberate configuration (licenses, mailbox audit actions, search events and audit retention policies) and then put MailItemsAccessed to work.
Day-to-day searching is covered in Searching the audit log in Microsoft Purview.
Standard vs Premium: what you actually get#
| Capability | Audit (Standard) | Audit (Premium) |
|---|---|---|
| Default retention | 180 days | One year for Microsoft Entra ID, Exchange, OneDrive and SharePoint records; 180 days for other services unless a policy says otherwise |
| Audit log retention policies | No | Yes, up to 50 per organization |
| 10-year retention | No | With the per-user 10-Year Audit Log Retention add-on |
| Intelligent insights | No | Yes, for example the sensitivity label of accessed mail and search events |
| Office 365 Management Activity API | Baseline of 2,000 requests per minute, scaling with seats | About twice the bandwidth |
Retention follows the license of the user who performs the activity. Longer retention needs Office 365 E5, Microsoft 365 E5, Microsoft Purview Suite (formerly Microsoft 365 E5 Compliance) or the E5 eDiscovery and Audit add-on. Guests and other non-E5 users stay at 180 days. Records created by non-user entities (service principals, system events, app activity) are kept for a fixed year, and custom policies don't change that.
Note: Microsoft Learn now documents MailItemsAccessed as part of Audit (Standard), on by default for users with Office 365 or Microsoft 365 E3 or E5 licenses. Premium still adds properties such as the sensitivity label of the mail item that was accessed.
Prerequisites#
- Roles: Audit Manager (search, export and audit settings) or Audit Reader (search and export only). Creating audit retention policies needs the Organization Configuration role. Enabling auditing and running the audit cmdlets still need the Audit Logs or View-Only Audit Logs roles in the Exchange admin center. See Purview roles and role groups.
- PowerShell: Exchange Online PowerShell V3 for mailbox settings and searches, and Security & Compliance PowerShell for retention policies.
Step-by-step#
1. Turn on Premium logging for the right users#
An E5 license isn't enough on its own; the Microsoft 365 Advanced Auditing service plan must be on. In the Microsoft 365 admin center, open Users › Active users, select the user, then Licenses and apps, expand Apps and make sure Microsoft 365 Advanced Auditing is selected. Logging of Premium insights starts within 24 hours. With group-based licensing, check the plan in the group's assignment (licensing errors). To spot-check with Microsoft Graph PowerShell:
Connect-MgGraph -Scopes User.Read.All
Get-MgUserLicenseDetail -UserId anna@contoso.com |
Select-Object -ExpandProperty ServicePlans |
Where-Object ServicePlanName -like '*AUDIT*' |
Format-Table ServicePlanName, ProvisioningStatusGood looks like the auditing plan with ProvisioningStatus Success, not Disabled.
2. Check what each mailbox audits#
Connect-ExchangeOnline
Get-OrganizationConfig | Format-List AuditDisabled
Get-Mailbox -Identity anna@contoso.com | Format-List AuditEnabled, DefaultAuditSet
(Get-Mailbox -Identity anna@contoso.com).AuditOwnerGood looks like AuditDisabled : False, DefaultAuditSet listing Admin, Delegate and Owner, and MailItemsAccessed in the owner list. A logon type missing from DefaultAuditSet means someone customized that mailbox, and customized mailboxes don't pick up new audit events Microsoft releases. Restore the defaults with Set-Mailbox -Identity anna@contoso.com -DefaultAuditSet Admin,Delegate,Owner.
3. Enable search query events#
SearchQueryInitiatedSharePoint is logged by default, but SearchQueryInitiatedExchange must be enabled per mailbox:
Set-Mailbox -Identity anna@contoso.com -AuditOwner @{Add="SearchQueryInitiated"}In a multi-geo tenant, run it in the geo where the mailbox lives (Get-Mailbox <user> | Format-List MailboxLocations). Search queries reveal what people looked for; agree the scope with privacy and HR first.
4. Create audit retention policies#
In the Microsoft Purview portal, open Solutions › Audit, go to the policies page and select Create audit retention policy:
- Policy name: unique, and it can't be renamed later.
- Users: leave blank for everyone.
- Record type: blank means all; pick a single type to choose specific activities.
- Duration: 7 Days, 30 Days, 6 Months, 9 Months, 1 Year, 3 Years, 5 Years, 7 Years, or 10 Years for users with the add-on. The 3, 5 and 7 year options also need the 10-year add-on.
- Priority: 1 to 10000; the lower number wins.
Connect-IPPSSession
New-UnifiedAuditLogRetentionPolicy -Name "Teams audit - legal team" `
-Description "Teams activity of the legal team, 10 years" `
-RecordTypes MicrosoftTeams -UserIds gc@contoso.com `
-RetentionDuration TenYears -Priority 50
Get-UnifiedAuditLogRetentionPolicy | Sort-Object Priority |
Format-Table Priority, Name, RecordTypes, Operations, UserIds, RetentionDurationAny custom policy beats the default one-year policy, even a shorter one: a six-month policy for Exchange activity shortens those records for the users it covers. Policies created in PowerShell for record types the portal doesn't offer can't be edited in the portal; use Set-UnifiedAuditLogRetentionPolicy.
5. Investigate with MailItemsAccessed#
MailItemsAccessed covers POP, IMAP, MAPI, EWS, Exchange ActiveSync and REST, and records two access types:
- Sync: logged only for desktop Outlook for Windows or Mac, as one record per synced folder. Treat every item in that folder as exposed.
- Bind: individual message access. Binds within a two-minute window are combined into one record;
OperationCountshows how many, and each message is listed by its InternetMessageId.
$records = Search-UnifiedAuditLog -StartDate 2026-10-01 -EndDate 2026-10-08 `
-UserIds anna@contoso.com -Operations MailItemsAccessed -ResultSize 5000
$records | ForEach-Object {
$d = $_.AuditData | ConvertFrom-Json
[pscustomobject]@{
TimeUtc = $d.CreationTime
AccessType = ($d.OperationProperties | Where-Object Name -eq 'MailAccessType').Value
ClientIP = $d.ClientIPAddress
Client = $d.ClientInfoString
SessionId = $d.SessionId
Count = $d.OperationCount
}
} | Sort-Object TimeUtc | Format-Table -AutoSizeTake the attacker's context (IP address, client, session) from the sign-in logs. A sync in that context means assuming the whole mailbox was taken; otherwise the bind records list the messages opened. Find them by InternetMessageId in an eDiscovery case to judge the exposure; for rules and forwarding, see investigating a compromised mailbox.
Verify#
- A day after enabling the plan, sign in as a licensed test user, open a few messages in Outlook on the web and search the mailbox for a keyword.
- Search the audit log for
MailItemsAccessedandSearchQueryInitiatedExchangefor that user. Good looks like bind records with anOperationCountand a search record for the query. - Run
Get-UnifiedAuditLogRetentionPolicyand confirm your policies and priorities. The built-in default policy isn't listed there or in the portal; that's expected.
Tips & gotchas#
- Nothing is retroactive. A record's lifetime is fixed when it's ingested; license or policy changes only affect new records.
- Duplicates are filtered. Repeated binds of the same message within an hour, and syncs at one-hour intervals, don't create new records unless the context changes, such as a different IP address, client, folder or session.
- Offline reading is invisible. Mail read offline after a sync creates no record, so a sync in the attacker's context counts as full exposure.
- Automation: the Audit Search Graph API runs the same searches from code, with its own Microsoft Graph permissions.
- AI apps: auditing interactions with non-Microsoft 365 AI apps (such as Copilot Studio, Security Copilot or connected AI apps) requires pay-as-you-go billing. See DSPM for AI.

