Filter vs. Where-Object

In this day and age, no one likes being told what to do. For instance, in the Microsoft Lync Server 2010 PowerShell documentation we invariably use the -Filter parameter or the -LdapFilter parameter any time we want to retrieve a targeted collection of user accounts. For example, if we want to return a collection of all the users who work in the Sales department we typically use a command that looks like this one:

Get-CsAdUser –LdapFilter "Department=Sales"

But wait a second: who is Microsoft to tell you that you have to use the –LdapFilter parameter? After all, this command, which pipes user data to the Where-Object cmdlet, also returns a collection of all the users who work in the Sales department, doesn’t it:

Get-CsAdUser | Where-Object {$_.Department –eq "Sales"}

As a matter of fact, it does. So then why can’t you do it your way? Why do you have to do it Microsoft’s way?

Listen, relax: no one is saying that you have to do it our way. (Well, no one other than us, that is.) Admittedly, there are times when you can filter user accounts by piping data to the Where-Object cmdlet. Despite that irrefutable fact, however, there are two reasons why you should use –Filter or –LdapFilter when filtering user accounts, rather than taking the alternate route of piping data to Where-Object:

  • –Filter and –LdapFilter will typically work faster than Where-Object.
  • –Filter and –LdapFilter will typically work. That isn’t always the case with Where-Object.

 

Sound reasonable? If not, maybe we should explain.

Let’s kick things off by discussing the speed (and the efficiency) of –Filter and -LdapFilter. Here’s a rhetorical question for you: what happens when you run this command:

Get-CsAdUser –LdapFilter "Department=Sales"

In a nutshell, here’s what happens: Get-CsAdUser sends that command to a domain controller and then – on the domain controller itself – Get-CsAdUser selects only those users that work in the Sales department. This subset of users is then transported back across the network to your computer. Let’s say your organization has 10,000 employees, and 338 of those people work in the Sales department. That means information about 338 people will be transferred across the network.

Now, what happens when you run this command:

Get-CsAdUser | Where-Object {$_.Department –eq "Sales"}

Well, once again Get-CsAdUser sends the command to a domain controller. This time, however, no processing of any kind is done on the domain controller itself. Instead, information about all 10,000 users is gathered together and transported en masse across the network to your computer; after that, all the filtering (that is, picking out the users who work in the Sales department) takes place on your local machine. That’s a lot of information flitting across the network, and a lot of wear-and-tear placed on the local computer. (Which, unlike a domain controller, might not be as well-equipped for handling that sort of load.)

Make sense? The –Filter and –LdapFilter parameters cause filtering to take place on the remote computer, thus reducing the amount of information that gets returned across the network to your computer. By contrast, Where-Object causes filtering to take place on your computer, which means that all the information about all your users must be returned to your machine.

Now, to be honest, that might not make much of a difference: if you have ample bandwidth and a relatively small number of users, well, who cares whether or not all the information about all your users gets ferried across the network? Point taken. But here’s something that you should care about: depending on the attribute you want to filter on, Where-Object just plain won’t work.

What do we mean by that? Well, try this: run the command Get-CsUser –ResultSize 1, a command that returns information for one of your users. Take a peek at the information that comes back and tell us the name of the department this user works for:

Identity : CN=Ken Myer,OU=Redmond,DC=litwareinc,DC=com
OriginatorSid :
VoicePolicy : RedmondVoicePolicy
ConferencingPolicy :
PresencePolicy :
DialPlan :
LocationPolicy :
ClientPolicy :
ClientVersionPolicy :
ArchivingPolicy :
PinPolicy : TestPinPolicy
ExternalAccessPolicy :
HostedVoiceMail :
HostedVoicemailPolicy :
HostingProvider : SRV:
RegistrarPool : pool0.litwareinc.com
TargetRegistrarPool :
CSEnabled : True
SipAddress : sip:kenmyer@litwareinc.com
LineURI :
LineServerURI :
EnterpriseVoiceEnabled : False
TenantId : 00000000-0000-0000-0000-000000000000
HomeServer : CN=Lc Services,CN=Microsoft,CN=co1:2,CN=Pools,CN=RTC Service,CN
                                    =Services,CN=Configuration,DC=litwareinc,DC=com
TargetHomeServer :
PrivateLine : TEL:+14255557777
IPPBXSoftPhoneRoutingEnabled : False
RemoteCallControlTelephonyEnabled : False
EnabledForRichPresence : True
AudioVideoDisabled : False
DisplayName : Ken Myer
SamAccountName : kenmyer
UserPrincipalName : kenmyer@litwareinc.com

What’s that? You say you can’t even find the department anywhere? Well, there’s a good reason for that: Get-CsUser doesn’t actually return the department. And because Get-CsUser doesn’t return the department that means that a command like this one will never, ever return any data:

Get-CsUser | Where-Object {$_.Department –eq "Sales"}

 

Note. For better or worse, you won’t get any sort of error message telling you that Department can’t be used as part of your Where-Object query. Instead, PowerShell will just quietly and do nothing.

Just like the authors of this article.

 

Ah, but don’t panic. After all, the following command, which uses –LdapFilter rather than Where-Object, will work:

Get-CsUser –LdapFilter "Department=Sales"

Granted, you won’t see the user’s department included in the output. But, in this case, that doesn’t matter: thanks to the filter, we already know that all the users who were returned are members of the Sales department.

The point is this: for the most part, –LdapFilter lets you filter on any Active Directory attribute. Now, to be honest, we haven’t tried this with all 200-plus Active Directory user account attributes. But we do know it works with the attributes – like department, title, and company – that you’re most likely to filter on.

 

Note. You can always use –LdapFilter with either Get-CsUser or with Get-CsAdUser. As a general rule, however, the –Filter parameter doesn’t work very well with Get-CsAdUser; that’s because you’re trying to filter on Lync Server-specific attributes that a user account that hasn’t been enabled for Lync Server won’t actually have.

 

And if that doesn’t convince you, maybe this will. Suppose you want a list of all the users who have been assigned the voice policy RedmondVoicePolicy. This command will work:

Get-CsUser –Filter {VoicePolicy –eq "RedmondVoicePolicy"}

This command won’t work:

Get-CsUser | Where-Object {$_.VoicePolicy –eq "RedmondVoicePolicy"}

Why doesn’t it work? Well, off the tops of our heads, we’re not really sure. But it doesn’t, which is all the more reason to stick with –Filter: when returning user accounts, Lync Server-specific properties usually won’t work with Where-Object. Period.

You know, now that we think about it, that would be good advice to give your son or daughter on their wedding day, wouldn’t it: “Son/daughter, always remember that, when returning user accounts, Lync Server-specific properties usually won’t work with Where-Object.” And don’t worry: someday they’ll thank you for that.