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
The format of
DistinguishedName
CN=ObjectName,OU=Unit1A,OU=Unit1,DC=domain,DC=com
The values represent:
CN=ObjectName
OU=Unit
DC=domain,DC=com
Based on the values from
DistinguishedName
CN=xxxxx,
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=
*
,
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
?
*
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 👍