2

I have the following simple code and it isn't working (simplified from a much larger function)

  • The user in my first example doesn't exist in my use case
  • The switch statement doesn't work
  • A break-point (using ISE) on both the statements in the first switch never get triggered
  • The second example works without issue
  • The third code snippet is some troubleshooting code to prove $myADObject is null

What am I missing?

Snippet 1:

$user = "no.one"
$myADUsr = Get-ADObject -Filter { sAMAccountName -like $user }

switch ($myADUsr) {

    $null { 'User object variable is null' }
    default  { 'User object variable has a value' }

}

Snippet 2:

$myADUsr = $null

switch ($myADUsr) {

    $null { 'The variable is null' }
    default { 'The variable has a value' }

}

Snippet 3:

clear-host
$member = "no.one"
$adobject = Get-ADObject -Filter { sAMAccountName -like $member }


'=== Frist switch ==='
switch ($adobject) {
    {$null} { "tests as null"}
    {$null -eq $_ } { 'another null test' }
    {[string]::IsNullOrEmpty($_)} {'string null test'}
    {$_ -eq [string]::Empty} { 'another string null test'}
    {$null -ne $_ } { 'not null' }
    default { "I don't think this is working ..." }
}



'==== if ====='
If ($null -eq $adobject) { 'null' } else { 'not null' }



'==== second switch ==='
$nullvariable = $null
switch ($nullvariable) {
    $adobject { 'null object' }
    $null { "null"}
    default { "not null" }
}
Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
jimbo.19
  • 23
  • 3
  • So #1 outputs nothing, #2 outputs `'The variable is null'`, and #3 outputs...? What does `$myADUsr.GetType()` produce? – Lance U. Matthews Dec 19 '19 at 02:00
  • Yes - #1 & #2 are as you state. #3 skips the first switch statement and the others behave as expected. I should have included that getType details .. it outputs "You cannot call a method on a null-valued expression." – jimbo.19 Dec 19 '19 at 02:11
  • As an aside: It's best to [avoid the use of script blocks (`{ ... }`) as `-Filter` arguments](https://stackoverflow.com/a/44184818/45375). – mklement0 Dec 19 '19 at 02:19
  • I'm able to reproduce this with `Get-ChildItem`. `$o = Get-ChildItem -Filter 'Some filter that doesn''t match anything'; switch ($o) { $null { 'Null' } default { 'Not null' } }` doesn't output anything, yet `$o -eq $null` yields `True` and `$o.GetType()` throws the same "You cannot call a method on a null-valued expression." error. I can't explain it, though. – Lance U. Matthews Dec 19 '19 at 02:25
  • Yeah - I've done it with Get-Service and Get-ADGroup also with the same results – jimbo.19 Dec 19 '19 at 02:31
  • from what i can tell, if the $Var has never been used - and is thus nonexistent - then the switch test will be skipped. if you use the $Var before the call that leaves it $null/blank ... then the switch block will run. ///// this sounds like a bug ... [*grin*] – Lee_Dailey Dec 19 '19 at 02:33

2 Answers2

3
  • The switch statement implicitly operates on collections (enumerable data types), and evaluates its branches for each element of the enumeration.

  • A function or cmdlet call that yields no output technically outputs the [System.Management.Automation.Internal.AutomationNull]::Value singleton, which can be conceived of as an array-valued $null - that is, in enumeration contexts such as switch it behaves like an empty collection: there's nothing to enumerate.

Therefore, because $myADUsr in your example contains [System.Management.Automation.Internal.AutomationNull]::Value due to Get-AdUser not producing any output, the switch statement is effectively skipped.


If all you need to know is whether an AD user object was returned, use PowerShell's implicit to-Boolean conversion in an if statement, because in an expression context [System.Management.Automation.Internal.AutomationNull]::Value behaves like $null (and therefore evaluates to $false):

$myADUsr = Get-ADObject -Filter 'sAMAccountName -like $user'

if ($myAdUsr) {
  'User object variable has a value'
}
else {
  'User object variable is null' 
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • So is this a bug or, I'm guessing, by-design? I could see `switch` responding to commands that yield no output with "I can't answer that question, so I won't", but I can't quite get past the `default` case not executing at all; that feels like an `if ... else ...` where somehow neither branch gets executed. `$o = if ($o -eq [System.Management.Automation.Internal.AutomationNull]::Value) { $null } else { $o }` seems to work to get it to process the `switch` as expected, or does this whole venture get filed under "Just **don't do that** with `switch`. Use an `if` statement to test for `$null`."? – Lance U. Matthews Dec 19 '19 at 02:53
  • @BACON, I can't speak to the design intent; the behavior is certainly surprising and non-obvious. Only _if_ you know the distinction between `$null` and `[System.Management.Automation.Internal.AutomationNull]::Value` - which, if I were to guess, most people don't, and arguably _shouldn't have to_ - does the behavior make sense, but is still a treacherously subtle distinction. – mklement0 Dec 19 '19 at 03:05
  • 1
    It is being discussed by folks over on the PowerShell project on GitHub. See "AutomationNull Behaviour #9997" for an example. https://github.com/PowerShell/PowerShell/issues/9997 – jimbo.19 Dec 19 '19 at 03:16
  • 1
    @jimbo.19, that discussion is primarily about whether it should be easier to detect a `[System.Management.Automation.Internal.AutomationNull]::Value` value specifically (as opposed to lumping it in with `$null`), but I've added a simplified example there to highlight the `switch` behavior discussed here. – mklement0 Dec 19 '19 at 03:36
  • 1
    @BACON, I'm not actually testing for `$null` in my real code. My sample snippets came from trying to understand why in some circumstances the code in my `switch` wasn't being run, I could tell from the breakpoints I had that I never even entered the switch statement. So this not being obvious really had me stumped as most of my unit tests passed because the objects existed in AD. It was the object that didn't exist, that was causing me issues. My `default` action that was the "catch all" never ran and started me hunting for an answer. I'm glad @mklement0 was around. – jimbo.19 Dec 19 '19 at 03:36
0

It think updating my original snippet #1 like this gets me out of trouble, it seems to work so I can continue to use the switch statement I have already written. I'm still testing.

$user = "no.one"
$myADUsr = Get-ADObject -Filter "sAMAccountName -like '$user'"

if ( @($myADUsr).Count -eq 0 ) { $myADUsr = $null }

switch ($myADUsr) { 
    $null { 'User object variable is null' }
    default { 'User object variable has a value' }
}
jimbo.19
  • 23
  • 3