๐ฅ Recipe ยท User & Group Management
List All Users in a Microsoft 365 Tenant with Name and Email
Export every user's first name, last name, and email address from Entra ID using the admin center, Microsoft Graph, or PowerShell
Complexity
Beginner
Impact
reporting + user-management + audit + low-risk + read-only
Context
Why This Matters
Getting a complete, accurate list of every user in your Microsoft 365 tenant is one of the most common admin tasks. Whether you're preparing a license audit, building a distribution list, handing a roster to HR, or validating that directory sync is producing the right set of accounts, you need a clean export of first name, last name, and email address.
The M365 admin center exposes this information in the Active users blade, but it caps the export at a fixed schema and doesn't always surface every user type (e.g., guest accounts, unlicensed service accounts). Microsoft Graph and the Microsoft.Graph PowerShell module give you full control over the fields returned, pagination, and filtering โ which is what you want for any list larger than a few hundred users.
Run this recipe whenever you need a point-in-time snapshot of your directory for reporting, migration planning, or compliance evidence.
Expected Outcomes
After completing this recipe you will have:
- A CSV or on-screen list of every user in the tenant showing givenName, surname, and mail (falling back to
userPrincipalNamewhen no primary SMTP is set). - A repeatable method โ manual, API, or scripted โ that correctly paginates through tenants with more than 100 users.
- A baseline you can diff against future exports to track joiners, leavers, and account changes.
Risks & Considerations
Watch out for
- Guest vs. member accounts. The default
/usersendpoint returns both. If you only want employees, filter with$filter=userType eq 'Member'. - Missing
mailvalues. Cloud-only accounts and unlicensed users often havemailset to null. Always fall back touserPrincipalNameso no row is blank. - Pagination. Graph returns at most 999 users per page. Always follow
@odata.nextLinkuntil it's absent โ otherwise your export is silently truncated. - PII handling. The exported CSV contains personal data. Store it on an encrypted share, limit who can read it, and delete it when the task is complete. Don't email it unencrypted.
- Throttling. On very large tenants (50k+), issue requests sequentially and respect
Retry-Afterheaders. Parallelizing the paged calls is not supported because each page depends on the previousnextLink.
Required Permissions
| Permission | Why It's Needed |
|---|---|
| User.Read.All | Minimum scope required to enumerate all users and read basic profile properties (givenName, surname, mail, userPrincipalName). |
| Directory.Read.All | Alternative broader scope if you also need to read related directory objects (groups, roles) in the same workflow. |
| Global Reader (role) | For interactive admin-center or PowerShell-with-delegated-auth usage, Global Reader is sufficient and avoids granting write privileges. |
The fastest way to get this done: just ask Dex. Copy the prompt below and paste it into your Dex conversation.
For IT Admins
Paste into Dex Pro
List users from the Microsoft 365 admin center
Best for small tenants or a quick one-off export.
- Sign in to the Microsoft 365 admin center as a Global Reader, User Administrator, or Global Administrator.
- In the left navigation, go to .
- Click Export users at the top of the list. Confirm the prompt to generate a CSV.
- Open the downloaded
Users_YYYY-MM-DD.csv. The columns you need are First name, Last name, and Primary email address. You can delete the other columns in Excel if you want a slimmer report.
If you also want guest users
- Go to and repeat the export, then combine the two CSVs.
When the admin center isn't enough
The admin center export truncates on tenants with unusual user types (resource mailboxes, shared mailboxes with accounts, B2B direct-connect users). For full fidelity, use the Graph API or PowerShell paths below.
List all users via Microsoft Graph
Use the /users endpoint with $select to return only the fields you need, and follow @odata.nextLink to page through the entire tenant.
Request
GET https://graph.microsoft.com/v1.0/users?$select=givenName,surname,mail,userPrincipalName&$top=999
Authorization: Bearer <access_token>
ConsistencyLevel: eventualSample response
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users",
"@odata.nextLink": "https://graph.microsoft.com/v1.0/users?$skiptoken=...",
"value": [
{
"givenName": "Patti",
"surname": "Fernandez",
"mail": "PattiF@contoso.onmicrosoft.com",
"userPrincipalName": "PattiF@contoso.onmicrosoft.com"
}
]
}Pagination
If @odata.nextLink is present, issue another GET against that exact URL (it already contains the $skiptoken) until the property disappears from the response. Concatenate the value arrays to get the full user list.
Useful filters
- Members only:
&$filter=userType eq 'Member' - Enabled accounts only:
&$filter=accountEnabled eq true(requiresConsistencyLevel: eventualand$count=true) - Licensed users only: combine with
assignedLicenses/$count ne 0via advanced queries
Permissions
Application or delegated scope User.Read.All (or Directory.Read.All). Admin consent is required for app-only flows.
List all users with the Microsoft.Graph PowerShell module
The Microsoft.Graph.Users module wraps Graph pagination for you โ Get-MgUser -All transparently follows every nextLink.
One-time setup
# Install the module (run once, as admin)
Install-Module Microsoft.Graph -Scope CurrentUserConnect and query
# Sign in with least-privilege scope
Connect-MgGraph -Scopes "User.Read.All"
# Retrieve all users, selecting only the fields we care about
$users = Get-MgUser -All -Property GivenName,Surname,Mail,UserPrincipalName `
| Select-Object `
@{Name='FirstName';Expression={$_.GivenName}},
@{Name='LastName'; Expression={$_.Surname}},
@{Name='Email'; Expression={ if ($_.Mail) { $_.Mail } else { $_.UserPrincipalName } }}
# Show on screen
$users | Format-Table -AutoSize
# Or export to CSV
$users | Export-Csv -Path ".\tenant-users.csv" -NoTypeInformation -Encoding UTF8
Disconnect-MgGraphNotes
-Allis essential โ without itGet-MgUserreturns only the first page (100 users by default).- The calculated
Emailcolumn falls back to UPN so rows are never blank. - On tenants with >25,000 users, add
-PageSize 999to minimize round-trips.
Next steps