PowerShell: When Best Practices and Accurate Results Collide

I’m a big believer in trying to write my PowerShell code to what the industry considers to be the best practices as most are common sense anyway, although as one person once told me: “Common sense isn’t all that common anymore”.

I would hope that even the most diehard best practices person would realize that if you run into a scenario where following best practices causes the results to be skewed, that at least in that scenario it’s worth taking a step back so you can see the bigger picture. I recently encountered a couple of these scenarios which I’ll demonstrate in this blog article.

Scenario 1: You want to find all users in the NorthWind Active Directory OU (Organizational Unit) whose department property does not have a value equal to “Sales”.

accurate-results1

In the previous example, I’ve followed the best practices by filtering left and while everything looks correct, unfortunately there is some sort of filtering going on behind the scenes that the filter parameter is performing which skews the results.

This time, I’ll grab all of the users in the NorthWind OU and pipe them to the Where-Object cmdlet to perform the filtering:

accurate-results2

Notice that although you would think that both of these commands in the previous two examples would return the same results, the ones without a Department are somehow filtered out by the first command even though their department is not equal to “Sales”.

Scenario 2: You want to return a list of SQL Server users from the default instance on a server named SQL01 where the password expiration policy is not enabled:

accurate-results3

Those results look plausible, but unfortunately they’re inaccurate. As we can see in the following results, the PasswordExpirationEnabled property is a Boolean:

accurate-results4

Conventional wisdom would lead you to believe that the valid values for a Boolean are true or false but it’s that assumption that if something isn’t true, it must be false that leads to inaccurate results in this scenario.

By adding the PasswordExpirationEnabled property to our results, we can see where the logic problem occurs:

accurate-results5

Based on the previous results, you can see that the value can also be null. The problem can also be seen in the GUI since it’s not possible to set the PasswordExpirationEnabled option in SQL for a Windows user:

accurate-results7

To get accurate results, we’ll need to see if the value is false because not true could be false or null:

accurate-results6

As you can see, those results are accurate based on the value of the PasswordExpirationEnabled property.

µ

1 Comment

  1. Kirk Munro

    Hi Mike,

    Great article. A few comments on this:

    1. For the first scenario, that definitely looks like a bug. But you shouldn’t resort to client-side filtering with Where-Object as the workaround to that bug, unless you like pulling back large volumes of AD data unnecessarily. Get-ADUser also has a -LDAPFilter parameter, and that parameter seems to work as advertised (e.g. Get-ADUser -LDAPFilter ‘(!(department=Sales))’). You’ll have much better results going that route, and learning LDAP filters is worthwhile if you’re doing a lot of work with AD anyway.

    2. In the second scenario, you’re filtering on a boolean value. When working with booleans, many people tend to want to skip comparisons against $true or $false, however as you’ve shown here that comes with a few caveats. When comparing boolean values against $true, whether the boolean you’re comparing is nullable (can be null) or not, you don’t have to specify “-eq $true” in your conditional expression, because simply passing in the value itself is equivalent to saying “return $true if this is not null and if it is equivalent to $true”. For booleans the only equivalency to $true is when they are actually true, so “-eq $true” is not needed in those comparisons. With that in mind, some people think you can simply test for a value of false by using the -not unary operator, but as you pointed out that doesn’t work. Testing for $false by explicitly including “-eq $false” in your conditional expression is the only foolproof way to perform such a test. By performing tests this way, you’re script will work against nullable boolean values, regular boolean values, and boolean values that appear like they can’t contain null but actually can due to internal implementation details behind the data you’re looking at. With that in mind, as a best practice you should always use the -eq operator when comparing a boolean value to see if it is set to $false (and not $null).

    Kirk out.

    Reply

Leave a Reply

%d bloggers like this: