One of the most common hangups when querying Active Directory with PowerShell is how to properly build filter syntax. The Filter
and LDAP Filter 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:
- you have the PowerShell ActiveDirectory module installed
- you’re on a domain-joined computer
- you can successfully connect and authenticate to an AD domain controller
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 filter 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. |
What is Active Directory LDAP?
LDAP, or Lightweight Directory Access Protocol, is a vendor-neutral protocol for accessing and modifying directory data. You may think of a phonebook when hearing the word directory, but in the context of Active Directory, this means so much more. Many different object types are stored and made accessible by Active Directory with the LDAP protocol functioning as a means to access that data.
As Active Directory can store many different data types, applications and users need a way to query that directory easily. Read on to learn how LDAP Filters assist in filtering that data!
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 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 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.
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)'
PS51> Get-ADGroup -Filter 'member -RecursiveMatch "CN=Diaz Kristin,OU=Professional Services,OU=All User Accounts,DC=domain,DC=local"'
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
:
- Base – The object that has been specified as the SearchBase.
- OneLevel – searches for objects immediately contained by the SearchBase but not in any sub containers.
- SubTree – searches for objects contained by the SearchBase and in any subcontainers, recursively down through the AD hierarchy.
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!