Last Update: Sep 04, 2024 | Published: May 22, 2017
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)
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.
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
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.
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"
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.