🔧 New: User Management Graph PowerShell Toolkit

Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.

🚀 Launch Toolkit

Fetch and Email Microsoft 365 Users with Admin Roles

Managing 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.


i) The Script


    # ============================
    # 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"

ii) How the Script Works

  1. Configuration – The administrator’s mailbox is defined in $AdminUPN, which will receive the emailed report.
  2. Connect to Microsoft Graph – The script connects using the necessary permissions:
    • Directory.Read.All → to read role and directory information
    • User.Read.All → to retrieve user properties
    • Mail.Send → to send the report via email
  3. Retrieve Roles and Members –
    • The script first retrieves all active directory roles using Get-MgDirectoryRole.
    • It then enumerates the members for each role using Get-MgDirectoryRoleMember.
  4. Map Admins and Roles – For each user, it retrieves their display name, UPN, sign-in status, and roles. If the user has multiple roles, they are appended to the same record.
  5. Generate Report – The report is exported to CSV with columns for Admin Name, User Principal Name, Sign-In Status, and Admin Roles.
  6. Email the Report – The CSV is attached to an email and sent automatically to the administrator with a summary count in the email body.

iii) Further Enhancements

  • Include Role Descriptions – Add a column for each role’s purpose or privileges.
  • Add Department Info – Include the user’s department or job title for context.
  • Scheduled Execution – Run weekly or monthly using Task Scheduler or Azure Automation.
  • Filter by Role Type – Create separate reports for Global Admins, Helpdesk Admins, etc.
  • Integrate with Monitoring – Automatically send alerts if new admins are added or roles change.

iv) Possible Errors & Solutions

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.

v) Conclusion

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.


Graph PowerShell Explorer Widget

20 Graph PowerShell cmdlets with easily accessible "working" examples.


Permission Required

Example:


                


                


                

© m365corner.com. All Rights Reserved. Design by HTML Codex