The Purview portal (Audit → Search) lets you search the Unified Audit Log and export the results to CSV. The full event sits in one column, AuditData, as a JSON blob. To get clean JSON, one event per line, use Exchange Online PowerShell and Search-UnifiedAuditLog. The script is below. Standard audit retention is 180 days; Audit (Premium) keeps a year by default.
The Unified Audit Log covers Exchange, SharePoint, OneDrive, Teams and admin activity in one place. In a business email compromise, it’s where the inbox rules, forwarding and mass downloads show up. Prime Core has 79 detection rules for Microsoft 365 audit data, mapped to 33 MITRE ATT&CK techniques. Among them: forwarding rules that siphon mail (T1114.003), extra mailbox permissions (T1098.002) and data pulled from SharePoint (T1213.002).
Before you start
Is auditing on? It is by default for most tenants, but check:
Connect-ExchangeOnline
Get-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabled
True is what you want. If it’s False, nothing was recorded, and turning it on won’t backfill.
Roles. You need the Audit Logs or View-Only Audit Logs role. You also need the ExchangeOnlineManagement module (Install-Module ExchangeOnlineManagement).
Latency. Events don’t appear instantly. Most show up within an hour or two, some take longer. If you’re chasing something from this morning, it may not be searchable yet.
Export with PowerShell
Search-UnifiedAuditLog returns at most 5,000 records per call. With a session ID and ReturnLargeSet, you can page through up to 50,000 per search. This loop does that and writes each record’s AuditData, already JSON, as one line:
$session = [guid]::NewGuid().ToString()
$out = "ual_2026-10-19.jsonl"
do {
$batch = Search-UnifiedAuditLog -StartDate "2026-10-19 00:00" -EndDate "2026-10-20 00:00" `
-SessionId $session -SessionCommand ReturnLargeSet -ResultSize 5000
$batch | ForEach-Object { $_.AuditData } | Add-Content $out
} while ($batch.Count -eq 5000)
The log stores times in UTC. Keep that in mind when you set the window.
What to look for
| Operation | Why it matters |
|---|---|
| New-InboxRule, Set-InboxRule, UpdateInboxRules | Rules that forward, move or delete mail; check the parameters in AuditData |
| Set-Mailbox with ForwardingSmtpAddress | Mailbox-level forwarding to an outside address |
| Add-MailboxPermission, Add-RecipientPermission | Someone getting access to another mailbox |
| MailItemsAccessed | Which messages were actually read; key to scoping a breach |
| FileDownloaded, FileSyncDownloadedFull bursts | Bulk download from SharePoint or OneDrive |
| Consent to an application | A rogue app granted access to mail or files |
The classic BEC sequence: a sign-in from a new location, then within minutes an inbox rule that moves replies from the finance team into an obscure folder.
Upload, sanitize and review in LogTotal
- Upload. Open logtotal.com and drop in the .jsonl file.
- Sanitize.
- Review.
When it goes wrong
| Symptom | Likely cause | Fix |
|---|---|---|
| Search-UnifiedAuditLog isn’t recognized | Not connected to Exchange Online PowerShell | Connect-ExchangeOnline first |
| No results | Auditing off, events too new, or outside retention | Check ingestion; wait; check your audit tier |
| Exactly 5,000 results | Per-call limit hit without a session | Use the session loop above |
| Stuck at 50,000 | Session limit | Split the window into smaller pieces |
| Access denied | Missing audit role | Ask for View-Only Audit Logs |
FAQ
Isn’t this the same as Entra sign-in logs?
They overlap: the UAL has UserLoggedIn events. But for authentication details (MFA, Conditional Access, risk), Entra sign-in logs are far richer. Export both.
What about the Management Activity API?
That’s the route for continuous collection into a SIEM. For a one-off investigation, PowerShell is simpler.