Published on

How to Find Active Directory Users and Their Managers (PowerShell Script)

Any workflow that routes through "who's this person's manager" — access approvals, offboarding notifications, an org chart synced from AD — is only as good as the Manager attribute actually being populated. It's optional, nobody's forced to set it, and gaps in it tend to go unnoticed until an automation depending on it breaks.

The quick answer

Get-ADUser -Filter * -Properties Manager | Select-Object Name, Manager

That gets you the raw distinguished name of the manager, which isn't exactly readable on its own.

A more useful reporting script

#requires -Modules ActiveDirectory
<#
.SYNOPSIS
    Reports each Active Directory user's assigned manager.
#>

[CmdletBinding()]
param(
    [string]$SearchBase,
    [string]$OutputCsv
)

Import-Module ActiveDirectory -ErrorAction Stop

$params = @{
    Filter     = { Enabled -eq $true }
    Properties = @('Manager', 'DistinguishedName')
}
if ($SearchBase) { $params['SearchBase'] = $SearchBase }

$users = Get-ADUser @params

$results = foreach ($user in $users) {
    $manager = if ($user.Manager) { Get-ADUser -Identity $user.Manager -ErrorAction SilentlyContinue } else { $null }

    [PSCustomObject]@{
        Name              = $user.Name
        SamAccountName    = $user.SamAccountName
        ManagerName       = if ($manager) { $manager.Name } else { $null }
        ManagerSam        = if ($manager) { $manager.SamAccountName } else { $null }
        DistinguishedName = $user.DistinguishedName
    }
}

if (-not $results) {
    Write-Host "No users found matching the given criteria." -ForegroundColor Yellow
    return
}

$sorted = $results | Sort-Object Name

Write-Host "Found $(@($sorted).Count) user(s)." -ForegroundColor Cyan
$sorted | Format-Table Name, SamAccountName, ManagerName, ManagerSam -AutoSize

$noManagerCount = @($sorted | Where-Object { -not $_.ManagerName }).Count
if ($noManagerCount -gt 0) {
    Write-Host "$noManagerCount user(s) have no manager set." -ForegroundColor Yellow
}

if ($OutputCsv) {
    $sorted | Export-Csv -Path $OutputCsv -NoTypeInformation
    Write-Host "Exported results to $OutputCsv" -ForegroundColor Green
}

The script resolves Manager to an actual name and SAM account name for you, and the summary line tells you straight away how many users have nothing set at all — the gap you actually need to find before something downstream trips over it.

Setting the manager attribute in bulk

If the report turns up gaps, filling them in from a CSV is the practical fix:

Import-Csv .\user-managers.csv | ForEach-Object {
    Set-ADUser -Identity $_.SamAccountName -Manager $_.ManagerSamAccountName
}

Why this matters more than it looks like it should

A real org chart usually exists somewhere — an HR system, a wiki page, a Slack directory — but if an approval workflow or offboarding process was built to read the Manager attribute in AD specifically, that external source of truth doesn't help it. The AD attribute has to actually be correct, independently, or the automation silently does the wrong thing (or nothing) for every user with a gap.

Where the manual approach runs out of road

A one-off script is fine for a single check. It starts to hurt once you actually need to run this regularly:

  • No scheduling. Cron/Task Scheduler can run the script, but now you own the scheduling, the credentials it runs as, and what happens when it silently fails.
  • No history. A CSV export is a snapshot. Was this the same five accounts as last month, or a growing list? A single export can't tell you.
  • No distribution. Getting the report to the right people (security, IT ops, compliance) on a schedule means building that plumbing yourself.
  • Multi-domain/multi-forest pain. Run it once per domain, reconcile the results yourself, and hope naming/OU conventions are consistent across all of them.
  • No alerting. If something changes unexpectedly between runs — an account re-enabled, a privileged group gaining a member — nothing tells you until you happen to run the script again.

None of that is a PowerShell problem. It's what turns a script into a product.

What SysFlint AD does instead

Our SysFlint AD runs the same kind of discovery shown above automatically, on a schedule, entirely inside your own network. No agents on domain controllers, no data leaving your environment. It's free, forever.

  • Scheduled reports by email Daily, weekly, or monthly runs delivered to the right inbox automatically. No Task Scheduler job for you to babysit.
  • Multi-domain and forest coverage Enumerate every domain in the forest in one pass and get one consolidated report instead of one per domain.

If you're currently doing this with a script on a scheduled task, here's the honest comparison between the two, or go straight to the download page.

Get this script, and the rest of them

The script above is part of awesome-it-scripts, SysFlint's free, open-source library of PowerShell scripts for Active Directory and other IT-admin tasks — no signup, no catch, MIT licensed. Grab this one directly from active-directory/Get-ADUserManagerReport.ps1, or browse the whole thing.

Find every disabled/locked-out/stale/privileged-account script we've published (plus the FAQ that goes with each one) in the repo's active-directory folder. Star it if it's useful — new scripts land there regularly.