One of the most common hangups when querying Active Directory with PowerShell is how to properly build filter syntax. The Filter and LDAPFilter parameters on all ActiveDirectory PowerShell module cmdlets is a black box to many.

In this blog post, we're going to dive deep into understanding how to use Active Directory filters. I hope by the end of this post, you'll no longer be attempted to use that Where-Object and filter right!

Prerequisites

For any of the code I'm about to show you to work, I'll be assuming a few things:

There are two different filter languages you can use when searching for objects using many of the Active Directory cmdlets: PowerShell filters and LDAP filters.

PowerShell Filters

PowerShell filters use the standard PowerShell expression syntax. This is commonly referred to as Active Directory search filter syntax.

These filters are used with the the Filter parameter. The Filter parameter syntax

Operators

While building a filter for the Filter parameter, you'll need to use at least one operator. The operators used here are the familiar operators you may be used to when using commands like Where-Object.

Inside of the Filter parameter, you can use the following operators.

Operator Explanation
-eq Equal to
-le Less than or equal to
-ge Greater than or equal to
-ne Not equal to
-lt Less than
-gt Greater than
-approx Approximately equal to
-bor Bitwise OR
-band Bitwise AND
-recursivematch Recursive match
-like Like
-notlike Not like
-and Boolean AND
-or Boolean OR
-not Boolean NOT

Reference AD Object Properties

Inside of the filter, you will compare various AD object properties using operators. For example, the Get-AdUser cmdlet returns a Name property. If you'd like to find all users matching a specific name, you'd use:

PS51> Get-Aduser -Filter "Name -eq 'Adam Bertram'"

Property names can be the name or LDAP name of the property returned with the AD cmdlet.

Property values are normally wrapped in single or double quotes. The only wildcard accepted is the asterisk (*). You can see above that the filter is surrounded by double quotes yet Adam Bertram is surrounded with single quotes.

Certain characters must be 'escaped' when used in filters. These are:

Character Escaped As Notes
" `" Only required if the data is enclosed in double quotes.
' \' Only required if the data is enclosed in single quotes.
NUL \00 This is a standard LDAP escape sequence.
\ \5c This is a standard LDAP escape sequence.
* \2a Automatically escaped. Only in -eq and -ne comparisons. You should use -like and -notlike operators for wildcard comparison.
( /28 Automatically escaped.
) /29 Automatically escaped.
/ /2f Automatically escaped.

LDAP Filters

Active Directory implements LDAP, the Lightweight Directory Access Protocol. Using the LDAPFilter parameter with the cmdlets allows you to use LDAP filters, such as those created in Active Directory Users and Computers.

The syntax for LDAP search filters is defined in RFC number 4515 .

Each filter rule is surrounded by parentheses ( ). Filter rules can be grouped by surrounding the group in parentheses and including a comparator from the following:

Operator Function
& and
| or
! not

LDAP filters also have a special matching rule Object IDentifiers (OIDs):

OID Purpose
1.2.840.113556.1.4.803 Bitwise AND
1.2.840.113556.1.4.804 Bitwise OR
1.2.840.113556.1.4.1941 Chain matching (for distinguished name attributes)

There are four filter types:

Operator Explanation
= Equal to
~= Approximately equal to
>= Greater than or equal to
<= Less than or equal to

There are four item types:

Type Explanation
= Simple
=* Present
=something* Substring
Extensible varies depending on type

The LDAP search filter rules must be used with the LDAP names of attributes, and certain character values must be 'escaped' if used in an LDAP filter. These are:

Character Escaped As
* \2a
( \28
) \29
\ \5c
NUL \00

Property values for comparison do not normally need to be wrapped in quotes.

LDAP Filter Examples

Building LDAP filters can be challenging. Here are some examples using active directory group filters you can use as a base to begin creating your own.

  • All groups with a name (cn) of 'Professional Services Department'

    'cn -eq "Professional Services Department"' or `'(cn=Professional Services Department)'
  • All groups with a name of 'Professional Services Department' and a description of 'Live'

    '(cn -eq "Professional Services Department") -and (description -eq "Live")' or '(&(cn=Professional Services Department)(description=Live))'
  • All groups with a name of either 'Professional Services Department' or 'All Departments Share Access'

    '(cn -eq "Professional Services Department") -or (cn -eq "All Departments Share Access")' or '(|(cn=Professional Services Department)(cn=All Departments Share Access))'
  • All groups not having a description of 'Live'. Includes those with no description field at all

    '(!(description=Live))'
  • All groups not having a description of 'Live'. Excludes those with no description field at all

    'description -ne "Live"'
  • All groups with a description of 'Live' but not with a name of 'Professional Services Department'

    '(description -eq "Live") -and (cn -ne "Professional Services Department")' or '(&(description=Live)(!(cn=Professional Services Department)))'
  • All groups whose description is '\\fileserver1\fileshare'

    'description -eq "\5c\5cfileserver1\5cfileshare"' or '(description=\5c\5cfileserver1\5cfileshare)'

Using RecursiveMatch or Chain Matching

Using a matching rule OID, or the RecursiveMatch parameter is a powerful way of solving a question often asked about querying AD: 'How can I tell all of the groups a user is a member of, both directly and indirectly?' You use the Active Directory Search Filter memberOf property to find out.

Using a simple LDAP matching rule can be far more efficient than a large script. Using our example domain domain.local, Kristin Diaz is a direct member of the Professional Services Department security group. Looking at her memberOf property in AD reflects this; only Professional Services Department is shown.

active directory search filter memberof

PS51> Get-ADUser -Identity Kristin.Diaz -Property memberOf

DistinguishedName : CN=Diaz Kristin,OU=Professional Services,OU=All User Accounts,DC=domain,DC=local
Enabled           : True
GivenName         : Kristin
MemberOf          : {CN=Professional Services Department,OU=All Groups,DC=domain,DC=local}
Name              : Diaz Kristin
ObjectClass       : user
ObjectGUID        : 04fe6336-c541-4e71-b7ed-6fee7db23482
SamAccountName    : Kristin.Diaz
SID               : S-1-5-21-447422785-3715515833-3878445295-1186
Surname           : Diaz
UserPrincipalName :

By using the matching rule OID, or RecursiveMatch parameter you will find that they are indirectly a member of All Departments Share Access. This is because the Professional Services Department group is a member of All Departments Share Access.

PS51> Get-ADGroup -LDAPFilter '(member:1.2.840.113556.1.4.1941:=CN=Diaz Kristin,OU=Professional Services,OU=All User Accounts,DC=domain,DC=local)'
LDAP filter
PS51> Get-ADGroup -Filter 'member -RecursiveMatch "CN=Diaz Kristin,OU=Professional Services,OU=All User Accounts,DC=domain,DC=local"'
PowerShell filter

Both return the following:

DistinguishedName : CN=All Departments Share Access,OU=All Groups,DC=domain,DC=local
GroupCategory     : Security
GroupScope        : Universal
Name              : All Departments Share Access
ObjectClass       : group
ObjectGUID        : 8ac0e0b7-9225-40a4-b168-a0330960e182
SamAccountName    : All Departments Share Access
SID               : S-1-5-21-447422785-3715515833-3878445295-1254

DistinguishedName : CN=Professional Services Department,OU=All Groups,DC=domain,DC=local
GroupCategory     : Security
GroupScope        : Universal
Name              : Professional Services Department
ObjectClass       : group
ObjectGUID        : a8432583-7cac-4e8e-8d94-51e1c5bb1989
SamAccountName    : Professional Services Department
SID               : S-1-5-21-447422785-3715515833-3878445295-1255

The SearchBase and SearchScope Parameters

In large environments, AD can contain many thousands of objects. A way of improving performance and reducing the number of objects returned for any query is to scope the search.

The SearchBase parameter determines where in the AD hierarchy any search begins. When using the cmdlets this is a string representation of a distinguished name (and by default is the 'top' of the domain). There are also three levels of SearchScope:

  1. Base - The object that has been specified as the SearchBase.
  2. OneLevel - searches for objects immediately contained by the SearchBase but not in any sub containers.
  3. SubTree - searches for objects contained by the SearchBase and in any subcontainers, recursively down through the AD hierarchy.
Carisbrookelabs.local OU Structure
Example OU Structure

In the above example, with the SearchBase set to OU=All User Accounts,DC=domain,DC=local, a SearchScope of Base would try to query the OU object itself, a SearchScope of OneLevel would search the All User Accounts OU only, and a SearchScope of SubTree would search both the All User Accounts and Professional Services OUs.

Summary

You should now have a good understanding of how to filter with the Active Directory PowerShell cmdlets. You can see there's a lot to crafting that perfect filter syntax. It's a lot harder building the appropriate filter than using the Where-Object cmdlet.

But spend the time learning the 'right' way to filter AD objects and reap the rewards of great performance and efficiency!

Further Reading