Active Directory|PowerShell

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.



Sponsored Content

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()

Total users found (Image Credit: Jeff Hicks)
Total Users Found (Image Credit: Jeff Hicks)

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.

$all = $searcher.FindAll()

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.

Grouping results by object type (Image Credit: Jeff Hicks)
Grouping Results by Object Type (Image Credit: Jeff Hicks)

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.

Refined search results (Image Credit: Jeff Hicks)
Refined Search Results (Image Credit: Jeff Hicks)

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*))"

Limiting search (Image Credit: Jeff Hicks)
Limiting Search (Image Credit: Jeff Hicks)

Or keep extending your filter:

$searcher.filter = "(&(objectcategory=person)(objectclass=user)(name=a*)(title=tester))"

Using a compound search filter (Image Credit: Jeff Hicks)
Using a Compound Search Filter (Image Credit: Jeff Hicks)

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.

New AD Query (Image Credit: Jeff Hicks)
New Active Directory Query (Image Credit: Jeff Hicks)

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.

Defining a custom query (Image Credit: Jeff Hicks)
Defining a Custom Query (Image Credit: Jeff Hicks)

I am going to build a query to find all disabled accounts. Click OK. You should now see the LDAP query string.

Getting the LDAP query string (Image Credit: Jeff Hicks)
Getting the LDAP Query String (Image Credit: Jeff Hicks)

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.113556.1.4.803:=2))"
$searcher.PageSize = 100

There is no way I could have figured out that query on my own.

Showing disabled accounts (Image Credit: Jeff Hicks)
Showing Disabled Accounts (Image Credit: Jeff Hicks)

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.

Limiting the search (Image Credit: Jeff Hicks)
Limiting the Search (Image Credit: Jeff Hicks)

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.

Related Topics:


Don't have a login but want to join the conversation? Sign up for a Petri Account

Comments (0)

Leave a Reply