powershellpowershell-5.1

Unexpected return value when using function in an if statement


$PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      22621  4391    

Consider the following snippet

# Function to determine if the OS is Windows Server
function Is-WindowsServer {
    $osVersion = (Get-WmiObject -Class Win32_OperatingSystem).Caption
    return $osVersion -like "*Server*"
}

# Function to check if SMB/Network Sharing is installed
function Is-SMBInstalled {
    if (Is-WindowsServer) {
        Write-Output "Detected Windows Server. This should print."
        $smbFeature = Get-WindowsFeature -Name FS-SMB1 -ErrorAction SilentlyContinue
        if ($smbFeature -and $smbFeature.Installed) {
            return $true
        } else {
            return $false
        }
    } else {
        Write-Output "Detected Windows 10/11 client. This should print."
        $smbFeature = Get-WindowsOptionalFeature -Online -FeatureName "SMB1Protocol" -ErrorAction SilentlyContinue
        if ($smbFeature -and $smbFeature.State -eq "Enabled") {
            return $true
        } else {
            return $false
        }
    }
    Write-Output "This should be unreachable."
}

Is-SMBInstalled # probe

if (Is-SMBInstalled -eq $true) {
    Write-Output "If above line is False. This should not print."
}

Expected output on Windows 10/11 with Samba disabled:

Detected Windows 10/11 client. This should print.
False

Script output on Windows 10/11 with Samba disabled:

Detected Windows 10/11 client.
False
If above line is False. This should not print.

Moreover, by itself,

if (Is-SMBInstalled -eq $true) {
    Write-Output "If above line is False. This should not print."
}

doesn't seem to properly calls Is-SMBInstalled. Without Is-SMBInstalled # probe, the output is just If above line is False. This should not print.

I've tried refactoring by putting Is-SMBInstalled inside a $variable, this works in a sense that the function is properly called. But the conditional still behaves unexpectedly.

I also tried the grouping function as per https://stackoverflow.com/a/66585435/14097947 if ((Is-SMBInstalled)) {, this also doesn't work.


Solution

  • By using Write-Output the result of Is-SMBInstalled is always a mixed-content array because Write-Output push its contents to the Success Stream(aka stdOut) the same as Return.

    And thus is always true when compared left-side.

    Replace the Write-Output with Write-Host, which does not write on the Success Stream.

    And since you are already there:

    1. The Wim cmdlets has been obsolete since Powershell 3, and have been removed from powershell 6+: replace Get-WimObject with Get-CimInstance.
      Doing so will make it forward-compatible.
    2. I suggest using Approved Verbs.

    have a example

    # Function to determine if the OS is Windows Server
    function Test-IsWindowsServer {
        # Left-side Filtering for the win.   
        # It returns $False if it finds nothing, and $True if it finds anything.   
        # NOTE WELL: I don't have Windows Server available to test now, so check if
        # the filter is working correctly.   
        [bool](Get-CimInstance -ClassName Win32_OperatingSystem -Filter 'Caption Like "%server%"')
    }
    
    # Function to check if SMB/Network Sharing is installed
    function Test-IsSMBInstalled {
            
        if (Test-IsWindowsServer) {
            Write-Host 'Detected Windows Server. This should print.'
            $smbFeature = Get-WindowsFeature -Name FS-SMB1 -ErrorAction SilentlyContinue
    
            # no reason for a IF, this will return either $true or $False directly.   
            return ($smbFeature.Installed) 
        }
        # Strictly speaking you don't need the Else because it's not reaching here
        # if Test-IsWindowsServer is $true, evaluate yourself if it's clearer with
        # or without.   
        else {
            Write-Host 'Detected Windows 10/11 client. This should print.'
            $smbFeature = Get-WindowsOptionalFeature -Online -FeatureName 'SMB1Protocol' -ErrorAction SilentlyContinue 
            
            # no reason for a IF, this will return either $true or $False directly.   
            # Evaluate using "-like" instead of "-eq".  
            return ($smbFeature.State -eq 'Enabled') 
        }
    
        # A Write-Warning is the perfect fit for this.   
        Write-Warning 'This should be unreachable.'
    }
    
    # This will print the 'Detected' version line and then the boolean output.   
    Test-IsSMBInstalled
    
    # note this will print again the 'Detected' version line.   
    if (Test-IsSMBInstalled) {  
        Write-Host 'If above line is False. This should not print.'
    }