Skip to content

Instantly share code, notes, and snippets.

@joerodgers
Last active March 14, 2025 21:00
Show Gist options
  • Save joerodgers/f9fc778e3c029a04c082ea4095165c5b to your computer and use it in GitHub Desktop.
Save joerodgers/f9fc778e3c029a04c082ea4095165c5b to your computer and use it in GitHub Desktop.
Generates a .csv file which can be uploaded to Viva Insights for custom reporting based on manager reporting chain.
#requires -modules "Microsoft.Graph.Authentication", "Microsoft.Graph.Users"
function Get-ManagerHierarchy
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]
$Identity
)
begin
{
$level = $counter = 1
$managerHierarchyUri = '/v1.0/users/{0}?$expand=manager($levels=max;$select=userPrincipalName)&$select=id,displayName,department,jobTitle,userPrincipalName' -f $Identity
}
process
{
$response = Invoke-MgGraphRequest -Method GET -Uri $managerHierarchyUri -Headers @{ "ConsistencyLevel" = "eventual" }
if( $response )
{
$output = [PSCustomObject] @{
UserPrincipalName = $response.userPrincipalName
Department = $response.department
JobTitle = $response.jobTitle
ManagerId = $response.manager.userPrincipalName
}
$manager = $response.manager
$managerHierarchy = while ( $manager )
{
[PSCustomObject] @{
UserPrincipalName = $manager.userPrincipalName
Level = $level++
}
$manager = $manager.manager
}
foreach( $manager in $managerHierarchy | Sort-Object -Property "Level" -Descending )
{
$output | Add-Member -MemberType "NoteProperty" -Name "Level$counter" -Value $manager.UserPrincipalName
$counter++
}
# useful for csv output since you could have a varying number of columns based on reporting depth
$output | Add-Member -MemberType "NoteProperty" -Name "ManagerHierarchyDepth" -Value $managerHierarchy.Count
return $output
}
else
{
Write-Error "Unable to find user with identity: '$($Identity)'"
}
}
end
{
}
}
# requires Microsoft Graph > Application > User.Read.All
Connect-MgGraph -ClientId $env:CDX_CLIENTID `
-CertificateThumbprint $env:CDX_THUMBPRINT `
-TenantId $env:CDX_TENANTID `
-ErrorAction Stop | Out-Null
$skuId = "06ebc4ee-1bb5-47dd-8120-11324bc54e06" # Microsoft 365 E5
$skuid = "18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e" # Microsoft Copilot
# get all users assigned a specific license
$users = Get-MgUser -Filter "assignedLicenses/any(i:i/SkuId eq $skuId)" `
-ConsistencyLevel Eventual `
-Property userPrincipalName, manager `
-ExpandProperty manager `
-All `
-ErrorAction Stop
# remove all users without a manager assigned
$users = $users | Where-Object -FilterScript { $null -ne $_.manager.Id }
# get the reporting change for all users
$results = foreach( $user in $users )
{
Get-ManagerHierarchy -Identity $user.userPrincipalName
}
# determine the maxium reporting chain depth, needed so we ensure the csv contains the max reporting depth
$maxdepth = $results | Measure-Object -Property "ManagerHierarchyDepth" -Maximum | Select-Object -ExpandProperty Maximum
# generate static headers for the csv export
$headers = "UserPrincipalName", "Department", "JobTitle", "ManagerId"
# append "LevelX" header for the maxium depth in our report
1..$maxdepth | ForEach-Object -Process { $headers += "Level$($_)" }
# timestamp the file
$timestamp = Get-Date -Format FileDateTime
# export results to csv
$results | Select-Object -Property $headers | Export-Csv -Path "UserHierarchy_$timestamp.csv" -NoTypeInformation
<#
EXAMPLE OUTPUT
UserPrincipalName Department JobTitle ManagerId Level1 Level2 Level3
----------------- ---------- -------- --------- ------ ------ ------
[email protected] IT IT Leader [email protected] [email protected] [email protected] [email protected]
[email protected] Legal [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected]
[email protected] HR Director [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] CVP HR [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected]
[email protected] HR Leader [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
[email protected] [email protected] [email protected] [email protected] [email protected]
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment