Reporting on Manager and/or DirectReports in Office 365

Getting a list of all the direct reports for a given user, or similarly building the manager’s chain seems to be a common question lately, so I figured I’d write a short article on this. There are few different ways for us to query this information, so let’s go over them.

First, it’s important to understand which attributes hold the relevant information are. In traditional AD, the manager and directReports (a “backlink”) attributes are used, with the former synchronized to Azure AD when directory synchronization is in use. However, the manager attribute isn’t actually exposed in the output of the good old Get-MsolUser cmdlet, or any of the cmdlets of the MSOnline module for that matter. This changed with the introduction of the Get-AzureADUserManager and Get-AzureADUserDirectReport cmdlets in the Azure AD module, yet being separate cmdlets, they make the process of retrieving this information across a group of users a bit more complicated than it should be.

Get-AzureADUserManager -ObjectId 584b2b38-888c-4b85-8871-c9766cb4791b

ObjectId DisplayName UserPrincipalName UserType
-------- ----------- ----------------- --------
e0d7442c-8cd8-4e65-8ede-ec9887816677 HuKu Member

Get-AzureADUserDirectReport -ObjectId 584b2b38-888c-4b85-8871-c9766cb4791b

ObjectId DisplayName UserPrincipalName UserType
-------- ----------- ----------------- --------
064abb3c-0812-44f9-bdcc-eea7e6ea398b Gosho Member

Thus, to this day my favorite method of retrieving managerial relationship information within Office 365 remain the Exchange Online cmdlets. To be more specific, the Get-User cmdlet exposes both the manager and directReports attributes, as follows:

Get-User vasil | select -ExpandProperty manager
Get-User vasil | select -ExpandProperty directreports

The bad news is that the output is a plain text “name”, meaning it’s hard to surface additional information, and it creates ambiguity in any moderately large organization. The good news is that the cmdlet supports a robust set of filters, making it possible to address some interesting scenarios. For example, if you want to list all users within the company that have at least one direct reports (or in other words, all “managers”), you can use something like this:

Get-User -Filter {DirectReports -ne $null}

Name RecipientType
---- -------------
vasil UserMailbox
HuKu UserMailbox

This time, the full object is returned, meaning you can easily expose additional information, for example the UserPrincipalName attribute, so you can avoid any ambiguity. Or you can group by department, giving you a quick way to count the number of managers per department in your organization:

Get-User -Filter {DirectReports -ne $null} | select DisplayName,UserPrincipalName,Department

DisplayName UserPrincipalName Department
----------- ----------------- ----------
Vasil Michev HQ
HuKu Sales

Get-User -Filter {DirectReports -ne $null} | group Department

Count Name Group
----- ---- -----
1 HQ {vasil}
1 Sales {HuKu}

Do note that if you want to use a filter against a specific user, you will need to provide the value of the DistinguishedName attribute. So if we want to find the manager for a specific user, we can use this:

$dn = Get-User vasil | select -ExpandProperty DistinguishedName
Get-User -Filter "DirectReports -eq '$dn'"

Name RecipientType
---- -------------
HuKu UserMailbox

which of course we can also do by looking at the manager property, as in our first example. The same set of filters works against the Manager attribute as well. For example, the below will return all the direct reports for HuKu:

$dn = Get-User huku | select -ExpandProperty DistinguishedName
Get-User -Filter "Manager -eq '$dn'"

Name RecipientType
---- -------------
vasil UserMailbox
WC UserMailbox

which of course we can also do by querying the directReports property:

Get-User huku | select -ExpandProperty directReports

Without boring you with too many repetitive examples, the rule of thumb is as follows: you can use the Get-User cmdlet to quickly list the manager or direct reports of a given user by checking the values of the appropriate attributes, or create a basic report across all users within the company. If you want to generate a proper report however, you should instead use the filter-based syntax, as it will return the full object, allowing you to expose additional properties and/or remove ambiguity.

Lastly, one can also use the Graph API to obtain this information, which should come as no surprise, given that the Azure AD module is a simple wrapper for Graph. You can query the /manager or /directReports endpoints accordingly, for example:

The problem with this approach is the limited filtering capabilities in the Graph, and the annoyingly convoluted syntax you need to use at times. For example, you can use the following query to list all users along with their managers:$expand=manager&$select=id,displayName

The manager property however will not even be returned for users without a manager set, which is the same behavior as when you didn’t include it in the query. And for the users where it is returned, the full user object will be returned. To request just specific properties, you need to adjust the expand operation as follows:$expand=manager($levels=max;$select=id,displayName)&$select=id,displayName&$count=true

where the $count parameter is mandatory and so is the $levels one it seems. Refer to the official documentation for more information on this.

For the directReport query the situation is even more annoying, as the property is always returned (in contrast to the manager one), and there doesnt seem to be a way to request just specific properties (the full user object is always returned). But hey, that’s Graph for you. You’re better off using client-side filtering anyway.

This entry was posted in Azure AD, Exchange Online, Graph API, Microsoft 365, Office 365, PowerShell. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

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