Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1 Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1

Using PowerShell in Parallel with the Active Directory Cmdlets

Let’s continue our exploration of parallel processing with PowerShell by taking a look at the Active Directory cmdlets. I came across the scenario in an online forum, so let’s start with that and how it led to my experiments in parallelization.

The question was about the best way to search for certain computers, but exclude a location. This seems like a reasonable use case. For my test, I have computers in a development organizational unit,f and I don’t want them to show up in the results. You should also know that I am testing from a Windows 8.1 desktop running PowerShell 4.0.

Because the distinguished name contains the OU, you might think that the following expression would work:

get-adcomputer -filter "name -like 'chi-*' -AND distinguishedname -notlike '*OU=Development,DC=globomantics,DC=local'"

But I get no results. Not even an error message. When I run into a situation like this, I try to simplify and test each component. I already know that the first part of the filter that grabs computers where the name starts with CHI works. So I try a number of variations on the second part, even trying to match case.

get-adcomputer -filter "distinguishedname -like '*OU=Development,DC=GLOBOMANTICS,DC=local*'"

No change, no results, and no errors. For whatever reason, this approach will not work. Next, I try to filter on the canonical name, e.g. GLOBOMANTICS.local/Development/CHI-HV02.

get-adcomputer -filter "canonicalName -like '*Development*'"

I like to test for positive results to make sure it works before testing with –Notlike. But this also fails.

This means I don’t think there is a way to do early filtering with Get-ADComputer for a location. Remember, I want to exclude an OU. Thus, it seems like I have no choice but to use Where-Object to exclude a location.

get-adcomputer -filter "name -like 'chi-*'" | where {$_.distinguishedname -notmatch 'OU=Development,DC=globomantics,DC=local'}

This works as expected. Because I’m using PowerShell 4.0, I could also use the new Where() method, which is a little faster.

(get-adcomputer -filter "name -like 'chi-*'").where({$_.distinguishedname -notmatch 'OU=Development,DC=globomantics,DC=local'})

An advantage to using late filtering with Where-Object is that I can exclude multiple locations.

(get-adcomputer -filter "name -like 'chi-*'").where({$_.distinguishedname -notmatch '(OU=Development|CN=Computers),DC=globomantics,DC=local'}) | 
Sort DistinguishedName | Select DistinguishedName
Using Where-Object to exclude multiple locations. (Image Credit: Jeff Hicks)
Using Where-Object to exclude multiple locations. (Image Credit: Jeff Hicks)

As much as I would prefer to have early filtering, sometimes you have no choice. But this is what led me to the next step. What if I could search multiple organizational units?

The Get-ADComputer cmdlet, like others in the ActiveDirectory module, has a SearchBase parameter.

The SearchBase parameter. (Image Credit: Jeff Hicks)
The SearchBase parameter. (Image Credit: Jeff Hicks)

But the SearchBase parameter doesn’t take an array, accept wildcards, or pipeline input. It’s more than likely that you have computer accounts organized into several organizational units. For my demonstration, I want to limit my search to these OUs.

$ous = "ou=Servers,DC=globomantics,dc=local","ou=Company Desktops,DC=globomantics,dc=local","ou=engineering,DC=globomantics,dc=local"

In a perfect world, I could do this:

get-adcomputer -filter "name -like 'chi-*'" -SearchBase $ous

But I can’t.

The first alternative that comes to mind is to use ForEach.

$ous | foreach {get-adcomputer -filter "name -like 'chi-*'" -SearchBase $_}

This has effect of running the command once for each location. If the number of locations is small, this might not be too bad. But I’m still searching sequentially. In my mind, if Get-ADComputer could search all locations simultaneously, that should be better. I can try some of the techniques I explored in the previous article.

Let’s see if a workflow helps.

Workflow GetADData {
Param ([hashtable]$Params,[string[]]$Search)
Write-Verbose "Searching for computers"
Write-Verbose ($params | out-string)

if ($Search) {
   Write-Verbose "Searching location:"
   foreach -parallel ($location in $Search) {
     Write-Verbose "..$location"
     Get-ADComputer @params -searchbase $location
else {
   Get-ADComputer @params
} #end workflow

The workflow takes a hashtable of parameters for Get-ADComputer so that it can be splatted. The workflow takes a second parameter for an array of search location. I probably could have added logic to test the $Params hashtable for –SearchBase, but I wanted to keep the code as simple as possible. The only parameter I need to pass in my example is for –Filter.

$paramHash = @{filter = "name -like 'chi-*'"}

I’ll run the workflow on the domain controller in hopes that it runs faster.

GetADData -Params $paramHash -Search $ous -PSComputerName chi-dc04 –Verbose

I will report that when testing, the workflow was slower than using ForEach. Perhaps I need a larger number of locations to search to justify the workflow overhead. I’ll get all OUs in my domain except Development.

$all = (Get-ADOrganizationalUnit -filter * | where {$_.distinguishedname -notmatch 'OU=Development,DC=Globomantics,DC=local'}).DistinguishedName

In my domain, this is 40 locations. Re-running the workflow with these locations works:

GetADData -Params $paramHash -Search $all -PSComputerName chi-dc04 -Verbose

And took 12 seconds. However, this approach took about 300ms.

$all | foreach {get-adcomputer -filter "name -like 'chi-*'" -SearchBase $_}

And using the ForEach enumerator was about the same.

foreach ($loc in $all) {
  get-adcomputer -filter "name -like 'chi-*'" -SearchBase $loc

From my testing, it would appear that the cmdlet alone searches a single location pretty quickly. I just need to construct a PowerShell expression to do process multiple locations. Because I want easy, I’m going to need a new tool. I’ll show you that next time.

Related Topics:


Don't have a login but want to join the conversation? Sign up for a Petri Account

Comments (0)

Leave a Reply

Register for the Hybrid Identity Protection (HIP) Europe Conference!

Hybrid Identity Protection (HIP) Europe 2021 - Virtual Conference

Mobile workforces, cloud applications, and digitalization are changing every aspect of the modern enterprise. And with radical transformation come new business risks. Hybrid Identity Protection (HIP) is the premier educational forum for identity-centric practitioners. At the inaugural HIP Europe, join your local IAM experts and Microsoft MVPs to learn all the latest from the Hybrid Identity world.