Active Directory Powershell – Advanced Filter

Do you find it difficult reading/writing LDAP filters? Do you wish you could write LDAP filter in a more natural way? Have you ever wished that Ldap filter parser message should point you to the exact error character in your filter string instead of displaying a catch-all LDAP_FILTER_ERROR (87) error message? Do you find it weird writing OIDs in your filter? If your answer to any of the above question was a “yes”, then continue reading..

Active Directory Module for Windows Powershell introduces a new powerful replacement for Ldap filter; we call it “The Advanced Filter”. The advanced filter is supported via the -Filter parameter in all Get-AD* cmdlets.

So, what is really “advanced” about this filter?

1. Easy to write – The new filter is easy to read and write. It uses infix notation which is a more natural form of expressing conditions (example: { GivenName –eq “John” –and SurName –eq “Smith” } ) instead of prefix notation (example: (&(givenName=john)(sn=smith)) ).

The new filter syntax is similar to Powershell Expression Language used in FilterScript parameter of Where-Object cmdlet. So Powershell users will find it easy to query AD, without knowing anything about Ldap Filters. Most of the operators supported are Powershell standard (ex: -eq, -like, -band etc.). We have also introduced some new operators like -recursivematch etc.

2. Better filter parsing error messages – Let us try passing an incorrect query string to the -Filter parameter.

PS D:\Users\Administrator> Get-ADUser -Filter { Name -isequal "Administrator" }
Get-ADUser : Error parsing query: ' Name -isequal "Administrator" ' Error Message: 'Operator Not supported: -isequal' at position: '7'.
At line:1 char:11
+ Get-ADUser <<<<  -Filter { Name -isequal "Administrator" }
+ CategoryInfo          : ParserError: (:) [Get-ADUser], ADFilterParsingException
+ FullyQualifiedErrorId : Error parsing query: ' Name -isequal "Administrator" ' Error Message: 'Operator Not supported: -isequal' at position: '7'., Microsoft.ActiveDirectory.Management.Commands.GetADUser

In this case the advanced filter parser error message correctly points you to that operator “-isequal” is not supported. Let us try another example where in we don’t supply a string value in quotes.

PS D:\Users\Administrator> Get-ADUser -Filter { Name -like Administrator }
Get-ADUser : Error parsing query: ' Name -like Administrator ' Error Message: 'syntax error' at position: '13'.
At line:1 char:11
+ Get-ADUser <<<<  -Filter { Name -like Administrator }
+ CategoryInfo          : ParserError: (:) [Get-ADUser], ADFilterParsingException
+ FullyQualifiedErrorId : Error parsing query: ' Name -like Administrator' Error Message: 'syntax error' at position: '13'., Microsoft.ActiveDirecto  ry.Management.Commands.GetADUser

In this case the advanced filter parser error message points you to the character position where the syntax is incorrect. The correct filter would be: -Filter { Name -like "Administrator" }

3. Access Powershell variables inside Filters! – Advanced Filter supports accessing Powershell variables inside the filter. You can also access a Powershell variable’s property inside the filter.

PS D:\> $JohnSmith = Get-ADUser JohnSmith
PS D:\> Get-ADUser -Filter { manager -eq $JohnSmith.DistinguishedName }  ## Gets all the user accounts whose manager is JohnSmith

Note: Advanced Filter does not support indexer (ex: $a[0]) and Function invocation (ex: $a.ToString()) inside filters.

4. Automatic Ldap encoding – Ldap encoding of values such as bytes etc. is automatically handled by Advanced filter converters. For example, if you want to query all the users based on their LogonHours then you don’t have to worry about encoding the Logon Hour byte value into Ldap string format. The advanced filter does that for you. You can simply supply the byte array value inside the Advanced Filter.

PS D:\> $u = Get-ADUser Administrator -Properties *
PS D:\> $byt  = $u.logonHours
PS D:\> $byt.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Byte[]                                   System.Array

Advanced Filter way:  

PS D:\> Get-ADUser -Filter { logonHours -eq $byt }

Ldap Filter way:   

## Somehow Ldap encode the byte[] into a string and supply that in the filter.PS D:\> Get-ADUser -LdapFilter "(logonHours=\00\0B\FF\FF\FF\1F\FB\FF\AF\FF\FF\FC\FF\0A\FF\FF\FF\FF\FF\FF\1B)"

5. Accepts values in rich format – Advanced Filter can accept values in the form of rich .NET objects and convenient string formats. It would do the necessary conversion to Ldap search format. For example:

PS D:\> Get-ADGroup -Filter {isCriticalSystemObject -eq $true } ## Gets all system critical Groups

PS D:\> $SixtyDaysAgo = (Get-Date).Subtract((New-TimeSpan -Days 60))
PS D:\> Get-ADUser -Filter { lastLogonTimeStamp -notlike "*" -or  lastLogonTimeStamp -le $ SixtyDaysAgo }  ## Gets all the users who have not logged in last 60 days.

PS D:\> Get-ADGroup -filter { groupType -band 0x80000000 } ## Gets all security groups by passing a integer value in Hex format.
                                              ## An equivalent query in LDAP would be: 
                                              ## (&(objectCategory=group)(groupType:1.2.840.113556.1.4.803:=2147483648)) 

 

Here is the list of supported.NET Types:

  • System.Guid, System.Security.Cryptography.X509Certificates.X509Certificate, System.Security.Principal.SecurityIdentifier, System.DirectoryServices.ActiveDirectorySecurity

- Values of this type are converted to byte[] and then encoded into Ldap search string.

  • System.DateTime
  • Values of this type are converted to appropriate Ldap date format - Large Integer/Interval, Generalized Time, UTC Coded Time.
  • System.String, System.Int32, System.Int64, System.UInt32, System.UInt64, System.Boolean, System.Byte, System.SByte, System.Int16, System.UInt16, System.Char, System.Single, System.Single, System.Double, System.Decimal
  • Values of this type are converted to string format.

Note: All Get-AD* cmdlets also support Ldap filter via the -LdapFilter parameter.

Cheers!
Swami

--
Swaminathan Pattabiraman [MSFT]
Developer – Active Directory Powershell Team

Comments

  • Anonymous
    April 03, 2009
    So this filters is transferred to the server (if i'm using cmdlets from workstation), and applied on server side, using server resources, and I'm receive only filtered objects to my workstation. Is this correct?
  • Anonymous
    April 03, 2009
    $SixtyDaysAgo = (Get-Date).Subtract((New-TimeSpan -Days 60))is the same as$SixtyDaysAgo = (Get-Date).AddDays(-60)which is easier to remember and shorter to type. :)
  • Anonymous
    April 03, 2009
    Do you find it difficult reading/writing LDAP filters? Do you wish you could write LDAP filter in a more
  • Anonymous
    April 04, 2009
    @XaegrYes the filters are applied on the server side and you would only receive the filtered objects.--Swami
  • Anonymous
    April 14, 2009
    In my previous post I discussed about the various features available in -Filter parameter aka “advanced
  • Anonymous
    May 19, 2009
    In my previous post about Advanced filter s I showed how to use Powershell variables to represent values
  • Anonymous
    January 17, 2014
    How does the syntax of the filter work if you're looking for an OU within a nested OU? For example, lets say I have 4 sites. Under the "sites" OU, I have each of those sites. Under each site name I have a "users" OU and other OUs that are similarly named. I messed with it listing the nested path as you would in an LDAP definition, but I could not get it to work.