Share via


PowerShell: Filter Results with Active Directory Module Cmdlets

Introduction

The Active Directory PowerShell modules support two parameters to filter results. The -LDAPFilter parameter for LDAP syntax filters and the -Filter parameter for PowerShell syntax filters.  The documentation indicates that PowerShell filters should be enclosed in braces (also called curly braces). However, many examples use single or double quotes instead. This article documents some of the resulting issues.


Filter or Where Clause

There are two ways to restrict the output of an AD cmdlet like Get-ADUser. First, you can use the -LDAPFilter or -Filter parameters to filter the output. Second, you can pipe the results to the Where-Object cmdlet. Where possible, the first method is more efficient for two reasons.

  • Filtering is done on the domain controller instead of the local client. The domain controller is more likely to be a server class computer optimized for queries.
  • Filtering results in a smaller resultset sent over the network from the domain controller to the client. In contrast, the Where-Object cmdlet only filters on the local client after the resultset has been sent from the remote computer.

For example, you could retrieve all users with a department that starts with "IT" using the Where-Object cmdlet as follows:

Get-ADUser -Filter * -Properties department | Where-Object {$_.department -Like "it*"} | Select sAMAccountName, department

The resultset from the Get-ADUser statement includes all users in the domain. A more efficient method to get the same results would use a filter, similar to below:

Get-ADUser -Filter {department -Like "it*"} -Properties department | Select sAMAccountName, department

Now only the users needed are included in the resultset from Get-ADUser. In a test domain with 2,150 users (7 of which have "IT" departments) the first command above took 4 times as long as the second (average of 10 trials each with 16 minutes between trials). The difference could be substantial in a domain with ten's of thousands of users.

Also, note that the statements above use the -Properties parameter to specify only the properties needed. The default properties exposed by the cmdlet are always included, like sAMAccountName in this case. If you request all properties, with -Properties *, the resultset will include many properties for each user. The resultset will be much smaller if you only specify the extended properties needed, like department in this case. Repeating the last command above in the test domain with 2,150 users, but requesting all properties (with -Properties *) required 75% more time on average to complete. The default and extended properties exposed by the Get-ADUser cmdlet are documented in Active Directory: Get-ADUser Default and Extended Properties.

↑ Return to Top


LDAP Filter Syntax

You can use the -LDAPFilter parameter to specify an LDAP syntax filter. This is the same syntax as that used in VBScript and command line utilites like dsquery and adfind. This allows you to leverage your experience with these other technologies. You can also use LDAP syntax filters in Active Directory Users and Computers (ADUC). On the "View" menu, select "Filter Options", then "Create Custom", click the "Customize" button, click the "Advanced" tab, and enter your filter, as shown below.

The equivalent PowerShell code to retrieve all users with a department that starts with "IT" using the -LDAPFilter parameter would be as follows:

Get-ADUser -LDAPFilter "(department=it*)" -Properties department | Select sAMAccountName, department

A similar query can be done at the command prompt of a domain controller using the dsquery utility. The extra clauses involving objectCategory and objectClass restrict the output to user objects (if necessary).

dsquery * -Filter "(&(objectCategory=person)(objectClass=user)(department=it*))" -attr sAMAccountName department

Notice the following about LDAP syntax filters (in contrast with PowerShell syntax filters):

  • The filter is a quoted string with each clause in parentheses (there are three clauses in the last example above).
  • The supported operators include "=", ">=", and "<=". The operators ">" and "<" are not supported.
  • Clauses can be combined with the following operators: "&" (the AND operator), "|" (the OR operator), and "!" (the NOT operator).
  • Instead of "(a<b)" you can use "(!(a>=b))". Instead of "(a>b)" you can use "(!(a<=b))".
  • Only Active Directory attributes, designated by their LDAPDisplayName, are recognized. The PowerShell properties, such as Modified and Enabled, are not supported.
  • PowerShell datetime values, such as those retrieved using the Get-Date cmdlet are not supported. The datetime values must be converted into the Generalized-Time format or the LargeInteger format, depending on the attribute (See Using Date Attributes below).

The following table shows some example LDAP syntax filters with the department attribute. The results would be similar using other Active Directory string attributes. In these examples the variable $Dept is defined as follows:

$Dept = "IT Department"

 

Filter Result
-LDAPFilter "(department=$Dept)" Works
-LDAPFilter "(department='$Dept')" No Results
-LDAPFilter "(department=""$Dept"")" No Results
-LDAPFilter '(department=$Dept)' No Results
-LDAPFilter "(department='$Dept')" No Results
-LDAPFilter '(department="$Dept")' No Results
-LDAPFilter "(department=IT Department)" Works
-LDAPFilter "(department=IT*)" Works

Some of the filters above do not raise an error but never produce results. The filter should be enclosed in double quotes, and any variables or constants should not be quoted.

↑ Return to Top


PowerShell Filter Syntax

The PowerShell Active Directory module cmdlets support an extended form of the PowerShell Expression Language. PowerShell documentation indicates that PowerShell syntax filters should be enclosed in braces. But there are many examples where single quotes or double quotes are used instead. As you might expect, this affects how the filter is interpreted.

Using String Attributes

The following table shows some example PowerShell syntax filters using string properties, like Department. Some filters result in error, others do not raise an error but never produce results. The variable $Dept is defined as previously.

Filter Result
-Filter {department -eq "IT Department"} Works
-Filter {department -eq $Dept} Works
-Filter {department -eq "$Dept"} No Results
-Filter {department -eq '$Dept'} No Results
-Filter "department -eq $Dept" Error
-Filter 'department -eq $Dept' Works
-Filter {department -eq "it*"} No Results
-Filter {department -Like "it*"} Works
-Filter "department -Like ""it*""" Works
-Filter "department -Like 'it*'" Works
-Filter 'department -Like "it*"' Works
-Filter 'department -Like ''it*''' Works
-Filter {department -ge "IT"} Works

Some of these results may not be expected. For example, you might expect enclosing a variable in a quoted string to work. The best policy might be to always enclose PowerShell syntax filters in braces, and to refrain from quoting variables.

The last example using the "-ge" operator is only useful in rare situations. The filter will result in any departments that are lexicographically greater than or equal to "IT". For example, it will return "Test Department", because "T" is greater than "I".

↑ Return to Top


Using DN Attributes

DN attributes are those where the value must be a distinguished name. Examples are member, memberOf, and manager. The wildcard character, "*", is not allowed within DN values. The following table shows results from various filters with the DN attribute manager. In these examples, the variable $DN is defined similar to the following:

$DN = "cn=Jim Smith,ou=Sales,ou=West,dc=MyDomain,dc=com"

 

Filter Result
-Filter {manager -eq "cn=Jim Smith,ou=Sales,ou=West,dc=MyDomain,dc=com"} Works
-Filter {manager -eq $DN} Works
-Filter "manager -eq $DN" Error
-Filter 'manager -eq $DN' Works
-Filter 'manager -eq "$DN"' Error
-Filter "manager -eq '$DN'" Works
-Filter {manager -eq "$DN"} Error
-Filter {manager -eq '$DN'} Error
-Filter {manager -Like "cn=Jim Smith,ou=Sales,*"} Error

The last example in the table is one case where you must pipe the results to the Where-Object cmdlet. The desired filter is not allowed. The following can be used instead:

Get-ADUser -Filter * -Properties manager | Where-Object {$_.manager -Like "cn=Jim Smith,ou=Sales,*"} | Select sAMAccountName, manager

Even here, it would help to filter the resultset as much as possible, to avoid retrieving all users in a large domain. In this case, it would be better to filter on users that have any value for the manager attribute. This would eliminate users with no value assigned. For example:

Get-ADUser -LDAPFilter "(manager=*)" -Properties manager | Where-Object {$_.manager -Like "cn=Jim Smith,ou=Sales,*"} | Select sAMAccountName, manager

↑ Return to Top


Using Date Attributes

Dates in Active Directory can be in one of two formats. Some attributes use Generalized-Time format, a string in the form "YYYYMMDDhhmmss.0Z". For example "20141006133000.0Z" corresponds to October 6, 2014, at 13:30:00 (in UTC). Handling these dates is discussed here: Active Directory: Generalized-Time Attributes. AD attributes in Generalized-Time format include whenChanged. The AD module cmdlets expose the property Modified, which is the same as whenChanged, but converted into a PowerShell date in the local time zone.

Other date attributes in Active Directory are in LargeInteger format (also called Integer8). These are 64-bit integers representing dates as the number of 100-nanosecond intervals since 12:00 am, January 1, 1601. Handling these dates is discussed here: Active Directory: Large Integer Attributes. Examples of AD attributes in this format include accountExpires and pwdLastSet. The corresponding properties exposed by the PowerShell AD cmdlets are AccountExpirationDate and PasswordLastSet. Again, these PowerShell properties convert the AD attributes into PowerShell datetime values in the local time zone.

If you use the -LDAPFilter parameter, you must code either the Generalized-Time string or the Large Integer value, depending on the attribute. PowerShell datetime values will not be recognized. However, if you use the -Filter parameter, you can use the Generalized-Time string values, Large Integer values, or PowerShell datetime values. All will be recognized. The variables in the following table are defined similar to below:

# PowerShell date 30 days in the past.
$PSDate = (Get-Date).AddDays(-30)


# Generalized-Time value corresponding to October 6, 2014, 13:30:00.
$GTDate = "20141006133000.0Z"


# LargeInteger (Integer8) datetime value corresponding to October 14, 2014 (U.S. Central Time).
$LargeIntDate = "130579128000000000"

 

Filter Result
-LDAPFilter "(whenChanged>=$GTDate)" Works
-LDAPFilter '(whenChanged>=$GTDate)' No Results
-LDAPFilter "(whenChanged>='$GTDate')" No Results
-LDAPFilter '(whenChanged>="$GTDate")' No Results
-LDAPFilter "(whenChanged>=20141006123500.0Z)" Works
-Filter {Modified -ge $PSDate} Works
-Filter {Modified -ge $GTDate} Works
-Filter {Modified -ge "$((Get-Date).AddDays(-30))"} No Results
-Filter {Modified -ge "09/26/2014 16:06:22"} Works
-Filter {Modified -ge '09/26/2014 16:06:22'} Works
-Filter {whenChanged -ge $PSDate} Works
-Filter {whenChanged -ge $GTDate} Works
-Filter {modifyTimeStamp -ge $PSDate} Works
-Filter {modifyTimeStamp -ge $GTDate} Works
-Filter {AccountExpirationDate -gt $PSDate} Works
-Filter {AccountExpirationDate -gt "10/16/2014"} Works
-Filter {accountExpires -ge $LargeIntDate} Works
-Filter {accountExpires -ge "10/01/2014"} No Results
-LDAPFilter "(accountExpires>=130579128000000000) Works
-LDAPFilter "(accountExpires>=$LargeIntDate) Works

↑ Return to Top


See Also

↑ Return to Top


Other Resources

↑ Return to Top