Identify Unlicensed Admins in Microsoft 365 Using Graph PowerShell

Unlicensed administrators in Microsoft 365 pose a significant risk—they may retain powerful roles without an active account, which undermines identity-based access and compliance. Whether it’s a former employee who was never offboarded properly or a misconfigured account, finding these users is essential for a secure tenant.

This article walks you through a Graph PowerShell script that fetches only unlicensed users assigned to any admin role, helping you audit and fix these oversights swiftly.


The Script: List All Unlicensed Administrators in Microsoft 365

# Connect to Microsoft Graph with the required permissions
Connect-MgGraph -Scopes "RoleManagement.Read.Directory", "Directory.Read.All", "User.Read.All"
                                
# Retrieve all activated directory roles (e.g., Global Admin, Teams Admin, etc.)
$activatedRoles = Get-MgDirectoryRole -All
                                
# Hashtable to track unlicensed admin users
$unlicensedAdmins = @{}
                                
foreach ($role in $activatedRoles) {
try {
# Get all members assigned to this admin role
$members = Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id -All
                                
foreach ($member in $members) {
    # Check if the member is a user object
    if ($member.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.user") {
    $userId = $member.Id
                                    
    # Retrieve user details including licenses
    $user = Get-MgUser -UserId $userId -Property DisplayName, UserPrincipalName, AssignedLicenses
                                    
    # Filter unlicensed users only
    if ($user.AssignedLicenses.Count -eq 0) {
        if ($unlicensedAdmins.ContainsKey($userId)) {
        # Add additional admin role to the existing entry
        $unlicensedAdmins[$userId].'Admin Roles' += ", $($role.DisplayName)"
    } else {
        # Add a new unlicensed user entry
        $unlicensedAdmins[$userId] = [PSCustomObject]@{
            'Admin Name'          = $user.DisplayName
            'User Principal Name' = $user.UserPrincipalName
            'License Status'      = "Unlicensed"
            'Admin Roles'         = $role.DisplayName
         }
    }
    }
    }
    }
    } catch {
    Write-Warning "Error while processing role '$($role.DisplayName)': $_"
    }
}
                                
# Output the final result
if ($unlicensedAdmins.Count -eq 0) {
    Write-Host "No unlicensed administrators were found." -ForegroundColor Yellow
} else {
    $unlicensedAdmins.Values | Sort-Object 'Admin Name' | Format-Table -AutoSize
}
                            

How the Script Works

This script performs a comprehensive scan of your M365 environment:

  1. Graph Connection
  2. It uses Connect-MgGraph with three key scopes:

    • RoleManagement.Read.Directory
    • Directory.Read.All
    • User.Read.All
  3. Gets Active Directory Roles
  4. It pulls all activated roles using Get-MgDirectoryRole. These include high-privilege roles like Global Admin, Teams Admin, Helpdesk Admin, etc.

  5. Loops Through Role Members
  6. For each role, it fetches members with Get-MgDirectoryRoleMember. It filters only those of type #microsoft.graph.user.

  7. Filters for Unlicensed Users
  8. It checks if the user has an empty AssignedLicenses array. If yes, the user is flagged as unlicensed.

  9. Aggregates Multiple Roles
  10. If a user has more than one role, the roles are concatenated in the output.

  11. Displays the Result
  12. The script prints a clean table of unlicensed admin users and their role assignments.


Further Enhancements

You can tailor this script to extract more insight:

  • Include Sign-In or Status Information
  • -Property DisplayName, UserPrincipalName, AccountEnabled, AssignedLicenses

    Add this line in the output object:

  • 'Sign In Status' = if ($user.AccountEnabled) { "Enabled" } else { "Disabled" }
  • Export the Report to CSV
  • $unlicensedAdmins.Values | Export-Csv -Path "UnlicensedAdmins.csv" -NoTypeInformation

  • Filter by Specific Role (e.g., only Global Admins)
  • if ($role.DisplayName -eq "Global Administrator") { ... }


Possible Errors & Solutions

Error Message Cause Solution
No activated roles found No roles are assigned or activated Ensure roles are in use via Entra ID admin center
Access Denied or Insufficient privileges Missing delegated permissions Connect with the right scopes: RoleManagement.Read.Directory, etc.
AssignedLicenses is null or missing API response limitation or lack of user access Ensure user is valid and Graph API scopes are sufficient
@odata.type not found or empty Non-user object (e.g., group, service principal) Validate object type before querying user details

Use Cases

This script is highly useful for the following real-world scenarios:

  • Security Audit: Discover unlicensed accounts holding admin roles.
  • Governance Checks: Ensure no privilege is assigned to untraceable or inactive users.
  • Offboarding Validation: Confirm that offboarded users don't retain access.
  • License Optimization: Ensure high-privilege accounts are properly licensed for traceability.
  • Threat Reduction: Eliminate unlicensed but privileged users from your attack surface.

Conclusion

Leaving admin roles assigned to unlicensed users—especially those disabled or offboarded—is a serious oversight. This script helps you quickly identify and report all such users using Microsoft Graph PowerShell. It’s a proactive way to maintain a secure and well-governed Microsoft 365 tenant.

Use this script regularly as part of your admin audit workflow to ensure that only licensed, valid users hold administrative power 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