This article describes an issue with the PowerShell "not equal to" operator, -ne, when used with the Active Directory module cmdlets. When used with the -Filter parameter you get incomplete results. The cmdlet does not return objects where the Active Directory attribute or PowerShell property has no value.


Introduction

The PowerShell "not equal to" operator in a filter should restrict the results to cases where the specified attribute or property does not have the specified value. Instead, it filters on objects where the attribute or property has a value, but it does not exactly match the one specified. It does not return objects where the attribute or property has no value. This is not the behavior most people would expect. It is also different from the results when you use the -LDAPFilter parameter and the "!" operator.

The issue was discussed in this forum thread: Get-ADComputer group mebership filtering

↑ Return to Top


PowerShell Properties and AD Attributes

You can use the PowerShell properties or the Active Directory attributes (or both) with the -Filter parameter. Both are supported by the Active Directory module cmdlets. The issue with the -ne operator is the same with either.

The PowerShell properties are similar to aliases. Many of the names are different from the corresponding attribute names in Active Directory. They can be thought of as property methods because they generally involve code. They often convert a corresponding Active Directory attribute into a "friendly" format. For example, the LastLogonDate PowerShell property converts the AD attribute lastLogonTimestamp from a Large Integer value into a datetime in the local time zone.

You can also filter with the actual Active Directory attribute names. You must specify the LDAPDisplayNames of the attributes. But you cannot use the "friendly" values supported by the PowerShell properties. If you filter on lastLogonTimestamp, for example, you must use a Large Integer value, similar to 130963329671524464.

The issue described here is the same whether you filter on the PowerShell property or the AD attribute. For example, you can filter on the PowerShell property Surname, or the equivalent AD attribute sn. If you use the -ne operator with the -Filter parameter, the results will be the same.

↑ Return to Top


Example

The following PowerShell script can be used to demonstrate the issue. It retrieves all users that have a value assigned to the City property, and the value is not exactly equal to "London". The comparison is not case sensitive.

(Get-ADUser -Filter {City -ne "London"}).Count

The results do not include any users where the City property is missing. A more useful example would include a clause to only consider members of a specified group. For example, you may want to find all members of the Sales group that are not in London.

(Get-ADUser -Filter {(memberOf -eq "cn=Sales,ou=West,dc=Domain,dc=com") -And (City -ne "London")}).Count

↑ Return to Top


LDAP Syntax Works as Expected

If you use the -LDAPFilter parameter to retrieve objects where a specified attribute does not exactly match a given value, the results are as expected. The results include all objects where the specified attribute does not have a value. For example, the following PowerShell script returns many more users than the first script above that uses the -Filter parameter (unless all users have a City). The corresponding AD attribute is l (for location).

(Get-ADUser -LDAPFilter "(!(l=London))").Count

↑ Return to Top


Workaround With PowerShell Syntax

As a workaround, you can include another clause in the -Filter parameter that filters on objects where the specified attribute or property has no value. You must use the -Or operator to combine this clause with the one using the -ne operator. Then objects are returned if either condition is met. For example, the following script returns the same results as the script using the -LDAPFilter parameter.

(Get-ADUser -Filter {(City -ne "London") -Or (City -NotLike "*")}).Count

You could also use the AD attribute l and get the same result.

↑ Return to Top


Possible Explanation of the Issue

Active Directory uses an Indexed and Sequential Access Method (ISAM) database. The interface for this ISAM database is provided by a Windows component called the Extensible Storage Engine (ESE). The ESE provides efficient storage by not reserving space in the database for attributes of any object that do not have values.

This explains another similar issue with the AD module cmdlets when you specify -Properties *. For example, consider the following script to retrieve values for a specified user.

Get-ADUser -Identity "jsmith" -Properties *

This will retrieve all default and extended properties of the user object exposed by the Get-ADUser cmdlet, whether they have values or not. In addition, it retrieves Active Directory attributes, but only those that have values.

The default and extended properties are PowerShell methods. The method code knows the corresponding AD attribute to query. The PowerShell methods display a null (blank) if the attribute has no value. But the cmdlet does not know about any AD attributes that do not have a value. The cmdlet is essentially blind to these attributes since no space is reserved for them. The cmdlet would need to query the AD Schema to determine which attributes apply to user objects.

This could also possibly explain why the -ne operator in a -Filter clause ignores objects where the AD attribute or the PowerShell property has no value. Except that PowerShell must convert the PowerShell filter into an LDAP filter. Active Directory only understands LDAP syntax filters. The only such filter that could provide the results we experience would be the one in this script.

(Get-ADUser -LDAPFilter "(&(!(l=London))(l=*))").Count

This implies that the -Filter parameter was coded to convert the -ne operator into an LDAP syntax filter like this on purpose.

↑ Return to Top


See Also

↑ Return to Top


Other Resources

↑ Return to Top