Finding Groups with the Active Directory Searcher and PowerShell

AD groups
We will continue looking at ways to use the Active Directory Searcher with PowerShell. If you are just joining in, you might want to start at the beginning with this article. If you have been following along, you know that this is a powerful way to work with Active Directory. It works without requiring the Active Directory module that is a part of Remote Server Administration Tools (RSAT). Let’s start with a fresh searcher object on a domain member that is configured with a filter to find group objects.
 

 

$searcher = New-Object system.DirectoryServices.DirectorySearcher
$searcher.filter = "(objectclass=group)"

We will leave the search root alone, which will default to the domain root. Let’s see what type of object we can find.
 
Getting an AD group object (Image Credit: Jeff Hicks)

Getting an AD Group Object (Image Credit: Jeff Hicks)

 
Even though this is a group, it is still an Active Directory search result. This means that I can use techniques from my previous articles like specifying the properties to retrieve and Convert-ADSearchResult function.

$props = "distinguishedname","name","member","description",
"whencreated","whenchanged","grouptype"
$searcher.PropertiesToLoad.Clear()
foreach ($item in $props) {
    $searcher.PropertiesToLoad.Add($item) | out-null
}

 
Now, I can get all the groups in my domain.

A sample group (Image Credit: Jeff Hicks)
A Sample Group (Image Credit: Jeff Hicks)

 
I can look at $groups by itself. I can also pipe it to Out-Gridview or Export-Csv. Really, I can do just about anything I want with it.

There is one bit of information that is not readily apparent in the output. It is difficult to tell what is a security or distribution group and whether it is global or universal. This information is available but it is hidden in the GroupType property.  When you search for a group, you should get a result with a GroupType property that matches one of these values:

Value Scope Category
2 Global Distribution
8 Universal Distribution
-2147483640 Universal Security
-2147483643 DomainLocal Security
-2147483644 DomainLocal Security
-2147483646 Global Security

 
The -2147483643 value is for built-in groups and -2147483644 is for default groups found in the User container. Of course, you can filter for any of the group types.

$searcher.filter = "(&(objectclass=group)(grouptype=-2147483644))"
$searcher | Convert-ADSearchResult | Out-Gridview

Filtered groups by type (Image Credit: Jeff Hicks)
Filtered Groups by Type (Image Credit: Jeff Hicks)

 
What would make this even easier? Let’s look at a function to get group objects.

#requires -version 5.0
#dot source my conversion function
. .\Convert-ADSearchResult.ps1
Function Get-MyADGroupObject {
[cmdletbinding()]
Param(
[Parameter(Position = 0)]
[ValidateNotNullorEmpty()]
[string]$Name="*",
[ValidatePattern("^LDAP://")]
[string]$SearchRoot
)
$searcher = New-Object system.DirectoryServices.DirectorySearcher
$searcher.PropertiesToLoad.Clear()
$props = "distinguishedname","name","member","description",
"whencreated","whenchanged","grouptype"
foreach ($item in $props) {
    $searcher.PropertiesToLoad.Add($item) | out-null
}
$searcher.filter = "(&(objectclass=group)(name=$name))"
if ($SearchRoot) {
    $searcher.SearchRoot = $SearchRoot
}
$groups = $searcher.findAll() | Convert-ADSearchResult
if ($groups) {
foreach ($group in $groups) {
    switch ($group.GroupType -as [string]) {
      "2" {
        $group | Add-Member -MemberType Noteproperty -Name GroupScope -Value "Global"
        $group | Add-Member -MemberType Noteproperty -Name GroupCategory -Value "Distribution"
       }
      "8" {
        $group | Add-Member -MemberType Noteproperty -Name GroupScope -Value "Universal"
        $group | Add-Member -MemberType Noteproperty -Name GroupCategory -Value "Distribution"
      }
     "-2147483640" {
        $group | Add-Member -MemberType Noteproperty -Name GroupScope -Value "Universal"
        $group | Add-Member -MemberType Noteproperty -Name GroupCategory -Value "Security"
      }
      "-2147483643" {
        $group | Add-Member -MemberType Noteproperty -Name GroupScope -Value "DomainLocal"
        $group | Add-Member -MemberType Noteproperty -Name GroupCategory -Value "Security"
      }
      "-2147483644" {
        $group | Add-Member -MemberType Noteproperty -Name GroupScope -Value "DomainLocal"
        $group | Add-Member -MemberType Noteproperty -Name GroupCategory -Value "Security"
      }
      "-2147483646" {
        $group | Add-Member -MemberType Noteproperty -Name GroupScope -Value "Global"
        $group | Add-Member -MemberType Noteproperty -Name GroupCategory -Value "Security"
      }
      Default {
        $group | Add-Member -MemberType Noteproperty -Name GroupScope -Value "Unknown"
        $group | Add-Member -MemberType Noteproperty -Name GroupCategory -Value "Unknown"
      }
  } #Switch
  $Group | Add-Member -MemberType ScriptProperty -Name MemberCount -Value {$this.member.count}
} #foreach
$groups
}
else {
    Write-Warning "Could not find group $Name under $($searcher.SearchRoot.Path)"
}
} #end function

 
This relies on my conversion function to properly format the results. The function properly decodes the GroupType property and adds 2 new properties, GroupScope and GroupCategory. It also adds a property that shows the total member count. Now, I have an easy way to find a group.
 

Testing the AD group function (Image Credit: Jeff Hicks)
Testing the Active Directory Group Function (Image Credit: Jeff Hicks)

 
I can also limit the search to a particular organizational unit. Just like with any other PowerShell command, I can use the output.

Get-MyADGroupObject -SearchRoot "LDAP://OU=employees,DC=globomantics,dc=local" |
sort MemberCount -Descending |
Select -first 10 -Property distinguishedname,name,description,WhenCreated,whenchanged,membercount |
Out-GridView -Title "Top Groups"

Getting AD groups by member count (Image Credit: Jeff Hicks)
Getting Active Directory Groups by Member Count (Image Credit: Jeff Hicks)

 

You could easily extend the function to add other filtering options, such as group category, scope, or test for empty groups. I hope you will grab a copy and try it out in your test environment. Next time, we will look at how to use the Active Directory Searcher with thousands of results. Feel free to leave a comment about what you think of this little series.