PowerShell Problem Solver: Finding Needles in the Active Directory Haystack

azure active directory logo
Recently, a follower sent me an email with a problem that he was trying to solve with Active Directory (AD) and PowerShell. At first, I was a little puzzled by what he was trying to do. We spent some time going back and forth on possible solutions. During the process, I realized his situation presented a great teachable opportunity. Let’s see what we can learn about PowerShell.
 

 
The initial problem was to find all AD computer accounts, where part of the name was found in a list of names. The initial code, which was not working, looked something like this:

$Filter = (Get-Content names.txt)
 Get-AdComputer  -Filter * | where-object{"*"+$_.name+"*" -match $Filter} | select name

It did not have errors but it also did not have results. Let me show you what is in the names.txt file.

Getting the list of names (Image Credit: Jeff Hicks)
Getting the List of Names (Image Credit: Jeff Hicks)

We needed to find computer accounts that had usernames as part of the name. I am using a small list but it could easily be hundreds of names. My friend knew there were accounts that should be found because he could find them on an individual basis.
Finding a matching computer account (Image Credit: Jeff Hicks)
 Finding a Matching Computer Account (Image Credit: Jeff Hicks)

Naturally, he was looking for a way to scale out this process. One approach we considered, was using the -Contains operator. He was really checking to see if the computer name belonged to a list.

Get-AdComputer  -Filter * | where-object{ $filter -contains $_.name} | select name

This does not quite work. If by chance $Filter contained the complete name, then this approach would work.

Testing with full names (Image Credit: Jeff Hicks)
Testing with Full Names (Image Credit: Jeff Hicks)

This was not the case. The only thing he knew was that the username might be a part of the computer name. We needed to go back to the drawing board.

Conceptually, I was beginning to realize what he needed. Talking through the process was helpful. You may also find it helpful to simply write it as a narrative.

“Get the computer names from AD and then look at each name. If the computer name property matches a value from the list of names, then get the computer name and distinguished name properties.”

At this point, it also became clear that trying to write this as a one-line solution was not practical. It could probably be done but it would be overly complicated. This is a trap many PowerShell beginners fall into, thinking they need to write one-line solutions. This not true. Sometimes, it makes much more sense to break the process down into several steps.
Based on my narrative, I knew the first step was to get all the computer accounts from AD.

$ad = Get-ADComputer -filter *

This turned out to be 96 items in my test domain. Next, get the list of usernames from the text file.

$names = Get-content .\names.txt

The main comparison has to be done on each computer account, one at a time. To make it easy to follow, I used the ForEach enumerator.

foreach ($name in $names) {
    $ad | Where {$_.name -match $name} | Select name,distinguishedname
 }

The code loops through each name. It then pipes $ad to Where-Object to see if the name matches.

Filtering computer names (Image Credit: Jeff Hicks)
Filtering Computer Names (Image Credit: Jeff Hicks)

You can see that it works. You may be wondering why I did not put the Select-Object expression after ForEach. ForEach does not write anything to the pipeline but I can do this on the inside.
There is also one other minor nit. If you see the output, you will notice there is a valid server name in the list, time-server2. It gets caught on the name tim. This is because of the regular expression, -match operator matched.
The solution is to use a more restrictive pattern. In fact, I should also try to limit the number of computer accounts to test as well. I know that any computer account in my domain that starts with CHI- is okay. I may try filtering Get-ADComputer like this:
Invalid filtering operator (Image Credit: Jeff Hicks)
Invalid Filtering Operator (Image Credit: Jeff Hicks)

This almost works. You cannot use -Match as a filtering operator with Get-ADComputer. I will do the next best thing and use -like.

$ad = Get-ADComputer -filter {Name -notlike "CHI-*"}

Now, I get 44 computer names. Here is my revised ForEach loop with a better regular expression pattern and using the Where() method for better performance.

foreach ($name in $names) {
    ($ad).Where({$_.name -match "$name$"}) | Select name,distinguishedname
}


There are certainly other ways to solve this problem but I hope you will pay attention to the process and principals I used.