SC-200 Microsoft 365 Investigations: Purview Audit, Content Search and Graph Activity Logs
Investigating Microsoft 365 activities for SC-200 means using three evidence sources to find out what an attacker did: Microsoft Purview Audit for activity records, Content search in Microsoft Purview eDiscovery for the content itself, and Microsoft Graph activity logs for API calls made against your tenant. This lesson covers who can search the audit log and how long records last, how to run and scope audit searches, how MailItemsAccessed scopes a mailbox compromise, how to pull audit data programmatically, how to build KeyQL content searches and purge malicious email, and how to turn on and query Graph activity logs.
On this page10 sections
- Which Microsoft 365 investigation tool answers which question
- Turning on Purview Audit and getting search access
- How long audit records are kept: Standard, Premium and retention policies
- Running an audit search in the Purview portal
- Scoping mailbox compromise with MailItemsAccessed
- Getting audit data out programmatically
- Content search in Microsoft Purview eDiscovery
- KeyQL syntax that changes the result set
- Removing malicious email with search and purge
- Investigating with Microsoft Graph activity logs
- Choose between Purview Audit, eDiscovery Content search and Microsoft Graph activity logs for a given investigation question
- Assign least-privileged audit and eDiscovery roles and confirm that unified audit log ingestion is on
- Predict how long an audit record is kept from the user's licence, the workload and any custom retention policy
- Run and scope Purview audit searches within their limits, and use MailItemsAccessed to scope a mailbox compromise
- Pick the right programmatic route for audit data: Search-UnifiedAuditLog, the Graph audit log query or the Office 365 Management Activity API
- Write correct KeyQL queries and remove malicious email with search and purge
- Turn on Microsoft Graph activity logs and query them in Log Analytics or Defender XDR advanced hunting
Which Microsoft 365 investigation tool answers which question
SC-200 names three Microsoft 365 investigation sources: Microsoft Purview Audit records what people and apps did, Content search in Microsoft Purview eDiscovery finds what content exists, and Microsoft Graph activity logs record which Graph API calls were made against your tenant. Choosing the wrong one is the commonest mistake: an audit search can't tell you whether a message body contains a phrase, and a content search can't tell you who downloaded a file.
| Source | What it holds | Typical SOC question | Where you query it |
|---|---|---|---|
| Microsoft Purview Audit (unified audit log) | Activity records from Exchange, SharePoint, OneDrive, Teams, Microsoft Entra ID and other services | Who downloaded this file? Did the attacker create an inbox rule? Which messages did they read? | Audit search in the Purview portal, Search-UnifiedAuditLog, the Graph audit log query, the Office 365 Management Activity API |
| Content search (eDiscovery) | The content itself: mailbox items, OneDrive and SharePoint documents, Teams messages | Which mailboxes received this phishing message? Where does the leaked file name appear? | A search in an eDiscovery case or the Content Search case; search and purge for email |
| Microsoft Graph activity logs | Every HTTP request made to Microsoft Graph for resources in your tenant | Which API calls did this compromised app or service principal make, and were any refused? | The MicrosoftGraphActivityLogs table in Log Analytics or Microsoft Sentinel; GraphAPIAuditEvents in Defender XDR advanced hunting |
The three are complementary. A typical business email compromise investigation uses audit search to find the attacker's sign-in session and inbox rules, MailItemsAccessed records to scope which mail was read, content search to find every copy of the phishing message, and search and purge to remove it. Alert triage in the Defender workloads belongs to the sibling topic on Defender alerts; this lesson is about the Microsoft 365 evidence behind them.
Turning on Purview Audit and getting search access
To search the audit log you need the Audit Logs or View-Only Audit Logs role in the Microsoft Purview portal, and auditing must be on for the tenant. Both roles allow searching and exporting; Audit Logs also allows turning auditing on or off, so View-Only Audit Logs is the least-privileged choice for analysts who only search.
Two places to assign the same roles
The portal and the cmdlets use different role systems. A role assigned in the Purview portal lets you search in the portal; to run Search-UnifiedAuditLog and the other audit cmdlets, the user needs Audit Logs or View-Only Audit Logs assigned in the Exchange admin center (Exchange Online RBAC). Portal access doesn't carry over to PowerShell: each role system needs its own assignment.
Checking and changing whether auditing is on
Auditing is on by default in Microsoft 365 organisations, but it can be turned off. Check and change it in Exchange Online PowerShell:
Get-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabled
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true
The same Get- cmdlet exists in Security & Compliance PowerShell, but there its UnifiedAuditLogIngestionEnabled value is always False, even when auditing is on. If portal searches return recent records, auditing is on; read the value in Exchange Online PowerShell before raising a change. Turning ingestion on captures activity from that moment; earlier activity is not backfilled.
Running an audit search in the Purview portal
An audit search in the Purview portal is an asynchronous search job: you set a date range and filters, start it, and read the results from the search job dashboard when it completes. Jobs keep running after you close the browser, and completed jobs are kept for 30 days, so a long search can be started at the end of a shift and opened the next morning.
Limits that shape an investigation
| Limit | Behaviour |
|---|---|
| Concurrent jobs | Up to 10 running per user, but only one unfiltered job (no workload, record type, user or activity filter). A second all-blank search won't start; add any filter or wait. |
| Date range | Up to 180 days per search; what you can find also depends on retention. |
| Latency | Microsoft doesn't guarantee a time. For core services (Exchange, SharePoint, OneDrive, Teams) records typically appear 60 to 90 minutes after the event, so an empty result for the last half hour usually means "not yet". Defender XDR advanced hunting is the faster source for near-real-time signals. |
| Export | Up to 50,000 rows for Audit (Standard) and up to 1,000,000 for Audit (Premium). For larger Standard result sets, run several narrower searches (for example, shorter date ranges). Copying a search reproduces the same result set. |
Search criteria
- Activities - operations names must be the exact operation name, such as
New-InboxRuleorFileDownloaded. A description such as "New inbox rule" returns nothing. The Activities - friendly names list lets you pick activities by description instead. - Record types and Workloads narrow by source; a workload filter on its own is tenant-wide.
- File, folder, or site takes part or all of a name or URL, with
*as a wildcard at the end.https://fabrikam.sharepoint.com/sites/Projects*returns that site and everything beneath it. - Keyword Search searches only the indexed common schema of the record, not the audit data content. Values that sit in an operation's parameters (an inbox rule's ForwardTo address, for example) are not found by keyword; filter on the operation, then read the parameters in the record details or export.
Administrative units
An admin assigned one or more Purview administrative units is a restricted admin: they can search and export only user-generated records within their units. Some operations, including Exchange Set-Mailbox, Set-MailboxPlan and SupervisionBulkEmailExclusion, are visible only to unrestricted admins (no unit assignment).
Scoping mailbox compromise with MailItemsAccessed
MailItemsAccessed is the mailbox audit action that records when mail is read or synced, and it is part of Audit (Standard): it is on by default for users with Office 365 or Microsoft 365 E3/E5 licences. An E3 organisation can therefore scope which messages an attacker accessed without buying an upgrade. Audit (Premium) adds intelligent insights such as extra properties (for example SensitivityLabel) on the records, not the event itself. The records are in the unified audit log, so portal search, Search-UnifiedAuditLog and the APIs all return them.
Sync versus Bind
| MailAccessType | When it is written | How to scope exposure |
|---|---|---|
| Sync | Desktop Outlook downloads a folder's items to the local computer; one record per folder, not per item | Assume every item in the synced folder is compromised; once synced it can be read offline with no further audit record |
| Bind | An individual message is accessed (for example in Outlook on the web); records list each message's InternetMessageId, aggregated over two-minute windows | Treat the messages named by InternetMessageId as exposed |
De-duplication and separating attacker from owner
To control volume, duplicate records for the same bind within an hour are filtered out, unless the context differs: ClientIPAddress, ClientInfoString, ParentFolder, Logon_type, MailAccessType, MailboxUPN, User or SessionId. For example, a user who opens a message in Outlook on the web and, 40 minutes later, in the Outlook mobile app produces two records, because ClientInfoString and SessionId differ; opening it again in the same browser session within the hour adds nothing. The audit trail shows when each context first touched a message, not every repeat.
In a compromised account the attacker signs in as the owner, so the identity fields can't tell them apart. To separate them, compare SessionId (the sign-in session) and ClientIPAddress (the network), helped by ClientInfoString. Find the attacker's session in the sign-in data first, then filter MailItemsAccessed records to it.
Other mailbox evidence comes from the same audit log: inbox rules created with New-InboxRule or changed with Set-InboxRule, and mailbox forwarding set with Set-Mailbox.
Getting audit data out programmatically
Use Search-UnifiedAuditLog for ad hoc scripted searches, the Microsoft Graph audit log query for filtered searches over REST, and the Office 365 Management Activity API for a continuous feed into a SIEM. Microsoft's guidance for programmatically downloading audit data is the Management Activity API rather than a PowerShell script.
| Method | Shape | Use it when |
|---|---|---|
Search-UnifiedAuditLog | Exchange Online cmdlet; run in Exchange Online PowerShell (Connect-ExchangeOnline), not Security & Compliance, Entra or Azure PowerShell | An analyst scripts a one-off pull, such as the last day's records for a list of accounts |
Microsoft Graph auditLogQuery (security API) | Create a query with filters (date range, record types, services, operations, user principal names, IP addresses, object IDs, administrative units), poll its status, then read its records | A filtered historical search from code, without PowerShell |
| Office 365 Management Activity API | Subscriptions per content type; the collector lists available content and downloads content blobs | A SIEM ingests audit data continuously, for correlation and retention beyond Purview's |
Management Activity API windows
When listing content, startTime and endTime must be no more than 24 hours apart, and the start no more than seven days in the past; older content can't be retrieved (error AF20051). Restarting a subscription gives access only to content from then on, it doesn't replay the past. A gap in the feed is therefore recoverable from the API only for its most recent seven days, in windows of 24 hours or less; anything older has to come from the audit log itself, which still holds it for at least 180 days. Audit (Premium) raises the API's bandwidth allocation.
Graph activity logs, covered below, are a different source: they describe calls made to Microsoft Graph, not Microsoft 365 workload activity.
Content search in Microsoft Purview eDiscovery
Content search now means a search in the unified eDiscovery experience, either inside a case or in the organisation's built-in Content Search case, which is meant for searches not tied to a legal matter. Microsoft retired the classic eDiscovery experiences, including classic Content search, on 31 August 2025, so there is no standalone classic search page to use.
Permissions
To create and run searches, a user needs membership of the eDiscovery Manager role group or the Compliance Search role. Deleting messages is a separate right (see search and purge), so an organisation can control who searches mailboxes separately from who deletes. Audit roles and Organization Configuration belong to Purview Audit, not eDiscovery.
Building and checking a search
- Data sources: choose the mailboxes, OneDrive accounts, SharePoint sites and Teams locations to search, or tenant-wide sources.
- Condition builder or KeyQL editor: the condition builder adds condition cards (keywords, date, sender and so on). The KeyQL editor takes a written query; existing conditions are converted to KeyQL when you switch, and a KeyQL condition must stand alone, so further criteria such as a date range go into the KeyQL text joined with
AND. - Choose search results: Statistics shows estimated hit counts arranged by top indicators, optionally with the query keywords report (hits per term); Sample shows a representative selection with a chosen number of items per location. Together they let you check a query before you add results to a review set or export.
KeyQL syntax that changes the result set
Keyword Query Language (KeyQL) is the query syntax for eDiscovery searches, and a few rules decide whether a query returns exactly the intended set or thousands of unrelated items.
| Rule | Effect | Example |
|---|---|---|
| A space between terms means OR | from:"Priya Shah" subject:budget returns everything from Priya plus everything with that subject | Write from:"Priya Shah" AND subject:budget |
| Operators must be uppercase (AND, OR, NOT, NEAR) | Lowercase and is treated as a keyword | kind:email AND from:"Lee Gu" |
| Quote multi-word property values | subject:Quarterly forecast matches "Quarterly" in the subject and "forecast" anywhere | subject:"Quarterly forecast" |
| No space after the colon | subject: invoice no longer filters on the property | subject:invoice |
| Wildcards are prefix only | Suffix (*voice), infix (in*oice) and substring (*invoic*) searches aren't supported | subject:invoic* |
| Email has separate dates | received is when a message arrived; sent is when it was sent | received:2026-10-01..2026-10-31 |
| Item kinds | kind:email and kind:microsoftteams separate mail from Teams chats | kind:microsoftteams AND invoice |
Site-only paths need a trailing / | Without it, sites with a similar path (HR-Old) also match | path:"https://fabrikam.sharepoint.com/sites/HR/" |
| Proximity | NEAR(n) allows n or fewer words between terms; bare NEAR defaults to 8 | acquisition NEAR(3) Fabrikam |
Date ranges use two dots between the start and end dates. When a query returns far more than expected, check first for a missing uppercase AND and for unquoted multi-word values; the query keywords report shows which term is responsible.
Removing malicious email with search and purge
Search and purge deletes the email found by an eDiscovery search from users' mailboxes, using New-ComplianceSearchAction -Purge in Security & Compliance PowerShell (or Microsoft Graph in a premium case). Purge actions need a search-only session: connect with Connect-IPPSSession -EnableSearchOnlySession using Exchange Online PowerShell module 3.9.0 or later, or the action fails. Where Microsoft Defender for Office 365 Plan 2 is available, Microsoft recommends its remediation (for example from Threat Explorer) instead; search and purge is the route for organisations without it.
Permissions
Purging needs the Search And Purge role or the Organization Management role group in the Microsoft Purview portal. Membership of Organization Management in Exchange Online doesn't count. Without the right, the cmdlet simply has no -Purge parameter, producing the error "A parameter can't be found that matches parameter name 'Purge'". Creating the search itself needs eDiscovery Manager or Compliance Search.
Soft delete or hard delete
| PurgeType | Where the message goes | Can the user recover it? |
|---|---|---|
SoftDelete | The Recoverable Items folder (Deletions) | Yes, for the deleted item retention period; use it when some matches may be legitimate |
HardDelete | Marked for permanent removal; moved to the Purges folder | No; even with single item recovery or a hold it is kept only for the organisation |
Limits
- Items per location: in a case without premium features only PowerShell can purge, up to 10 items per mailbox per action; rerun the action to remove more. Premium (E5) cases can purge up to 100 items per location through Microsoft Graph (
purgeData). Don't mix PowerShell and Graph purges for the same search. - Mailboxes per search: a search used for a purge can cover at most 50,000 mailboxes, and the purge fails if it covers more, even when fewer contain matches. In larger tenants, split the mailboxes with temporary search permissions filters (by department, location and so on) and purge each subset.
- Scope: mailboxes and public folders only. Teams chat messages are removed through a separate process, and SharePoint or OneDrive files aren't purged this way. Unindexed items aren't purged, and when a mailbox is on litigation hold, purged items are kept in Recoverable Items rather than permanently deleted.
Investigating with Microsoft Graph activity logs
Microsoft Graph activity logs are an audit trail of every HTTP request made to Microsoft Graph for resources in your tenant, delivered through a Microsoft Entra diagnostic setting as the MicrosoftGraphActivityLogs category. They show what a compromised app, service principal or user token did through the API, which sign-in logs alone can't.
Turning them on
- Licence: Microsoft Entra ID P1 or P2 for the tenant. Purview Audit licensing plays no part.
- Role: Security Administrator is the least-privileged role that can create the diagnostic setting; Global Administrator works but is broader.
- Destination: a Log Analytics workspace (analysis in Azure Monitor or Microsoft Sentinel), an Azure Storage account (archive), or an event hub (streaming to a third-party SIEM), in an Azure subscription where the admin has permission to use it (for a storage account, the right to list its keys).
- No filtering in the diagnostic setting. To cut cost in a workspace, apply a workspace transformation to the table to drop rows (for example an internal inventory tool's routine reads) or columns. The Basic plan also lowers cost but limits how the table can be queried, so it suits tables kept for occasional investigation rather than ones that detections run against.
- Delivery and scope: most events arrive within 30 minutes, some take up to two hours. Logs exist only from when the setting is configured (no backfill), and they cover the resource tenant: calls a multitenant app makes into a customer's tenant appear in that customer's logs, not the publisher's.
Querying the logs
Key columns are AppId, ServicePrincipalId, UserId, RequestUri, RequestMethod, ResponseStatusCode, IPAddress, Roles, Scopes, ClientAuthMethod and SignInActivityId.
- Permission probing: refused requests show
ResponseStatusCode401 or 403; 429 is throttling and 5xx are service errors. - App-only versus delegated: a call with application permissions has an empty
UserIdand its app roles inRoles; a delegated call carries the user'sUserIdand delegated permissions inScopes.ClientAuthMethodis 0 for a public client, 1 for a client secret and 2 for a certificate. - Linking a request to its sign-in: join
SignInActivityIdtoUniqueTokenIdentifierin the sign-in table that matches the actor: SigninLogs or AADNonInteractiveUserSignInLogs for users, AADServicePrincipalSignInLogs for service principals, AADManagedIdentitySignInLogs for managed identities.
This query lists which apps and users changed service principals (for example by adding credentials) over the last week:
MicrosoftGraphActivityLogs
| where TimeGenerated >= ago(7d)
| where RequestUri has "/servicePrincipals"
| where RequestMethod in ("POST", "PATCH", "DELETE")
| where ResponseStatusCode between (200 .. 299)
| summarize Calls = count() by AppId, UserId, RequestMethod
| order by Calls desc
In Defender XDR advanced hunting, the equivalent data is the GraphAPIAuditEvents table (columns such as RequestUri, RequestMethod, ResponseStatusCode, ApplicationId, ServicePrincipalId and IpAddress). MicrosoftGraphActivityLogs exists only in a Log Analytics workspace, so an analyst without a workspace uses GraphAPIAuditEvents.
Tip. Questions on this topic set a Microsoft 365 investigation scenario, with the organisation's licences, roles and tooling, and ask what to use, assign, run or expect. Expect to pick the right evidence source or API, the least-privileged role, the correct PowerShell session or cmdlet, and the right search criterion; to predict how long a record survives given the user's licence, workload and retention policies; to scope mailbox exposure from MailItemsAccessed records; to choose or complete KeyQL and KQL queries; and to explain why a search, purge or log pipeline returned nothing or failed.
- Audit records activity, Content search finds content, Graph activity logs record Graph API calls: match the tool to the question.
- View-Only Audit Logs is least privilege for audit search; assign it in the Exchange admin center too for Search-UnifiedAuditLog.
- UnifiedAuditLogIngestionEnabled is reliable only in Exchange Online PowerShell; Security & Compliance PowerShell always shows False.
- Audit (Standard) keeps 180 days; the one-year default needs an E5 user and an Entra ID, Exchange, OneDrive or SharePoint record.
- Custom retention policies beat the default even when shorter, lower priority numbers win, and changes are never retroactive.
- Audit search jobs run on after the browser closes, last 30 days, allow one unfiltered job, and usually lag 60 to 90 minutes.
- MailItemsAccessed is in Audit (Standard); a Sync record means assume the whole folder was taken, and SessionId plus ClientIPAddress separate attacker from owner.
- SIEM feed = Office 365 Management Activity API (7-day content window); REST search = Graph auditLogQuery; scripts = Search-UnifiedAuditLog in Exchange Online PowerShell.
- KeyQL: space is OR, operators uppercase, quote multi-word values, prefix-only wildcards, trailing slash for site-only paths.
- Search and purge needs Search And Purge in Purview, removes 10 items per mailbox per run (100 via Graph in premium cases), and fails over 50,000 searched mailboxes.
Frequently asked questions
What is the difference between Purview Audit and Content search?
Microsoft Purview Audit records activities, such as who downloaded a file, created an inbox rule or read a message, in the unified audit log. Content search in Purview eDiscovery searches the content itself, such as the text of email, documents and Teams messages. Use Audit to reconstruct what an attacker did and Content search to find every copy of a malicious message or leaked file.
How long does Microsoft Purview keep audit records?
Audit (Standard) keeps audit records for 180 days. With Audit (Premium), records from Exchange, SharePoint, OneDrive and Microsoft Entra ID generated by E5-licensed users are kept for one year by default, while other workloads, non-E5 users and guests keep 180 days. Custom audit log retention policies can set other durations, and ten-year retention needs the 10-Year Audit Log Retention add-on on the users who generate the records.
Does MailItemsAccessed require Audit (Premium)?
No. MailItemsAccessed is part of Audit (Standard) and is on by default for users with Office 365 or Microsoft 365 E3 or E5 licences. It records both Sync access, where desktop Outlook downloads a folder, and Bind access to individual messages. Audit (Premium) adds extra properties to the records but is not needed to see which mail an attacker accessed.
Why does my Purview audit search return nothing for recent activity?
Audit records are not instant. Microsoft doesn't guarantee a time, but for core services such as Exchange, SharePoint, OneDrive and Teams, records typically become searchable 60 to 90 minutes after the event. Other causes of empty results are an operation name that isn't typed exactly, a keyword that sits only in the record's data content, or records that have passed their retention period.
How do I send Microsoft 365 audit logs to a SIEM?
Use the Office 365 Management Activity API, which Microsoft recommends over PowerShell scripts for programmatic download. The SIEM's collector subscribes to content types, lists available content in windows of up to 24 hours and downloads the content blobs. Content older than seven days can't be retrieved from the API, so gaps longer than that are filled from an audit search.
What permissions do I need to purge email in Microsoft Purview?
Searching needs the eDiscovery Manager role group or the Compliance Search role, and purging needs the Search And Purge role or the Organization Management role group in the Microsoft Purview portal. Organization Management in Exchange Online doesn't grant purge rights. Without them, New-ComplianceSearchAction has no Purge parameter and returns a parameter-not-found error.
How do I turn on Microsoft Graph activity logs?
Create a Microsoft Entra diagnostic setting that sends the MicrosoftGraphActivityLogs category to a Log Analytics workspace, a storage account or an event hub. The tenant needs Microsoft Entra ID P1 or P2, and Security Administrator is the least-privileged role that can create the setting. The setting can't filter the logs, so use a workspace transformation to drop unwanted rows, and expect most events within 30 minutes.
Can I query Microsoft Graph activity logs in Defender XDR advanced hunting?
Yes, through the GraphAPIAuditEvents table, which holds Microsoft Graph API requests for resources in the tenant with columns such as RequestUri, RequestMethod, ResponseStatusCode, ApplicationId and ServicePrincipalId. The MicrosoftGraphActivityLogs table is a separate Log Analytics table created by the Entra diagnostic setting, queried in a workspace or Microsoft Sentinel.
Source
This lesson covers the "Respond to security incidents" domain of the official SC-200 exam guide. Vendors revise their guides — check the source for the current version.
- Microsoft SC-200 study guide — Microsoft Learn
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.