Is there some way to define a parameterless powershell parameter set? - powershell

I've got some fairly complex functions that I'm writing for a library module, with lots of different ways it can be called. However, it is in fact possible to default all of them, but when I try to call my function with no parameters the call fails because the parameter set cannot be determined.
I would like to define a parameter set that contains no parameters whatsoever, such that calls with no parameters succeed. This is difficult to do since ParameterSetName is a property of the Parameter attribute though, and it's not possible to attribute nothing.
I experimented with the DefaultParameterSet property of the CmdletBinding attribute that is placed on the param block, however nothing seemed to work. It seems that the parameter set name defined there must actually exist in order for Powershell to default to it.
Currently my best approximation of this use case is to define one of the parameter sets to have no Mandatory parameters, however these fail when empty strings or nulls are piped in, and I would like for this not to be the case.
Is this possible?

Sure is. Just specify a default parameter set name that isn't used otherwise:
function Foo {
[CmdletBinding(DefaultParameterSetName='x')]
Param(
[Parameter(Mandatory=$true, ParameterSetName='y')]$a,
[Parameter(Mandatory=$false, ParameterSetName='y')]$b,
[Parameter(Mandatory=$true, ParameterSetName='z')]$c,
[Parameter(Mandatory=$false)]$d
)
"`$a: $a"
"`$b: $b"
"`$c: $c"
"`$d: $d"
}

Related

Single parameter argument with position equal to 1 or no argument value?

I have stumbled upon a function with single parameter, and there are 2 things I do not understand simply because documentation does not mention them.
Here is a function:
function Some-Function
{
[CmdletBinding()]
param (
[Parameter(Mandatory,
Position = 1, ValueFromPipeline)]
[string] $Input
)
}
Question 1:
Since this is single parameter function, why is Position set to 1? what does that mean? I don't see any point here, what is wrong with the default value of Position = 0?
Question 2:
ValueFromPipeline and Mandatory arguments are not set to any value ie, = $true or = $false
what is the default value of these arguments if not set to value?
I can't find anywhere in the docs to explain this, and none of the examples on docs are like this one.
Since this is single parameter function, why is Position set to 1?
There is no strict need to set a Position property at all in this case.
While it is a good convention to start Position properties with 0, it isn't technically necessary - all that matters is the relative ordering of all parameters with Position attributes.
In the absence of any Position properties, it is the order of parameter declarations (other than [switch] parameters) that implicitly defines their positional ordering, EXCEPT if you use [CmdletBinding(PositionalBinding=$false)], in which case only explicit Position properties matter.
Note: The presence of [CmdletBinding(PositionalBinding=$false)] or at least one [Parameter(Position=...)] attribute implicitly switch the default to non-positional: that is, all parameters that do not explicitly specify a Position value become non-positional, meaning they can only be passed arguments if preceded by the parameter's name (e.g., -foo bar instead of just bar).
ValueFromPipeline and Mandatory arguments are not set to any value
In PowerShell v3+, omitting an attribute's property value defaults to $true, in the interest of brevity.

How does ValidateSet stop a variable being changed to a non-set value?

I've just noticed that if you are using ValidateSet for a parameter variable in a function you cannot within that function change the parameter variable to a value that is not in the set.
Here is a simple example to demonstrate:
Function Test {
[cmdletbinding()]
Param(
[ValidateSet(1,2,3)]
[int]$Number
)
$Number = 4
}
Test 3
Returns:
The variable cannot be validated because the value 4 is not a valid value for the Number variable.
I've used Get-Member to explore $Number and I can't see any indication of how (or why) it restricts the variable like this. I assume it's some sort of custom object or strong typing but the variable looks to be a System.Int32. Does anyone know how/why this happens?
This isn't unique to Int variables, this is just a simple example. I have found the same true for a String Array parameter.
I discovered from this related question: Find the values in ValidateSet that if I do the following inside my function:
(Get-Variable 'Number').Attributes.ValidValues
this lists the defined ValidateSet values. I therefore assume setting this attribute is how ValidateSet works, with the side effect being that it is then in effect throughout the life of the variable.

Multiple conditions on parameterset

In a function, I have 2 [String] parameters, $DBServer and $ServiceServer
Both should be allowed to be blank or filled, but nothing inbetween.
Is this possible to do with parametersets?
Or should I include a check in my PowerShell function which makes sure that my conditions are made?
Yes, you do this with Parameter Sets. So you'd have 2 sets. One in which both parameters are Mandatory, and one in which both parameters are absent.
function Ruin-Database {
[CmdletBinding(DefaultParameterSetName='Blank')]
param(
[Parameter(
ParameterSetName='Filled',
Mandatory=$true
)]
[ValidateNotNullOrEmpty()]
[String]
$DBServer ,
[Parameter(
ParameterSetName='Filled',
Mandatory=$true
)]
[ValidateNotNullOrEmpty()]
[String]
$ServiceServer
)
If ($PSCmdlet.ParameterSetName -eq 'Blank') {
Write-Verbose 'Blank' -Verbose
} else {
Write-Verbose 'Filled' -Verbose
}
}
Once you define the function this way, look at the help to see both parameter sets:
Get-Help Ruin-Database
Explanation
If a parameter specifies one or more parameter sets, then it will only be included in those sets. If it doesn't specify, it will be included in all sets.
PowerShell must be able to unambiguously resolve the set if you want it to work, so sometimes it takes some trial and error.
One thing you can do to help this is to specify a DefaultParameterSetName in the CmdletBinding attribute. But you can also specify a parameter set name there that you haven't used in any other parameter. That means the default set would only include parameters that don't specify any set (are available in all), even if there are none of those.
Running help on a function/cmdlet is a good way to see what sets PowerShell is interpreting, as it can get complex.
You can specify multiple [Parameter()] attributes on a single parameter to define different characteristics in different sets, for example, a parameter may be mandatory in one set, but optional in another. It may be specified via pipeline in one set, but not in another.

How to mark a switch parameter as mandatory in Powershell

I'm pretty sure I don't have any other options other than what I've uncovered, but I wanted to pick the collective internet brain.
I prefer to use a [switch] parameter when passing boolean values to custom functions. However, I have a few cases in which I wanted to mark the switch parameter as mandatory. This can optionally be accomplished through [parameter(Mandatory = $true)] on the parameter. However, I really dislike the UI prompt that shows up. I much prefer throwing an exception.
But, a switch can be either true or false, and the "IsPresent" property makes no distinction. If I pass a switch parameter as -example:$false, the switch reports that $example.IsPresent is false!
I have resorted to using a [bool]. For example:
param
(
[bool]$theValue = $(throw('You didn't specify the value!'))
);
Are there any other tricks I can pull?
In a way a switch parameter is always mandatory. If you don't specify it, it gets a value of false. If you specify it ( -var) it gets a value true and if you specify the value too (-var:$false) it gets the value specified.
I can't really think of a situation where it is mandatory to specify a switch. If you don't specify, it is false. Simple as that.
I think what you want is to specifically mention the value of the param to be true or false? If that is the case, the bool version that you mention is what I would go for, though it works the same way with switch too:
param([switch]$a = $(throw "You didn't specify the value"))
And also regarding $example.IsPresent - I know it is not intuitive /broken , but it is the same as the value of the switch variable itself. That is how the constructor for Switch Paramater is defined and the only property that it has is the IsPresent:
Creates a new SwitchParameter object that includes a Boolean value
that identifies whether the switch is present.
http://msdn.microsoft.com/en-us/library/system.management.automation.switchparameter.ctor%28v=vs.85%29.aspx

not using [parameter()] on some parameters in a PowerShell advanced function

If you are doing advanced functions and have the parameters decorated [parameter()], would there be any reason to NOT decorate a parameter with [Parameter()] . I've seen this a few times and don't know whether its just a case of laziness, oversight, or purposeful design.
Laziness I think but to be fair, it isn't needed if you aren't using any special parameter attibutes like Mandatory, Position, etc. As long is one parameter is marked [Parameter(...)] or the param block is marked [CmdletBinding(...)] PowerShell will interpret that function as an advanced function. OTOH there is no harm in adding the empty [Parameter()] to parameters.