So I had a somewhat odd request (isn’t that how all truly great stories start) a long time ago in an IT galaxy far, far away. And it went something like this: we want to email an employee and CC their manager, but if their manager is inactive (took a one-way trip to the gallows) we want to CC their manager’s manager. Andddd if the manager’s manager is inactive…and that’s how Inception 2 was started. Like usual, we started looking at it manually through some ooey GUIs but quickly dove into PowerShell to be able to process users in bulk, on-demand. And as you can imagine, if you have to go 3 or 4 managers deep, this thing could get out of hand quicker than El Clasico. In this post, we’ll go over how one might initially start writing this and then we’ll show some ways to improve efficiency. We’ll end up with a function and use it in a script for returning the first active AD manager and then we’ll use it in a script for returning all of the AD managers in a hierarchy.

\\\How Not To Do it

I don’t want to spend too much time on this method, but to begin with let me show you how I would probably start it out and then see how it turns into a flaming dumpster fire. First, let’s kickoff with Get-ADUser and get the manager property:

$SamAccountName = 'lskywalker'
$User = Get-ADUser $SamAccountName -Properties Manager
Cool, but the manager property is the DistinguishedName and doesn’t tell us much about the manager. So we need to use it in another Get-ADUser cmdlet to get the rest of the info we need. And in case this user is disabled, we need to get their manager as well:
$Manager1 = Get-ADUser $User.Manager -Properties manager
Now I would put this in an If/Else statement….and then keep going down the rabbit hole until I accepted my demise. To demonstrate, let’s just look at what happens if the first 2 managers are disabled:
if($Manager1.enabled -eq $false){
    Write-Host "First manager is is disabled. Checking second manager..." -ForegroundColor Cyan
    $Manager2 = Get-ADUser $Manager1.Manager -Properties manager, emailaddress
    if($Manager2.enabled -eq $false){
        Write-Host "Second manager is is disabled." -ForegroundColor Cyan
    else{
        Write-Host "Second manager is enabled:" -ForegroundColor Cyan
        $Manager2 | select SamAccountName, EmailAddress, Enabled
    }
}
else{
    Write-Host "First manager is enabled:" -ForegroundColor Cyan
    $Manager | select SamAccountName, EmailAddress, Enabled
}

I wrote a little Write-Host output so you can see where you’re at if you’re lost in the if’s and the else’s. What if you’re managing a large enterprise and have 10k+ employees and potentially 7, 10, or 31515 levels of reporting? This is going to be painful and you have to keep adding if/else statements for each new layer of your corporate onion you peel. And if you would have never started this script like this because you “big brain and you too smart for script,” then this part wasn’t for you.

\\\A More Excellent Way

To get your hamster wheel turning when thinking about how to approach your code to do what you want it to do, I like to ask myself this question: “who is your daddy, and what does he do?” I then quickly follow up with a second question, “what parts of my code am I repeating multiple times in my script?” If the answer is none, I go back and ponder the first question. But if the answer is a lot, that’s a good indicator that you could make your code more efficient or at least type less. In our quest to get those managers, we’re constantly doing Get-ADUser with the manager, then checking the manager to see if they’re enabled. Let’s make it fun-ction!

function Get-Manager ($SamAccountName){
    $ADUser = Get-ADUser $SamAccountName -Properties Manager
    $Manager = Get-ADUser $ADUser.Manager -Properties Manager, EmailAddress, Title
    $Manager | select SamAccountName, EmailAddress, Title, Enabled, Manager
}
So now when we use Get-Manager with a $SamAccountName, it will get that user’s manager, then spit out the manager with a few of the properties we want. Feel free to adjust the properties to your liking.

Right, we have our function now, but that means slightly less typing but still going down the infinite doom loop of manager’s manager’s manager’s………..manager. [Do...Until loop has entered the chat]

\\\Do the Dew…Until

We’re going to start with a couple of variables we’ll use in our loop: $SamAccountName and $Level. I’m using level so we can see just how deep this trail goes. Then in our Do block, we’ll use our Get-Manager function then add a “Level” property to it. Let’s look at how this looks before we add anything else:

$SamAccountName = 'lskywalker'
$Level = 1
Do{
    $ManagerCheck = Get-Manager $SamAccountName
    $ManagerCheck | Add-Member -MemberType NoteProperty -Name Level -Value $Level -Force
}
Now, we need to see if the manager stored in $ManagerCheck is enabled or not. If it’s not, we want to keep going by running our Do block again, but this time making the $ManagerCheck the $SamAccountName. Otherwise, we would just keep querying for the same user and getting the same manager…forever. As much fun as that sounds, let’s not do that. If $ManagerCheck is enabled, we’ll set $Stop to $true and then exit our Do...Until loop. Finally, we’ll remove our $Stop variable and then see what we have stored in $ManagerCheck:
$SamAccountName = 'lskywalker'
$Level = 1
Do{
    $ManagerCheck = Get-Manager $SamAccountName
    $ManagerCheck | Add-Member -MemberType NoteProperty -Name Level -Value $Level -Force
    if(!$ManagerCheck.Enabled){
        #KeepGoing
        $SamAccountName = $ManagerCheck.SamAccountName
        $Level++
    }
    else{
        #Stop
        $Stop = $true
    }
}
Until($Stop)
rv Stop
$ManagerCheck

\\\The Final Countdown

Neato gang! But what if you couldn’t care less if the manager was enabled or disabled, but instead you just want to see the entire hierarchy and see just how the road leads back to Rome? Very similar to what we’ve already written above, but with a couple of additional lines.

First, let’s build an array to store all of our managers in $Hierarchy. Let’s populate it with our first user with a level of 0 before we add managers to it. If you don’t care about the pawn at the bottom, just define $Hierarchy as a blank array and skip this part. Otherwise, it looks like this:

$SamAccountName = 'lskywalker'
$Hierarchy = @()
$ADUser = Get-ADUser $SamAccountName -Properties SamAccountName, EmailAddress, Title | select SamAccountName, EmailAddress, Title, Enabled
$ADUser | Add-Member -MemberType NoteProperty -Name Level -Value 0 -Force
$Hierarchy += $ADUser

So far so good. Our Do loop is the same but I found some anomalies out there in the real world that threw me for a loop (pun intended) that I accounted for. I had a manager who reported to…themselves. Self-employed? Not sure, but if you run across that, it will just keep you stuck in the fruit loop. What we’re now saying is 1) if you are your own manager, stop. 2) if the last manager has no manager, stop.

Putting it all together, we can wrap everything into a function because the only people who love one-liners more than our significant other is us. Here’s everything as a function called Get-ManagerHierarchy (you can also get the code at Github).

function Get-ManagerHierarchy($SamAccountName){
    $Level = 1
    $Hierarchy = @()
    $ADUser = Get-ADUser $SamAccountName -Properties SamAccountName, EmailAddress, Title | select SamAccountName, EmailAddress, Title, Enabled
    $ADUser | Add-Member -MemberType NoteProperty -Name Level -Value 0 -Force
    $Hierarchy += $ADUser
    Do{
        $ManagerCheck = Get-Manager $SamAccountName
        $ManagerCheck | Add-Member -MemberType NoteProperty -Name Level -Value $Level -Force
        if($ManagerCheck.Manager){
            #Make sure the manager doesn't report to themselves - weirdos
            if($ManagerCheck.SamAccountName -eq $SamAccountName){
                $Stop = $true
            }
            else{
                $Hierarchy += $ManagerCheck | select SamAccountName, EmailAddress, Title, Enabled, Level
                #Keep Going with the next Manager
                $SamAccountName = $ManagerCheck.SamAccountName
                $Level++
            }
        }
        else{
            #Stop
            Write-Host "This user has no manager: $ManagerCheck"
            $Stop = $true
        }
    }
    Until($Stop)
    rv Stop
    $Hierarchy
}

Now we can just call the one-liner to do our dirty work!

Get-ManagerHierarchy -SamAccountName 'lskywalker'

And that’s how the cookie crumbles! Hope you enjoyed this and whether you need this specific info or not, I hope you can use the ideas and approach here in your own coding adventures! Rock on!

OTHER POSTS YOU WANT TO READ

6 Things I Wish I Had Known About PowerShell

About a hundred years ago when I stopped riding dinosaurs to work and started learning PowerShell, I struggled to know what to learn first and where to even begin. That blue box with white writing was intimidating to say the least. I finally worked up the courage to...

Index Scripts for Windows Search

So you just finished writing some code and you go to save your file. You summarize all of the important aspects this section of code contains into a nice, easy-to-read file name that your future self will immediately recognize. Fast forward to the future where you...

Array vs ArrayList (PowerShell)

For some tasks in life, being precise is a necessity. But most of us get away with rounding, paraphrasing, and hitting in the general vicinity most of the time. Depending on your personality, you may be one who strives for perfection and strains on every miniscule...

Spice Up HTML Emails with PowerShell – Part III

So far in this series we've fumbled our way around the kitchen and tried to wing it when sending HTML emails with PowerShell. It was clunky to say the least. We then went through our spice rack and built an HTML template, highlighting the nuances of each spicy element...

Spice up HTML Emails with PowerShell – Part II

In Part I of our scrumptious concoction we put our script into the oven to let it bake. But we forgot to add our secret sauce that's sure to leave our recipients drooling, which is clearly our goal. In this post we'll continue to spice up HTML emails with PowerShell...