jphughan
jphughan

Reputation: 59

PowerShell booleans -- How to handle null differently from false?

I'm trying to write a server build script that includes Boolean parameters for various tasks, e.g. whether to install IIS. If the user does not specify this parameter one way or the other, I want the script to prompt for a decision, but for convenience and unattended execution I want the user to be able to explicitly choose to install IIS or NOT install IIS by setting the value to True or False on the command line and therefore avoid being prompted. My issue is that when I create a Boolean parameter, PowerShell automatically sets it to False, rather than leaving it null, if it wasn't specified on the command line. Here is the design that I THOUGHT would've worked:

param(
    [bool]$IIS
)

if ($IIS -eq $null) {
    $InstallIIS = Read-Host "Do you want to install IIS? (Y/N)"
    if ($InstallIIS -eq "Y") {$IIS = $true}
}

if ($IIS) {Do stuff here}

Any suggestions for how to achieve my desired outcome would be most appreciated. Then if this changes anything, what I'd REALLY like to do is leverage PSRemoting to accept these build decision parameters on the user's system host and then pass them to the targets as an ArgumentList, and I'm wondering if that will affect how these Booleans are handled. For example:

param (
    [string[]]$Computers
    [bool]$IIS
)

$Computers | Foreach-Object {
    Invoke-Command -ComputerName $_ -ArgumentList $IIS -ScriptBlock {
        param(
            [bool]$IIS
        )

        if ($IIS -eq $null) {
            $InstallIIS = Read-Host "Do you want to install IIS? (Y/N)"
            if ($InstallIIS -eq "Y") {$IIS = $true}
        }
        if ($IIS) {Do stuff here}

Ideas?

Upvotes: 2

Views: 4349

Answers (4)

mjolinor
mjolinor

Reputation: 68243

Instead of using an equality test that's going to try to coerce the value to make the test work:

if ($IIS -eq $Null)

Use -is to check the type directly:

PS C:\> $iis = $null
PS C:\> $iis -is [bool]
False

Upvotes: 0

jphughan
jphughan

Reputation: 59

Well of course even though I Googled about this quite a bit before posting here, including discovering the [AllowNull()] parameter and finding that it did NOT help in my use case, I ended up finding the answer in the first Google search AFTER posting. This is what worked:

[nullable[bool]]$IIS

My only gripe with that syntax is that running Get-Help against the script now returns shows this for the IIS parameter:

-IIS <Nullable`1>

instead of:

-IIS <Boolean>

But unless there's a more elegant way to achieve what I need, I think I can live with that by adding a useful description for that parameter as well as Examples.

Upvotes: 3

briantist
briantist

Reputation: 47792

The way to accomplish this is with Parameter Sets:

[CmdletBinding()]
param (
    [Parameter()]
    [string[]]$Computers ,

    [Parameter(ParameterSetName = 'DoSomethingWithIIS', Mandatory = $true)]
    [bool]$IIS
)

$Computers | Foreach-Object {
    Invoke-Command -ArgumentList $IIS -ScriptBlock {
        param(
            [bool]$IIS
        )

        if ($PSCmdlet.ParameterSetName -ne 'DoSomethingWithIIS') {
            $InstallIIS = Read-Host "Do you want to install IIS? (Y/N)"
            if ($InstallIIS -eq "Y") {$IIS = $true}
        }
        if ($IIS) {Do stuff here}

Upvotes: 2

Maximilian Burszley
Maximilian Burszley

Reputation: 19644

Even though boolean operators handle $null, $False, '', "", and 0 the same, you can do an equality comparison to see which is which.

If ($Value -eq $Null) {}
ElseIf ($Value -eq $False) {}
..etc..

In your situation, you want to use [Switch]$IIS. This will be $False by default, or $True if entered with the command a la Command -IIS, then you can handle it in your code like:

If ($IIS) {}

Which will only be $True if entered at the command line with -IIS

Upvotes: 0

Related Questions