Active Directory|PowerShell

Fine Tuning the Active Directory Searcher

Last time, we started looking at the Active Directory Searcher object and how to find a single user object. If you missed it, take a moment to get caught up.



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?

If you recall, I created a simple searcher.

$searcher = New-Object system.DirectoryServices.DirectorySearcher
$searcher.filter = "samaccountname=jeff"

My Active Directory (AD) domain is not especially large, so the query does not take long to run. However, you will want to fine tune your search to be as specific and limited as possible. We will look at some filtering techniques later. Right now, I want to focus on search scope by discussing how much AD you will need to search.

When you create a search object, it defaults to the domain root for the current logged on user.

The searcher search root (Image Credit: Jeff Hicks)
The Searcher Search Root (Image Credit: Jeff Hicks)

When I invoked the FindOne() method, it searched the entire domain structure. That may be perfectly acceptable if you have no idea where the object is located. In my case, I know that all active user accounts are under the Employees organizational unit. I will need to know the distinguished name, and assuming that I do, I can reposition my search.

$searcher.SearchRoot = "LDAP://ou=employees,dc=globomantics,dc=local"

Now the search runs very quickly.

Searching for an object (Image Credit: Jeff Hicks)
Searching For an Object (Image Credit: Jeff Hicks)

This search took 27ms and it took 35ms when searching the entire domain. Sure, that is hardly earth shattering but this will make a difference when searching for many objects. If you have a large domain, it will make a bigger difference.

Another tweak you can make is to modify the set of object properties that get returned. If you recall from last time, the user account result came back with 75 properties. What if I know I only need a small subset? I can configure the searcher to only retrieve those properties.

This takes a little juggling to get the property names loaded.

$props = "distinguishedname","name","samaccountname","title","department","directreports"
foreach ($item in $props) {
    $searcher.PropertiesToLoad.Add($item) | out-null

Search Properties to Load (Image Credit: Jeff Hicks)
Search Properties to Load (Image Credit: Jeff Hicks)

The result is now limited to these properties and ran a little quicker at 22ms.

Limited Property Results (Image Credit: Jeff Hicks)
Limited Property Results (Image Credit: Jeff Hicks)

You can use the techniques from the previous article to clean this up.

You can also get the complete object with the GetDirectoryEntry() method. If you recall, the FindOne() and FindAll() methods write a result object to the pipeline, not the actual object.

Getting the full directory entry (Image Credit: Jeff Hicks)
Getting the Full Directory Entry (Image Credit: Jeff Hicks)

Here is another way to display properties cleanly:

$entry | Select @{Name="DN";Expression={$_.DistinguishedName.value}},
@{Name="DirectReports";Expression = {$_.directreports.value}}

Selected Properties (Image Credit: Jeff Hicks)
Selected Properties (Image Credit: Jeff Hicks)

I could have easily retrieved these properties alone in the searcher without having to get the full object. If you want to do a full directory entry, you do not have to resort to 100 Select-Object expressions.

Foreach -begin {
  $h = @{}
} -process {
} -end {
    new-object psobject -Property $h

A better property display (Image Credit: Jeff Hicks)
A Better Property Display (Image Credit: Jeff Hicks)

Let’s put everything we have looked at into a function that will get the full user object from AD with nothing but the SamAccountname.

Function Get-MyADUserObject {
[Parameter(Position = 0, Mandatory)]

$searcher = New-Object system.DirectoryServices.DirectorySearcher
$searcher.filter = "samaccountname=$SamAccountName"

#limit search properties since we're going to get the complete user object
$searcher.PropertiesToLoad.Add("distinguishedname") | out-null

if ($SearchRoot) {
    $searcher.SearchRoot = $SearchRoot

$user = $searcher.FindOne()

if ($user.Path) {
    $entry = $user.GetDirectoryEntry()

    Foreach -begin {
      $h = @{}
    } -process {       
    } -end {
        new-object psobject -Property $h
else {
    Write-Warning "Could not find user $samaccountname under $($searcher.SearchRoot.Path)"

} #end function

Feel free to expand upon this. Now, I have a tool to get a user from AD that writes an object to the pipeline. I can work with this.

Testing the function (Image Credit: Jeff Hicks)
Testing the Function (Image Credit: Jeff Hicks)

Everything we have been doing for a single object, we can also do for many. We will start down that path next time.

Related Topics:


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

Comments (0)

Leave a Reply

Don't leave your business open to attack! Come learn how to protect your AD in this FREE masterclass!REGISTER NOW - Thursday, December 2, 2021 @ 1 pm ET

Active Directory (AD) is leveraged by over 90% of enterprises worldwide as the authentication and authorization hub of their IT infrastructure—but its inherent complexity leaves it prone to misconfigurations that can allow attackers to slip into your network and wreak havoc. 

Join this session with Microsoft MVP and MCT Sander Berkouwer, who will explore:

  • Whether you should upgrade your domain controllers to Windows Server
    2019 and beyond
  • Achieving mission impossible: updating DCs within 48 hours
  • How to disable legacy protocols and outdated compatibility options in
    Active Directory

Sponsored by: