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.
Passwords Haven’t Disappeared Yet
123456. Qwerty. Iloveyou. No, these are not exercises for people who are brand new to typing. Shockingly, they are among the most common passwords that end users choose in 2021. Research has found that the average business user must manually type out, or copy/paste, the credentials to 154 websites per month. We repeatedly got one question that surprised us: “Why would I ever trust a third party with control of my network?
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.1135188.8.131.523:=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.