How to group Active Directory objects by Organizational Unit (OU) using PowerShell.
Grouping objects by Organizational Unit might be helpful for preparing reports or stats for our directory. Let's learn how we can do it using PowerShell!
First, let's grab all the objects we'd like to group. Some example cmdlets we can use:
# Gets all the computers
$objects = Get-AdComputer -Filter *
# Gets all the users
$objects = Get-AdUser -Filter *
Now we need to find the value of the organizational unit for each object in the array.
We can first check if there's any property we could use out-of-the-box.
$objects[0].OU
$objects[0].OrganizationalUnit
# Return nothing
Apparently, such property doesn't exist. However, we could use DistinguishedName
property.
The format of DistinguishedName
property is similar to the below:
CN=ObjectName,OU=Unit1A,OU=Unit1,DC=domain,DC=com
The values represent:
CN=ObjectName
is the name of the particular objectOU=Unit
is for OU structure, starting from the bottom. Top-level OUs are listed lastDC=domain,DC=com
is for Active Directory domain nameBased on the values from DistinguishedName
the easiest way to find the OU would be to strip the first part CN=xxxxx,
and group by that value. We could use calculated property to achieve this.
Let's try to replace the first part with a regular expression:
$objects | Select-Object @{n="OU";e={$_.DistinguishedName -replace "CN=.*,",""}}
The output will be a very long list of something like:
DC=internal
DC=internal
DC=internal
# and so on
Why is that? Let's start from the very beginning. PowerShell is based on .NET. Therefore, regular expressions use the .NET implementation.
In this implementation, regex quantifiers are greedy by default. In contrary to lazy quantifiers, the greedy ones try to match as many characters as possible. See the image below for comparison
In our example, CN=
is matched based on character matching. Next, the *
quantifier matches as many other characters as it can, provided they are followed by ,
.
So, how to match a minimal number of characters? You can probably guess - we need to change the quantifier to be lazy by adding a ?
character! We want our *
quantifier to be lazy. We don't need to apply the same mechanism for the characters we use. It'd not have any effect anyway.
Tip
If you want to learn more about quantifiers check Quantifiers in Regular Expressions article provided by Microsoft.
Our code will now be:
$objects | Select-Object @{n="OU";e={$_.DistinguishedName -replace "CN=.*?,",""}}
And the output will be:
OU=Unit1A,OU=Unit1,DC=domain,DC=com
OU=Unit1B,OU=Unit1,DC=domain,DC=com
OU=Unit2A,OU=Unit2,DC=domain,DC=com
OU=Unit1B,OU=Unit1,DC=domain,DC=com
# and so on
Now we only need to group (and possibly sort) the OU names we got from the previous step. Let's save the output to a variable and use Group-Object and Sort-Object:
# Saving output from previous step
$ous = $objects | Select-Object @{n="OU";e={$_.DistinguishedName -replace "CN=.*?,",""}}
# Groupping
$groupped = $ous | Group-Object OU -NoElement
# And sorting
$groupped | Sort-Object Count -Descending
The output will be:
Count Name
----- ----
100 OU=Unit1B,OU=Unit1,DC=domain,DC=com
21 OU=Unit1A,OU=Unit1,DC=domain,DC=com
15 OU=Unit2A,OU=Unit2,DC=domain,DC=com
# and so on
Now we have stats and we can export or work on them further. Have fun 👍