Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.
🚀 Launch ToolkitManaging administrative roles in Microsoft 365 is a critical security practice. Over time, users accumulate multiple admin privileges, and it becomes difficult to track who has access to what. This can introduce security risks and hinder proper delegation of responsibilities.
Using Microsoft Graph PowerShell, administrators can quickly identify all users who hold admin roles, including their sign-in status and assigned roles. This script automates the process — fetching the data, exporting it to CSV, and emailing the report to the administrator.
# ============================
# Config
# ============================
# Admin mailbox to receive the report
$AdminUPN = "admin@yourtenant.onmicrosoft.com" # <-- replace
# Connect to Microsoft Graph
# Required scopes: Directory.Read.All (roles & members), User.Read.All (user props), Mail.Send (email)
Connect-MgGraph -Scopes "Directory.Read.All" ,"User.Read.All","Mail.Send"
#= = = = = = = = = = = = = = = = = = = = = = = = = = = =# 1) Fetch users with admin roles
#= = = = = = = = = = = = = = = = = = = = = = = = = = = =# Get all activated directory roles (e.g., Global Admin, Helpdesk Admin)
$activatedRoles=Get-MgDirectoryRole -All
# Hashtable to store users and their roles
$adminUserMap=@{}
foreach ($role in $activatedRoles) {
try {
# Get members of the role (could include users, groups, service principals)
$members=Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id -All
foreach ($member in $members) {
# Process only user objects
if ($member.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.user" ) {
$userId=$member.Id
# Fetch detailed user info only once
if (-not $adminUserMap.ContainsKey($userId)) {
$user=Get-MgUser -UserId $userId -Property DisplayName, UserPrincipalName, AccountEnabled
$adminUserMap[$userId]=[PSCustomObject]@{
'Admin Name' =$user.DisplayName
'User Principal Name' =$user.UserPrincipalName
'Sign In Status' =if ($user.AccountEnabled) { "Enabled" } else { "Disabled" }
'Admin Roles' =$role.DisplayName
}
} else {
# Append additional role to existing user
$adminUserMap[$userId].'Admin Roles' +=", $($role.DisplayName)"
}
}
}
} catch {
Write-Warning "Error while processing role '$($role.DisplayName)': $_"
}
}
# Optional console output (unchanged)
$adminUserMap.Values | Sort-Object 'Admin Name' | Format-Table -AutoSize
#= = = = = = = = = = = = = = = = = = = = = = = = = = = =# 2) Export to CSV
#= = = = = = = = = = = = = = = = = = = = = = = = = = = =$ReportRows =@()
if ($adminUserMap.Count -gt 0) {
$ReportRows=$adminUserMap.Values | Sort-Object 'Admin Name'
}
$ReportPath="$env:TEMP\AdminUsersWithRoles.csv"
# Ensure consistent column order even if empty
$ReportRows | Select-Object 'Admin Name' ,'User Principal Name','Sign In Status','Admin Roles' |
Export-Csv -Path $ReportPath -NoTypeInformation -Encoding UTF8
#= = = = = = = = = = = = = = = = = = = = = = = = = = = =# 3) Email the report to the administrator
#= = = = = = = = = = = = = = = = = = = = = = = = = = = =$userCount =$adminUserMap.Count
$Subject="Admin Users with Roles Report — $(Get-Date -Format 'yyyy-MM-dd')"
$Body=@"
Hello Admin,
Attached is the latest report of users with admin roles in the tenant.
Total admin users: $userCount.
Regards,
Graph PowerShell Script
"@
# Read and attach the CSV
$AttachmentContent = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($ReportPath))
$Attachments = @(
@{
"@odata.type" = "#microsoft.graph.fileAttachment"
Name = [System.IO.Path]::GetFileName($ReportPath)
ContentBytes = $AttachmentContent
}
)
# Build the message payload
$Message = @{
Message = @{
Subject = $Subject
Body = @{
ContentType = "HTML"
Content = $Body
}
ToRecipients = @(
@{ EmailAddress = @{ Address = $AdminUPN } }
)
Attachments = $Attachments
}
SaveToSentItems = "true"
}
# Send the email
Send-MgUserMail -UserId $AdminUPN -BodyParameter $Message
Write-Host "Admin users with roles report emailed successfully to $AdminUPN"
| Error | Cause | Solution |
|---|---|---|
| Insufficient privileges to complete the operation | Missing Graph API permissions. | Ensure you connect with Directory.Read.All, User.Read.All, and Mail.Send scopes. |
| Resource not found | The $AdminUPN specified is not a valid mailbox. | Replace $AdminUPN with a valid mailbox-enabled account. |
| Empty CSV File | No admin users were retrieved or insufficient access. | Verify permissions and ensure directory roles exist in the tenant. |
| API Throttling in Large Tenants | Too many calls to retrieve role members in a single run. | Implement retry logic or introduce small delays between Graph calls. |
This Graph PowerShell script gives administrators a complete and automated view of users holding admin roles in Microsoft 365. By sending the results directly via email, it simplifies governance reviews and helps maintain tighter control over privileged access.
With small enhancements like scheduling, additional metadata, or integration with alerting systems, this script can become a vital part of your Microsoft 365 identity and role auditing toolkit.
© m365corner.com. All Rights Reserved. Design by HTML Codex