With the release of Windows Server 2008 R2, Microsoft released a Windows PowerShell module for managing Active Directory. To be able to use the PowerShell module you must have at least one domain controller running Windows Server 2008 R2, or at least one domain controller running Windows Server 2003 or Windows Server 2008 with the Active Directory Management Gateway Service installed. The PowerShell module is also available in Windows 7 as part of the Remote Server Administration Tools (RSAT).
In this article we`re going to focus on filtering performance when using the Microsoft Active Directory PowerShell module.
Filtering search results
We start by looking at a common cmdlet, Get-ADUser. This cmdlet has two parameter sets, each with one mandatory parameter; -Filter and -LDAPFilter.
The following table shows frequently used search filter operators.
The left column lists operators valid for the Filter parameter, while the right column lists the equivalent for the LDAPFilter parameter.
| Operator | Description | LDAP Equivalent |
| -eq | Equal to. This will not support wild card search. | = |
| -ne | Not equal to. This will not support wild card search. | !x = y |
| -approx | Approximately equal to | ~= |
| -le | Lexicographically less than or equal to | <= |
| -lt | Lexicographically less than | !x >= y |
| -ge | Lexicographically greater than or equal to | >= |
| -and | AND | & |
| -or | OR | | |
| -not | NOT | ! |
| -bor | Bitwise OR | :1.2.840.113556.1.4.804:= |
| -band | Bitwise AND | :1.2.840.113556.1.4.803:= |
| -recursivematch | Use LDAP_MATCHING_RULE_IN_CHAIN (Note: This control only works with Windows 2008 and later.) | :1.2.840.113556.1.4.1941:= |
| -like | Similar to -eq and supports wildcard comparison. The only wildcard character supported is: * | = |
| -notlike | Not like. Supports wild card comparison. | !x = y |
If we take a look at Get-Help Get-ADUser –Parameter Filter we can see the following description:
Specifies a query string that retrieves Active Directory objects. This string uses the PowerShell Expression Language syntax. The PowerShell Expression Language syntax provides rich type-conversion support for value types received by the Filter parameter. The syntax uses an in-order representation, which means that the operator is placed between the operand and the value.
Examples on using the Filter parameter
Retrieve all users objects:
Get-ADUser -Filter *
Retrieve all user with a name beginning with “test”:
Get-ADUser -Filter {name -like "test*"}
Retrieve all users objects with an e-mail address:
Get-ADUser -Filter {EmailAddress -like "*"}
If we also take a look at Get-Help Get-ADUser –Parameter LDAPFilter we can see the following description: Specifies an LDAP query string that is used to filter Active Directory objects. You can use this parameter to run your existing LDAP queries. The Filter parameter syntax supports the same functionality as the LDAP syntax.
Examples on using the LDAPFilter parameter
Retrieve all users objects:
Get-ADUser -LDAPFilter "(&(objectCategory=Person)(objectClass=User))"
Retrieve all user with a name beginning with “test”:
Get-ADUser -LDAPFilter "(mail=*)"
Retrieve all users objects with an e-mail address:
Get-ADUser -LDAPFilter "(name=test*)"
For more information, see Get-Help about_ActiveDirectory_Filter.
For new PowerShell users which has got comfortable with the common core PowerShell cmdlets, a natural approach to filter information is to retrieve all users and filter the results using Where-Object. There is nothing wrong using this approach, which works fine, but it will be slower and also consume more resources especially in larger environments.
PowerShell has a builtin cmdlet called Measure-Command which we can use to compare filtering performance. The basic syntax is Measure-Command -Expression {command-to-measure}
To show the impact the size of the Active Directory database has, the following test is performed both in a small environment with ~100 users (Environment A) and in a larger environment with ~20 000 users (Environment B).
Environment A
Measure-Command -Expression {Get-ADUser -Filter * | Where-Object {$_.name -like "test*"}} | Format-List TotalSeconds
TotalSeconds : 0,2395828
Measure-Command -Expression {Get-ADUser -Filter {name -like "test*"}} | Format-List TotalSeconds
TotalSeconds : 0,0207963
Measure-Command -Expression {Get-ADUser -LDAPFilter "(name=test*)"} | Format-List TotalSeconds
TotalSeconds : 0,0128277
Environment B
Measure-Command -Expression {Get-ADUser -Filter * | Where-Object {$_.name -like "test*"}} | Format-List TotalSeconds
TotalSeconds : 64,485127
Measure-Command -Expression {Get-ADUser -Filter {name -like "test*"}} | Format-List TotalSeconds
TotalSeconds : 0,0788577
Measure-Command -Expression {Get-ADUser -LDAPFilter "(name=test*)"} | Format-List TotalSeconds
TotalSeconds : 0,0418277
We can see that performing the filtering on the server side by using the -Filter and -LDAPFilter parameters is much faster than filtering on the client side. We also see that in the smaller environment, the difference isn`t noticeable to the user running the command.
So far we can clearly see that using the filter parameters is important in larger environments. The next question we`ll ask, what is the recommended filter parameter? The answer is: It depends.
Which of the following is the easiest to read and understand?
Get-ADObject -LDAPFilter "(&(|(objectclass=user)(objectclass=computer))(memberOf=CN=Test Group,OU=Groups,DC=domain,DC=local))"
Get-ADObject -Filter 'objectclass -eq "user" -or objectclass -eq "computer" -and memberof -eq "CN=Test Group,OU=Groups,DC=domain,DC=local"'
Most people will find the syntax for the Filter parameter be the easiest to understand. In the previous example we used Get-ADObject rather than Get-ADUser, and this makes filtering even more important since all object classes is retrieved.
Let`s compare the performance of the filtering in the above example:
Measure-Command -Expression {Get-ADObject -LDAPFilter "(&(|(objectclass=user)(objectclass=computer))(memberOf=CN=Test Group,OU=Groups,DC=domain,DC=local))"} | Format-List TotalSeconds
TotalSeconds : 0,4034246
Measure-Command -Expression {Get-ADObject -Filter 'objectclass -eq "user" -or objectclass -eq "computer" -and memberof -eq "CN=Test Group,OU=Groups,DC=domain,DC=local"'} | Format-List TotalSeconds
TotalSeconds : 25,0171279
We can see that LDAP-filtering is more than 60 times faster.
Generally, the –Filter parameter is recommended because it`s easy to use and has an acceptable performance. In some situations, especially when working with Get-ADObject, LDAP filtering is by far the fastest.
If using Where-Object rather than LDAP filtering when using Get-ADObject, the performance difference might be several hours (of course depending on the size of the environment).
If you got questions related to working with Active Directory from PowerShell I would like to encourage you to post your question in the Windows PowerShell forum on Microsoft TechNet.



