Finding Managers of Users with the Microsoft Graph PowerShell SDK

Find Manager for Entra ID Accounts is Easy at the Individual Level

Following Friday’s discussion about needing to update the script to create the Managers and Direct Reports report, I was asked what’s the best way to find managers assigned to Entra ID user accounts (Figure 1).

The manager listed in the properties of an Entra ID account.

Find manager for Entra ID account.
Figure 1: Find manager for Entra ID account in the Entra admin center

It is simple to find and report the manager for an individual user account with PowerShell. For instance, to find Sean Landy’s manager, run the Get-MgUserManager cmdlet. The return value is the object identifier for the manager’s account, so to find details of the manager, we must fetch it from the data stored in the additionalProperties property.

Get-MgUserManager -UserId Sean.Landy@office365itpros.com | Select-Object -ExpandProperty additionalproperties

Key               Value
---               -----
@odata.context    https://graph.microsoft.com/v1.0/$metadata#directoryObjects/$entity
@odata.type       #microsoft.graph.user
businessPhones    {+353 1 8816644}
displayName       James Ryan
givenName         James
jobTitle          Chief Story Teller
mail              James.Ryan@office365itpros.com

The Manager property is in the set available to Get-MgUser, but it must be fetched to be available for processing. The property is a reference to another account, so it must be resolved by using the ExpandProperty parameter. Again, the manager’s display name is retrieved from the additionalProperties property.

$UserData = Get-MgUser -UserId Sean.Landy@office365itpros.com -Property displayname, manager -ExpandProperty Manager

$UserData | Format-Table @{n='Employee'; e={$_.displayname}}, @{n='Manager'; e={$data.manager.additionalproperties['displayName']}}

Employee   Manager
--------   -------
Sean Landy James Ryan

Find the Managers for Multiple Users

Challenges emerge when dealing with multiple user accounts. For example, it’s common to retrieve the set of licensed user accounts in a tenant with a complex query that checks for the presence of at least one license. However, adding the ExpandProperty parameter to this command stops it working:

[array]$users = Get-MgUser -Filter "userType eq 'Member' and assignedLicenses/`$count ne 0" -ConsistencyLevel eventual -CountVariable UsersFound -All -PageSize 999 -Property Id, userPrincipalName, displayName, Manager, Department, JobTitle, EmployeeId -ExpandProperty Manager

The error is not terribly helpful:

Expect simple name=value query, but observe property 'assignedLicenses' of complex type 'AssignedLicense'.

Removing the ExpandProperty parameter from the command makes it work, but the Manager property is not populated.

Any filter to find user accounts that needs to populate the Manager property is restricted to a simple query. Here’s an example of a query to find all member accounts and populate the Manager property. A client-side filter then reduces the set to accounts with an assigned manager:

[array]$EmployeesWithManager = Get-MgUser -All -PageSize 999 -Property Id, DisplayName, JobTitle, Department, City, Country, Manager -ExpandProperty Manager -Filter "UserType eq 'Member'"| Where-Object {$_.Manager.id -ne $null}

$EmployeesWithManager | Format-Table id, displayname, @{Name='Manager';expression={$_.Manager.additionalProperties.displayName}} -Wrap

Id                                   DisplayName                             Manager
--                                   -----------                             -------
a3eeaea5-409f-4b89-b039-1bb68276e97d Ben Owens                               James Ryan
d446f6d7-5728-44f8-9eac-71adb354fc89 James Abrahams                          Kim Akers 
cad05ccf-a359-4ac7-89e0-1e33bf37579e James Ryan                              René Artois

The results generated by this code are acceptable because a user account with an assigned manager is probably one used by a human. The account probably has licenses too. Obviously, any account that hasn’t got an assigned manager will be left out of the report.

Looking for User Accounts without Managers

Things get a little more difficult if we reverse the client-side filter and look for member accounts that don’t have an assigned manager:

[array]$EmployeesWithoutManager = Get-MgUser -All -PageSize 999 -Property Id, DisplayName, JobTitle, Department, City, Country, Manager, UserPrincipalName -ExpandProperty Manager -Filter "UserType eq 'Member'"| Where-Object {$_.Manager.id -eq $null}

In addition to user accounts lacking managers, the set of resulting accounts will include utility accounts created by Exchange Online, including:

  • Room and equipment accounts.
  • Shared mailbox accounts.
  • Accounts used for Microsoft Bookings.
  • Accounts synchronized from other tenants in a multi-tenant organization (MTO).
  • Accounts created for submission of messages to the Exchange Online High Volume Email (HVE) solution.
  • Accounts created for Teams meeting rooms.
  • Service accounts created by the tenant for background processing and other reasons.

In a medium to large tenant, there might be thousands of these kinds of accounts cluttering up the view. To remove the utility accounts, create an array containing the object identifiers of the owning accounts:

[array]$CheckList = Get-ExoMailbox -RecipientTypeDetails RoomMailbox, EquipmentMailbox, SharedMailbox, SchedulingMailbox -ResultSize Unlimited | Select-Object -ExpandProperty ExternalDirectoryObjectId

If the tenant uses HVE, add the account identifiers for the HVE accounts to the array.

Get-MailUser -LOBAppAccount | ForEach { $Checklist += $_.ExternalDirectoryObjectId }

Now filter the account list to find those that don’t appear in the list of utility mailboxes:

$EmployeesWithoutManager = $EmployeesWithoutManager | Where-Object {($_.Id -notin $Checklist)}

If the tenant is part of a multi-tenant organization, this filter removes the accounts synchronized from the other tenants:

$EmployeesWithOutManager = $EmployeesWithoutManager | Where-Object {$_.UserPrincipalName -notlike "*#EXT#@*"}

Eventually, you’ll end up with hopefully a very small list of employees without assigned managers and can take the necessary action to rectify the situation.

Entra ID Should Mark Utility Accounts

The problem of dealing with utility accounts that end up in Entra ID with the same status as “human” user accounts is growing. Applications create new member accounts without thinking about the consequences. No problem is apparent because no licenses are consumed, but the steps needed to cleanse the set of accounts returned by Entra ID with cmdlets like Get-MgUser are another trap waiting for the unwary administrator. Microsoft really should do better in this area, like creating a new “utility” value for the UserType property. Would that be so bad?


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

9 Replies to “Finding Managers of Users with the Microsoft Graph PowerShell SDK”

  1. Not only utility accounts, but also rooms and equipment. All of them have associated user resource

  2. I agree, Entra is becoming a mess and it’s difficult to report for example who is registered for MFA, when the built-in report gives me Shared Mailboxes and other things.

    For managers, we utilize EmployeeType property in AD (employee, contractor, service, etc).
    I populate a group of just managers of employees & contractors.

    $users = Get-MgUser -Filter “employeeId ne null” -all -ConsistencyLevel eventual -CountVariable CountVar
    ForEach ($user in $users){
    if (Get-MgUserDirectReport -UserId $user.id -All) {

    $manager = $user
    #get all direct reports
    $allDirects = Get-MgUserDirectReport -UserId $manager.id -All

    # add manager to manager array if the employeetype of the direct report is valid.
    foreach ($direct in $allDirects) {
    $employeeType = (Get-MgUser -UserId $direct.id -Property extension_72865f8793294a44b04f437101a06033_employeeType).additionalproperties.extension_72865f8793294a44b04f437101a06033_employeeType
    if (($employeeType -eq “Employee”) -or ($employeeType -eq “Vendor”) -or ($employeeType -eq “Temp”) -or ($employeeType -eq “Contractor”)) {
    $managers += $manager

    # we only need to find one valid direct report, then we can move to the next manager.
    break
    }
    }
    }
    }

    I then compare the array to the existing group members and add or subtract if needed.
    It takes about 10 min to run for 3000 users.

      1. It doesn’t populate from AD Connect so we have to use the extension.

      2. I actually checked again and it appears to populate now after 2 years of using this script
        Get-MgUser -UserId -Property employeetype |select employeetype

        EmployeeType
        ————
        Employee

      3. I forgot to mention. The main reason why we use the employeeType extension is because the Dynamic Rules in groups doesn’t include user.employeeType. Kind of a head scratcher why they haven’t included that.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.