If you have been working in identity governance for any length of time, you have almost certainly encountered the question: “Is this account actually being used?” It sounds simple. It is not. In a mature Microsoft 365 environment, answering it properly requires pulling together sign-in data from Entra ID, mailbox activity from Exchange Online, and enough freshness in the data to make a governance decision you can defend.
This post introduces a new Granfeldt PSMA — mim-m365-activity — that does exactly that, and specifically digs into why the Microsoft Graph getEmailActivityUserDetail endpoint is the right tool for the Exchange side of this problem.
Why people want this data
The classic driver is licence cost (or security compliance). An Exchange Online Plan 2 licence sitting on an account that last received an email two years ago represents a straightforward saving once you can prove the inactivity. But the value does not stop at procurement.
- Access certification: Reviewers need real activity signals, not just “account exists.” A last-activity date from Exchange and a last sign-in date from Entra together give a much more honest picture of risk than either alone.
- Orphan and stale account detection: AD accounts that have been off-boarded from HR but never disabled often show no Entra sign-in activity but may still have mail flow, forwarding rules, or delegation — the mailbox side tells the full story.
- Joiner–mover–leaver automation: Being able to suppress or extend provisioning actions based on whether an account has been genuinely active makes lifecycle rules far more defensible.
- Audit and compliance: Many regulatory frameworks require evidence that privileged and service accounts are monitored. Having this data flowing into MIM’s metaverse means it can be reported on, acted on, and exported to whatever governance tooling is downstream.
The frustrating part, historically, has been getting this data into MIM in a clean, reliable, and maintainable way. The Graph Reports API solves the Exchange half of this cleanly.
The getEmailActivityUserDetail endpoint
The Microsoft Graph reportRoot: getEmailActivityUserDetail endpoint returns a per-user breakdown of Exchange Online mail activity over a specified reporting window (D7, D30, D90, or D180). What makes it genuinely useful for governance work rather than just reporting dashboards is the combination of fields it returns:
| Field | Type | Governance value |
|---|---|---|
User Principal Name | string | Join key to Entra / MIM objects |
Last Activity Date | date | Core inactivity signal for mailbox |
Send Count | int | Distinguish read-only from active senders |
Receive Count | int | Detect mail flow without human interaction |
Read Count | int | Confirm genuine user engagement |
Is Deleted | bool | Soft-delete detection |
Deleted Date | date | Retention / recovery window tracking |
Assigned Products | string | Licence reconciliation |
Note on privacy settings: By default, Microsoft anonymises user-identifying data in usage reports at tenant level. You need to turn off the “Show concealed user, group, and site names in all reports” setting in the Microsoft 365 admin centre to see real UPNs. The PSMA README covers this.
The endpoint is available on both the /v1.0 and /beta tracks. The beta variant also supports JSON output via ?$format=application/json which simplifies parsing, though the v1.0 CSV response is stable and well-suited to the PowerShell pipeline used here.
The call itself is minimal — a single authenticated GET against the reports endpoint with a period parameter.
# Get email activity for the last 30 days
$uri = "https://graph.microsoft.com/v1.0/reports/getEmailActivityUserDetail(period='D30')"
The required Graph permission is Reports.Read.All (application permission). No delegated permission is needed for the PSMA use case — the management agent authenticates as an app registration with a client secret or certificate.
How the PSMA works
The management agent combines two data sources per user object: the Graph Reports API (Exchange activity) and the Graph /users/{id}/signInActivity endpoint (Entra sign-in data). These are merged on UPN and presented as a single flat object in the MIM connector space.
Repository structure
The repo follows the standard Granfeldt PSMA layout — three scripts and a schema file:
PSSchema.ps1 — defines the object class and attribute schema presented to MIM Sync
PSImport.ps1 — full import script; calls both Graph endpoints and merges results
App registration requirements
Create a dedicated app registration in Entra ID. The MA needs three Graph application permissions:
Reports.Read.All— for the email activity reportAuditLog.Read.All— for the sign-in activity data- User.Read.All – for all users data
All require admin consent. A certificate credential is recommended over a client secret for production deployments.
Authentication
Merging the two data sources
MIM Sync configuration
Once the connector space is populated, the activity attributes flow into the MIM metaverse via standard inbound synchronisation rules. From there they are available to:
- Outbound sync rules that set a stale-account flag on the AD user object
- MIM Service workflows triggered by a calculated “days since last activity” attribute
- Reporting exports via MIM’s built-in reporting or a downstream BI connector
Getting started
The full implementation — including the schema script, import script, settings template, and a detailed README covering app registration, privacy settings, and MIM configuration — is available on GitHub.
As always, if you hit something interesting in your own tenant — an edge case with guest accounts in the reports data, or a pattern worth sharing — contributions and issues on the repo are welcome.

