Back to Basics: A Casting Quandary

I could hear the screams of frustration from miles away!

Not too long after my Skype for Business chimed in... it all became clear... a colleague has been struggling with some seemingly simple PowerShell.

PoshChap to the Rescue

We have a list of Active Directory computer accounts.

$hosts = Get-ADComputer -Filter * -Properties ServicePrincipalName

We need to enumerate a multi-value attribute, ServicePrinicpalName, and check for a specific string. Initially, we need to report on the computer objects where the string is present. Simple, no?

Here's what my colleague sent me...

$hosts | Where-Object {$_.serviceprincipalname -match "MSServer*"} | Format-Table DnsHostName

Which generates the below output containing the desired results.

Capture138

Now, the source of the screams was the fact that when they reversed the logic, i.e. used the -notmatch operator, the above list would be included in the output!

$hosts | Where-Object {$_.serviceprincipalname -notmatch "MSServer*"} | Format-Table DnsHostName

Capture139

On initial inspection, this makes little sense and could well be the source of many a scream. However, if you consider that the ServicePrincipalName attribute is multi-valued and that -notmatch will check each of the values in the multi-valued attribute, and that true is returned if just one of those entries isn't matched, then it starts to make more sense.

A Casting Fix

The answer? Cast the returned ServicePrinicpalName attribute as a string, i.e. turn it from a multi-valued array to single-valued string. Look at the [string] prefix below - this performs the cast.

This time -notmatch only searches for the pattern in a single string.

$hosts | ? {[string]$_.serviceprincipalname -notmatch "MSServer*"} | ft DnsHostName

Capture140

The desired result. The screams fall silent.