As mentioned in Rohn Edwards's answer, [AllowNull()] and/or [AllowEmptyString()] are part of the solution, but a few things need to be taken into account.
Even though the example given in the question is with a type string, the question on the title is how to pass null to a mandatory parameter, without a mention of type, so we need to expand the answer slightly.
First let us look at how PowerShell handles assigning $null to certain types:
PS C:\> [int] $null
0
PS C:\> [bool] $null
False
PS C:\> [wmi] $null
PS C:\>
PS C:\> [string] $null
PS C:\>
Analyzing the results:
- Passing
$null to an [int] returns an [int] object with value 0
- Passing
$null to a [bool] returns a [bool] object with value False
- Passing
$null to a [wmi] returns ... nothing. It does not create an object at all. This can be confirmed by doing ([wmi] $null).GetType(), which throws an error
- Passing
$null to a [string] returns a [string] object with value '' (empty string). This can be confirmed by doing ([string] $null).GetType() and ([string] $null).Length
So, if we have a function with a non-mandatory [int] parameter what value will it have if we don't pass that parameter? Let's check:
Function Foo {
Param (
[int] $Bar
)
Write-Host $Bar
}
Foo
> 0
Obviously if it was a [bool] the value with be False and if it was a [string] the value would be ''
So when we have a mandatory parameter of most standard types and we assign it $null, we are not getting $null, but rather the "default value" for that type.
Example:
Function Foo {
Param (
[Parameter(Mandatory = $true)][int] $Bar
)
Write-Host $Bar
}
Foo -Bar $null
> 0
Notice there is no [AllowNull()] at all there, yet it still returns a 0.
A [string] is slightly different, in the sense that it doesn't allow empty strings on a mandatory parameter, which is why the example in the question fails. [AllowNull()] doesn't fix it either, as an empty string is not the same as $null and so we need to use [AllowEmptyString()]. Anything else will fail.
So, where does [AllowNull()] come in play, and how to pass a "real" $null to an int, bool, wmi, etc?
Neither int nor bool are nullable types, so in order to pass a "real" $null to them you need to make them nullable:
Function Foo {
Param (
[Parameter(Mandatory = $true)][AllowNull()][System.Nullable[int]] $Bar
)
Write-Host $Bar
}
This allows a "true" $null to be passed to an int, obviously when calling Write-Host it converts the $null to a string meaning we end up with an '' again, so it will still output something, but it is a "true" $null being passed.
Nullable types like [wmi] (or a .NET class) are easier as they are already nullable from the start, so they don't need to be made nullable, but still require [AllowNull()].
As for making a [string] truly nullable, that one still eludes me, as trying to do:
[System.Nullable[string]]
Returns an error. Very likely because a system.string is nullable already, though PowerShell doesn't seem to see it that way.
EDIT
I just noticed that while
[bool] $null
gets coerced into False, doing...
Function Foo {
Param (
[bool] $Bar
)
$Bar
}
Foo -Bar $null
throws the following error:
Foo : Cannot process argument transformation on parameter 'Bar'. Cannot convert value "" to type "System.Boolean".
This is quite bizarre, even more so because using [switch] in the function above instead of [bool] works.