Entra / Microsoft 365 · Users & guests
Report users and managers
A script to show how to create a report about the links between managers and employees stored in Azure AD.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-ExchangeOnlineConnect-MgGraph -Scopes Directory.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-ExchangeOnlineConnect-MgGraph -Scopes Directory.Read.All$HtmlHead="<html><style>BODY{font-family: Arial; font-size: 8pt;}H1{font-size: 22px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H2{font-size: 18px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H3{font-size: 16px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt;}TH{border: 1px solid #969595; background: #dddddd; padding: 5px; color: #000000;}TD{border: 1px solid #969595; padding: 5px; }td.pass{background: #B7EB83;}td.warn{background: #FFF275;}td.fail{background: #FF2626; color: #ffffff;}td.info{background: #85D4FF;}</style><body><div align=center><p><h1>Microsoft 365 Managers and Direct Reports</h1></p><p><h3>Generated: " + (Get-Date -format 'dd-MMM-yyyy hh:mm tt') + "</h3></p></div>"$Version = "1.0"$HtmlReportFile = "c:\temp\UsersAndManagers.html"$CSVReportFile = "c:\temp\UsersAndManagers.CSV"$Organization = Get-MgOrganization# Find user accountsWrite-Host "Finding user accounts..."[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All `-PageSize 999 -Property Id, displayName, userprincipalName, department, city, countryIf (!($Users)) {Write-Host "No licensed Entra ID accounts found - exiting"break}Write-Host ("Found {0} user accounts - checking management information..." -f $Users.count)# The accounts used for Room and shared mailboxes can hold licenses (for example, for Teams Rooms Devices or to get a larger quota for a shared mailbox)# so we remove these accounts here$OriginalUserAccounts = $Users.CountWrite-Host "Finding Entra ID accounts used for shared and room mailboxes..."[array]$NonUserAccounts = Get-ExoMailbox -RecipientTypeDetails SharedMailbox, RoomMailbox -ResultSize Unlimited | Select-Object UserPrincipalName, ExternalDirectoryObjectIdWrite-Host "Removing non-user accounts from set to be processed..."$Users = $Users | Where-Object {$_.Id -notin $NonUserAccounts.ExternalDirectoryObjectId}$RemovedAccounts = $OriginalUserAccounts - $Users.CountWrite-Host ("Proceeding to process {0} user accounts after removing {1} room and shared mailbox accounts" -f $Users.Count, $RemovedAccounts)$UsersWithNoManagers = [System.Collections.Generic.List[Object]]::new()$ManagerReports = [System.Collections.Generic.List[Object]]::new()ForEach ($User in $Users) {$UserManager = Get-MgUserManager -UserId $User.Id -ErrorAction SilentlyContinueIf ($Null -eq $UserManager) { # No manager$DataLine = [PSCustomObject] @{User = $User.DisplayNameUPN = $User.UserPrincipalNameUserId = $User.IdDepartment = $User.DepartmentOffice = $User.OfficeLocationCity = $User.CityCountry = $User.Country}$UsersWithNoManagers.Add($Dataline)Write-Host ("No manager found for {0}" -f $User.displayname)}$DirectReports = Get-MgUserDirectReport -UserId $User.IdIf ($Null -ne $DirectReports) { # Manager with direct reportsWrite-Host ("User {0} has {1} direct reports" -f $User.DisplayName, $DirectReports.count)ForEach ($Report in $DirectReports.AdditionalProperties) {$ReportUser = $Users | Where-Object {$_.UserPrincipalName -eq $Report.userPrincipalName}If (!($ReportUser)) {$ReportUserRecord = Get-MgUser -UserId $Report.userPrincipalName -ErrorAction SilentlyContinue$ReportDisplayName = $ReportUserRecord.DisplayName + " (unlicensed)"$ReportDepartment = $ReportUserRecord.Department$ReportCity = $ReportUserRecord.City$ReportCountry = $ReportUserRecord.Country} Else {$ReportDisplayName = $ReportUser.DisplayName$ReportDepartment = $ReportUser.Department$ReportCity = $ReportUser.City$ReportCountry = $ReportUser.Country}$DataLine = [PSCustomObject] @{ManagerId = $User.IdManager = $User.DisplayNameUser = $ReportDisplayNameDepartment = $ReportDepartmentCity = $ReportCityCountry = $ReportCountry }$ManagerReports.Add($DataLine)} # End ForEach Report} # End If Reports}# Add the information about managers and direct reports to the report[array]$Managers = $ManagerReports | Sort-Object ManagerId -Unique | Select-Object ManagerId, Manager | Sort-Object Manager$HtmlReport = $HtmlHeadForEach ($Manager in $Managers) {$DataToReport = $ManagerReports | Where-Object {$_.ManagerId -eq $Manager.ManagerId}$HtmlHeading = ("<p><h1>Direct Reports for <b>{0}</b></h1>" -f $Manager.Manager)$DataToReport = $DataToReport | Select-Object User, Department, City, Country$HtmlData = $DataToReport | ConvertTo-Html -Fragment$HtmlReport = $HtmlReport + $HtmlHeading + $HtmlData}# Add a section about users without managers$HtmlHeading = "<p><h1>User Accounts Without a Manager</h1>"$HtmlData = $UsersWithNoManagers | ConvertTo-Html -Fragment$HtmlReport = $HtmlReport + $HtmlHeading + $HtmlData# Create the HTML report$Htmltail = "<p>Report created for: " + ($Organization.DisplayName) + "</p><p>" +"<p>Number of users: " + $Users.count + "</p>" +"<p>Number of managers: " + $Managers.count + "</p>" +"<p>Number of users without a manager: " + $NoManagers.count + "</p>" +"<p>-----------------------------------------------------------------------------------------------------------------------------" +"<p>Microsoft 365 Managers and Direct Reports <b>" + $Version + "</b>"$HtmlReport = $HtmlHead + $HtmlReport + $HtmlTail$HtmlReport | Out-File $HtmlReportFile -Encoding UTF8Write-Host ""Write-Host "All done"Write-Host ""Write-Host ("{0} managers with direct reports found" -f $Managers.count)Write-Host ("{0} user accounts found with no managers" -f $NoManagers.count)$ManagerReports | Export-CSV -NoTypeInformation $CSVReportFileWrite-Host ("Output files are available in {0} and {1}" -f $HtmlReportFile, $CSVReportFile)
Attribution
Author
Office365itpros