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