A Blog on Wndows Server and Microsoft Exchange by Thomas Pätzold

Archive for February 2011

Searching the Active Directory

leave a comment »

Since Windows 2000 Server the old Windows NT Directory service also known as SAM (Security Account Manager) is replaced by the Active Directory. The Active Directory is a hierarchical directory service optimized for searching objects. You need to know how to connect to your Active Directory and using the DirectorySearcher object in order to perform searches. It is important to know how searches are processed. Detailed information on this topic can be found on TechNet.

Before you are able to connect to your Active Directory using LDAP you have to determine your Active Directory Domain Controller. In order to get this information  you can use the RootDSE Object introduced in LDAPv3. In order to connect this object you can use e.g. LDP.exe on your Domain Controller. Each user is able to connect to the RootDES object without authentication, so you only need to bind to your domain name. If you want to read more information about the LDAPv3 protocol and the definition of the rootDSE object  please read RFC2251 a technical description you will find in RFC3377.

using LDP.EXE connecting to rootDSE

Now we use the Windows PowerShell in order to connect to the rootDSE object. If you start the command

$Root = [ADSI]LDAP://RootDSE

on your domain controller or a connected workstation you get the same information as using the LDP.EXE command.

Connecting RootDSE object using Windows Powershell

One of the most important attributes of the rootDSE object is the DefaultNamingContext. Because of the value of this property you are able to connect to your Active Directory from any workstation connected to it. The benefit of using this property instead of using the physical name of your Active Directory is one script can run against different Active Directories. The following script connects to the Active Directory running search on it and displays the result.

# Connecting to the Active Directory
# determine the name of the Active Directory
$Root = [ADSI]"LDAP://RootDSE"
$DomainLDAP= "LDAP://"+$Root.DefaultnamingContext
# Connecting to the Active Directory
$objDomain = New-Object System.DirectoryServices.DirectoryEntry($DomainLDAP)
# Creating the searcher object
$strFilter = "((objectCategory=user))"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = $strFilter
# Create the property list which is included in the result
$colProplist = "sAMAccountName","userPrincipalName","homeDirectory","profilePath";"terminalservicesprofilepath","terminalserviceshomedirectory"
foreach ($i in $colPropList){$amount=$objSearcher.PropertiesToLoad.Add($i)}
$Results = $objSearcher.FindAll()
foreach ($obj in $Results){
   $item = $obj.Properties
   $user = [ADSI]([String]$item.adspath)
   # if the attribute isn´t set then you will get an error. So we use a try-catch statement
   try{$item_TerminalServicesHomeDirectory = $user.psbase.invokeget("TerminalServicesHomeDirectory")}
   catch {$item_TerminalServicesHomeDirectory = ""}
   # if the attribute isn´t set then you will get an error. So we use a try-catch statement
   try{$item_TerminalServicesProfilePath = $user.psbase.invokeget("TerminalServicesProfilePath")}
   catch {$item_TerminalServicesProfilePath = ""}
   write-output $item.adspath
   write-Output $item.samaccountname
   write-output $item_terminalserviceshomedirectory
   write-output $item.homedirectory
   write-Output $item_terminalservicesprofilepath
   write-output $item.profilepath

There are some parameters within the searcher object that should be explained further:

Paged Searches

In order to optimize your directory search you have to use a paged search. The default value for the parameter PageSize is 0 this meanS no paging is occuring and that only 1000 objects are returned as the result. This parameter specifies the maximum number of objects in each page that is returnd from the search commnd. You are able to configure this parameter by configuring the parameter PageSize. If you want to retrive more than 1000 objects you have to set the PageSize parameter to a value between 0 and 1000. The number of objects returnd by the search will be also affected by the sizeLimit parameter.

The Code looks like this:

$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 1000

After this you can use a foreach-loop in order to step through the resultant set.


The parameter SizeLimit contains the maximum amount of objects returned by a search. So the parameter PageSize splits this amount in parts which are transferred to the client.

Search Scope

There are three different search scopes defined in the SearchScope Parameter:

  • Base – Only Objects within the Searchroot are returned by the search
  • One Level – Only Objects within the first Level of the directory hierarchy
  • Subtree – Objects with the search root, and all descendant objects


This collection contains all properties of the directory objects contained in the resultant set. The attribute “ADsPath” is always part of the result, nevertheless you have specified this parameter or not. If you don´t have specified this parameter you will get all attributes of a specific object which will mean that a lot of resources (network traffic, processor, memory) are required in order to execute the search request. So it is a good idea to limit the attributes to that, that are uses further in the script. A list of possible properties to include in the result, you will find in the schema of your Active Directory. If you want to look your own schema in Active Directory you have to install the schema.dll on your domain controller.

Terminalserver Properties

Some user properties are stored in a binary attribute called user Parameters. So the question is how to get the values of such Attributes.

foreach ($obj in $Results){
    $item = $obj.Properties
    $user = [ADSI]([String]$item.adspath)
    # if the attribute isn´t set then you will get an error. So we use a try-catch statement
    try{$item_TerminalServicesHomeDirectory = $user.psbase.invokeget("TerminalServicesHomeDirectory")}
    catch {$item_TerminalServicesHomeDirectory = ""}
    # if the attribute isn´t set then you will get an error. So we use a try-catch statement
    try{$item_TerminalServicesProfilePath = $user.psbase.invokeget("TerminalServicesProfilePath")}
    catch {$item_TerminalServicesProfilePath = ""}

The methods for reading and setting  the terrminalserver attributes are defined in the class System.DirectoryServices.DirectoryEntry (you can also tpye [ADSI] in order to generate an object of this type) but as you see at this point of script we only have the object $obj which type is System.DirectoryServices.SearchResult. So in order to get the attribute values we have to generate an object of type DirectoryEntry for the actual user-object stored in $obj. User Objects which are created during the setup process of Windows haven´t got these terminalserver attributes so the script generates an error if accessing these objects. So I used the try-catch block in order to do the error handling.

Written by Thomas Pätzold

February 11, 2011 at 2:32 pm

%d bloggers like this: