Find All Disabled Admin Users in Microsoft 365 with Graph PowerShell

One of the most overlooked risks in Microsoft 365 environments is when disabled accounts still hold admin privileges. These accounts might have been offboarded improperly or simply forgotten, yet they retain access to sensitive administrative capabilities.

This article walks you through a Graph PowerShell script that helps you identify all disabled users assigned to admin roles—an essential step in maintaining a secure and compliant M365 tenant.


The Script: List Disabled Microsoft 365 Administrators

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "RoleManagement.Read.Directory", "Directory.Read.All", "User.Read.All"

# Get all active directory roles (e.g., Global Admin, Helpdesk Admin)
$activatedRoles = Get-MgDirectoryRole -All

# Hashtable to track disabled admin users
$disabledAdminUsers = @{}
                                
foreach ($role in $activatedRoles) {
    try {
        # Get members of the current role
        $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 full user object
        $user = Get-MgUser -UserId $userId -Property DisplayName, UserPrincipalName, AccountEnabled
                                        
        # Include only disabled accounts
        if (-not $user.AccountEnabled) {
            if ($disabledAdminUsers.ContainsKey($userId)) {
                # Append new role to existing user
                $disabledAdminUsers[$userId].'Admin Role Assigned' += ", $($role.DisplayName)"
            } else {
                # Add new entry
                $disabledAdminUsers[$userId] = [PSCustomObject]@{
                'Admin Name'          = $user.DisplayName
                'User Principal Name' = $user.UserPrincipalName
                'Sign In Status'      = "Disabled"
                'Admin Role Assigned' = $role.DisplayName
            }
        }
    }
    }
    }
    } catch {
    Write-Warning "Error while processing role '$($role.DisplayName)': $_"
    }
}

# Display final result
if ($disabledAdminUsers.Count -eq 0) {
    Write-Host "No disabled users with admin roles were found." -ForegroundColor Yellow
} else {
    $disabledAdminUsers.Values | Sort-Object 'Admin Name' | Format-Table -AutoSize
}
                            

How the Script Works

Here’s what happens under the hood:

  1. Connection:The script connects to Microsoft Graph using three delegated permissions:
    • RoleManagement.Read.Directory
    • Directory.Read.All
    • User.Read.All
  2. Activated Roles: It fetches all active directory roles like Global Administrator, Teams Admin, etc., using Get-MgDirectoryRole.
  3. Role Members: For each active role, it retrieves members via Get-MgDirectoryRoleMember. Members can include users, service principals, or groups.
  4. Filter by Disabled Users:
    • The script checks only for members where @odata.type equals #microsoft.graph.user.
    • It filters for users with AccountEnabled set to $false.
  5. Data Aggregation:
    • If a user is assigned multiple roles, they’re grouped under a single record.
    • Uses a hashtable to build a unique list of disabled admin users and their roles.
  6. Output: Results are sorted by name and printed in a clear table format.

Further Enhancements

You can easily build on this script:

Include Additional User Properties

  • Job Title
  • Department
  • User Creation Date
$user = Get-MgUser -UserId $userId -Property DisplayName, UserPrincipalName, JobTitle, Department, AccountEnabled

Export to CSV

For documentation or audit purposes:

$disabledAdminUsers.Values | Export-Csv -Path "DisabledAdminUsers.csv" -NoTypeInformation

Auto-remove Roles (with caution!)

With an approval workflow in place, use Remove-MgDirectoryRoleMemberByRef to clean up roles.


Possible Errors & Solutions

Error Message Cause Solution
No activated roles found No roles are currently active in your tenant Activate roles in the Azure Portal or via Graph Explorer
Get-MgDirectoryRoleMember : Access Denied Missing required permissions or insufficient rights Use Connect-MgGraph with appropriate delegated scopes
@odata.type not accessible The member object structure doesn't expose this directly Access it using .AdditionalProperties.'@odata.type'
Cannot bind argument to parameter...it is null The user object is null or not of the expected type Ensure only #microsoft.graph.user types are processed

Use Cases

Here are real-world scenarios where this script provides high value:

  • Security & Compliance Reviews: Identify and remove stale admin accounts to reduce attack surface.
  • Offboarding Checks: Ensure offboarded employees no longer hold privileged roles.
  • Governance Dashboards: Feed this into Power BI or reporting systems for continuous oversight.
  • Alert Triggers: Use in conjunction with automated workflows to alert security teams.

Conclusion

Leaving disabled users assigned to admin roles is a major security loophole in any Microsoft 365 tenant. This Graph PowerShell script helps plug that gap by giving you clear visibility into these accounts, their sign-in status, and their role assignments.

By using this script as part of your routine administrative checks, you ensure that no dormant but privileged accounts linger undetected in your environment.


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