Scaling the PowerShell Active Directory Searcher
Over the course of the last several articles, I have been demonstrating how to find Active Directory objects using the Active Directory Searcher Object from .NET and PowerShell. I have purposely kept my examples simple and constrained but today we are running free. I need to show you how to manage large environments.
What is “Inside Microsoft Teams”?
“Inside Microsoft Teams” is a webcast series, now in Season 4 for IT pros hosted by Microsoft Product Manager, Stephen Rose. Stephen & his guests comprised of customers, partners, and real-world experts share best practices of planning, deploying, adopting, managing, and securing Teams. You can watch any episode at your convenience, find resources, blogs, reviews of accessories certified for Teams, bonus clips, and information regarding upcoming live broadcasts. Our next episode, “Polaris Inc., and Microsoft Teams- Reinventing how we work and play” will be airing on Oct. 28th from 10-11am PST.
I know that my domain has 6351 user accounts. Let’s see what I get with the searcher object.
$searcher = New-Object system.DirectoryServices.DirectorySearcher $searcher.filter = "(objectclass=user)" $all = $searcher.FindAll()
Well, that cannot be right. Actually, it is. By default, the searcher object will only return the first 1000 matching objects. Heaven forbid that we ask a server to do any work! The solution is to modify the searcher’s PageSize property. The default value is 0. You can set it to a new value and PowerShell will return matching objects in pages or groups of that number.
$searcher.PageSize=100 $all = $searcher.FindAll() $all.count
I have experimented with different page sizes and cannot seem to find any value that is necessarily better than another. I tend to use 100 but you should experiment in your own test environment.
Regardless, $all now has 6447 objects! I have cleared the first hurdle and appear to be getting more than 1000 objects. However, I have even more than expected.
My searcher is defaulting to the domain root, so I know it is not missing anything. Using my Convert-ADSearchResult function from a previous article, I can group the results on the object type.
This is interesting, especially for those of you who are not up to speed on LDAP schemas. When we refer to a user account, this is also a Person. Likewise, computer accounts can be counted as Users. The solution is to refine my search filter.
$searcher.filter = "(&(objectcategory=person)(objectclass=user))" $all = $searcher.FindAll()
If you recall in a previous article, I showed you how to create a compound filter. Let’s check again.
If you are searching for users or other specifics in a large environment, you might need filters like this:
$searcher.filter = "(&(objectcategory=person)(objectclass=user)(name=mysampleuser3*))" $searcher.findall()
Or keep extending your filter:
$searcher.filter = "(&(objectcategory=person)(objectclass=user)(name=a*)(title=tester))" $searcher.findall()
That filter may look complicated. I will admit, they can be tricky to write. Let’s look at a trick.
Open Active Directory Users and Computers. Right-click on Saved Queries and select New – Query.
If you plan on saving the query, you can enter a name. Otherwise, click the Define Query button. Use the dialogue box to configure whatever query you need.
I am going to build a query to find all disabled accounts. Click OK. You should now see the LDAP query string.
You can select that text and copy it to Clipboard. Use that as your new search filter.
$searcher = New-Object system.DirectoryServices.DirectorySearcher $searcher.filter = "(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113522.214.171.1243:=2))" $searcher.PageSize = 100
There is no way I could have figured out that query on my own.
Now, I could pipe $disabled to Where-Object and filter out things like Guest. That would be easier than trying to modify the filter. A better choice would be to restrict my search scope.
You could use the Saved Query wizard to get the filter syntax for your common search needs and create a set of PowerShell tools. In fact, I have given you a number of PowerShell functions that could serve as the starting point for your own toolbox. I might put together my own module when I have a little time. Although, I happened to check the PowerShell Gallery and found a very complete module called AdsiPS from fellow MVP Francois-Xavier Cat. He has assembled a complete toolkit that uses ADSI from the .NET Framework to manage Active Directory. This is very similar to what I have been demonstrating. None of it should require the RSAT Active Directory module. Run Install-Module AdsiPS to download and install it in your test network.
I hope you have found this series helpful. As always, comments welcome.