How do I allow the option to run a PowerShell script that leverages parameter sets to run without passing any parameters? Is this possible?
param(
[Parameter(Mandatory=$true,HelpMessage="GenSecureFile used to generate secure password file.", ParameterSetName = 'GenSecureFile')]
[switch]$GenSecureFile,
[Parameter(Mandatory=$true,HelpMessage="GenSettingsFile used to generate settings file.", ParameterSetName = 'GenSettingsFile')]
[switch]$GenSettingsFile
)
Attempted to use a default parameter but this does not let you run the script with no parameters.
For this to work properly the parameter being declared as DefaultParameterSetName shouldn't be flagged as Mandatory and possibly be set to $true which wouldn't make sense for a switch Parameter. switch Parameters are meant to be optional (should not be Mandatory) and should not have a default value.
For Example:
function Test-Parameter {
[CmdletBinding(DefaultParameterSetName = 'GenSecureFile')]
param(
[Parameter(ParameterSetName = 'GenSecureFile')]
[switch] $GenSecureFile = $true,
[Parameter(Mandatory, ParameterSetName = 'GenSettingsFile')]
[switch] $GenSettingsFile
)
end {
$PSCmdlet.ParameterSetName
}
}
Test-Parameter
What you should do instead is have only one parameter that uses a ValidateSet Attribute Declaration, by doing so there wouldn't be a need for Parameter Sets.
For Example:
function Test-Parameter {
[CmdletBinding()]
param(
[Parameter()]
[ValidateSet('SecureFile', 'SettingsFile')]
[string] $Gen = 'SecureFile'
)
end {
if($Gen -eq 'SecureFile') {
# do something here when `SecureFile`
return
}
# do something here when `SettingsFile`
}
}
Test-Parameter
Related
This question already has answers here:
Which parameter set has been used?
(2 answers)
Closed last month.
I have a function:
Function Get-CMAPICollection {
[CmdletBinding()]
param (
[parameter(ValueFromPipeline = $true, Mandatory = $True, ParameterSetName = "ByName")]
[string]$collectionName,
[parameter(ValueFromPipeline = $true, Mandatory = $True, ParameterSetName = "ById")]
[string]$collectionId
)
If ($collectionName) {
$URI = "https://$($CMAPI.cmServer)/AdminService/wmi/SMS_Collection?`$filter=Name eq '$collectionName'"
}
If ($collectionId) {
$URI = "https://$($CMAPI.cmServer)/AdminService/wmi/SMS_Collection?`$filter=CollectionId eq '$collectionId'"
}
(Send-CMAPIRequest -method GET -URI $uri).Value
}
Which all works fine.
I'm wondering if there is a better way to dynamically handle the parameters in the function than a bunch of 'If' statements.
My concern is that if I need to add a whole lot of parameter sets, the functions itself is going to have a whole bunch of 'If' statements cluttering up the place.
No matter how far I abstract it out, there is still an 'If' collecionName or 'If' collectionId.
Thanks!
As always, literally minutes after posting I have found the answer. Provided by this answer here:
Which parameter set has been used?
Using the variable:
$PSCmdlet.ParameterSetName
and then a switch statement will reduce the requirement for IF statements.
I suppose this is a duplicate, however I think the question and answer provide some good examples and information.
Fixed function:
Function Get-CMAPICollection {
[CmdletBinding()]
param (
[parameter(ValueFromPipeline = $true, Mandatory = $True, ParameterSetName = "ByName")]
[string]$collectionName,
[parameter(ValueFromPipeline = $true, Mandatory = $True, ParameterSetName = "ById")]
[string]$collectionId
)
switch ($PSCmdlet.ParameterSetName) {
byName { $URI = "https://$($CMAPI.cmServer)/AdminService/wmi/SMS_Collection?`$filter=Name eq '$collectionName'" }
ById { $URI = "https://$($CMAPI.cmServer)/AdminService/wmi/SMS_Collection?`$filter=CollectionId eq '$collectionId'" }
}
(Send-CMAPIRequest -method GET -URI $uri).Value
}
I currently have the following powershell function:
function d { doppler run -- $args }
However, I would like to run something like d -Config dev ... and have that translate to
doppler run --config dev -- ...
How can I accomplish this?
If you want to add an optional parameter to your function you would no longer be able to use $args the same way you're currently using it. By adding a new parameter to your non-advanced function the same would be always bound positionally (-Config would be always bound hence wouldn't be optional).
What you could do instead to replace its functionality is turn your function into an advanced one and have a parameter that takes ValueFromRemainingArguments.
I haven't tested it but I believe this should do the trick.
function d {
[CmdletBinding(PositionalBinding = $false)]
param(
[Parameter(Position = 0, ValueFromRemainingArguments)]
[string[]] $Arguments,
[Parameter()]
[string] $Config
)
end {
doppler #(
'run'
if($PSBoundParameters.ContainsKey('Config')) {
'--config', $Config
}
'--'
$Arguments
)
}
}
Then both options should be available, with and without -Config:
PS ..\> d some arguments here
PS ..\> d -Config dev some arguments here
I am writing a PowerShell module, the functions inside this module have some parameters which will be re-used across all functions. Rather than copy-pasting the function definition each time I add a new function, I would like to define them at the top like a script variable and then insert them into each function, giving me a single place to update if they need to be changed.
Looking at how dynamic parameters are defined it seems like I should be able to define an object of that type and then reference it in the function definitions, but I can't find anything online giving me the correct syntax to do this.
Using PowerShell version 7.2
$Script:ReUsedParameters = param(
[Parameter()]
[String]$Name,
[Parameter()]
[Int]$Id
)
Function New-Command {
Param ($ReUsedParameters)
Write-Output "Name: $Name, ID: $ID"
}
For the sake of answering, you can store the runtime parameters definitions in a script block and then call it & inside the function's dynamicparam block.
I do not think this is a good idea nor I recommend using this. All functions should have their own repeated param blocks if needed.
$reusedParameters = {
$paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
# Since both parameters don't have any arguments (Mandatory, Position, ValueFromPipeline, etc..)
# you can use this one for both, otherwise, each dynamic parameter should have their own
# Parameter Declaration
[Parameter[]] $paramAttribute = [Parameter]::new()
$paramDictionary['Name'] = [System.Management.Automation.RuntimeDefinedParameter]::new('Name', [string], $paramAttribute)
$paramDictionary['Id'] = [System.Management.Automation.RuntimeDefinedParameter]::new('Id', [int], $paramAttribute)
return $paramDictionary
}
Function New-Command {
[CmdletBinding()] # `CmdletBinding` is Mandataroy here
param() # if the `param` block is empty
dynamicparam {
& $reusedParameters
}
end {
# Caveat: you can reference these parameters via $PSBoundParameters
# $Name and $Id are not part of the `param` block
# hence that wouldn't work here
"Name: {0}, ID: {1}" -f $PSBoundParameters['Name'], $PSBoundParameters['ID']
}
}
New-Command -Name asd -Id 123
As a declarative approach, you may turn the common parameters into class properties and have a single function parameter of the class type.
class MyReUsedParameters {
[String] $Name
[Int] $Id = 23
}
Function New-Command {
Param (
[MyReUsedParameters] $ReUsedParameters,
$AnotherParam
)
Write-Output "Name: $($ReUsedParameters.Name), ID: $($ReUsedParameters.ID)"
}
# Pass the common parameters as a hashtable which gets converted to
# MyReUsedParameters automatically.
New-Command -ReUsedParameters #{ Name = 'foo'; Id = 42 } -AnotherParam bar
# Alternatively pass the common parameters as a (typed) variable.
# PowerShell is able to deduce the argument name from the type.
$commonArgs = [MyReUsedParameters] #{ Name = 'Foo'; Id = 42 }
New-Command $commonArgs -AnotherParam bar
When passing a hashtable or PSCustomObject that has matching properties, it will automatically be converted to the class type.
You may even validate class properties similar to regular parameters. Most parameter validation attributes can be specified for class properties as well.
class MyReUsedParameters {
[ValidateNotNullOrEmpty()] [String] $Name
[Int] $Id = 23
# Constructor - required to apply validation
MyReUsedParameters( [Hashtable] $ht ) {
$this.Name = $ht.Name
$this.Id = $ht.Id
}
}
Function New-Command {
Param (
[Parameter(Mandatory)]
[MyReUsedParameters] $ReUsedParameters
)
Write-Output "Name: $($ReUsedParameters.Name), ID: $($ReUsedParameters.ID)"
}
# Causes an error (as expected), because Name property is missing
New-Command -ReUsedParameters #{ Id = 42 }
Here is a distilled version of my function:
function Get-CommandBar{
[CmdletBinding()]
[Alias("GCom")]
Param(
[Parameter()]
[string]$Path = $pwd,
[Parameter()]
[ArgumentCompletions('Path', 'Copy')]
$ActiveTab = 'Path',
[Switch]$ActiveVideo,
[Switch]$AllTabs
)
begin{
}
process {
Switch ($PSBoundParameters.Keys){
'ActiveTab'{
If ($ActiveTab -eq "Path"){
"Path"
}elseif($ActiveTab -eq "Copy"){
"Copy"
}
}
'AllTabs'{
"All Tabs"
}
'ActiveVideo'{
"Active Video"
}
}
}
}
get-CommandBar -ActiveTab Path outputs:
Path
get-CommandBar -ActiveTab Copy outputs:
Copy
But trying just get-CommandBar -ActiveTab, instead of Path, it outputs an error:
Get-CommandBar: Missing an argument for parameter 'ActiveTab'. Specify a parameter of type 'System.Object' and try again.
What gives? Shouldnt $ActiveTab = 'Path' be sufficient? Information on ArgumentCompletions seems to be sparse out here.m
Any help would be wonderfull.
function CTC {
[CmdletBinding(DefaultParameterSetName = "LocalBox")]
param
(
[Alias("l")]
[Parameter(ParameterSetName = "LocalBox", Mandatory)]
[Switch]
$Local,
[Alias("b")]
[Parameter(ParameterSetName = "LocalBox")]
[Parameter(ParameterSetName = "CloudTest")]
[string]
$BranchName = "head",
[Parameter(ParameterSetName = "CloudTest")]
[string]
$Tenant = "bingadsucmaz_cu"
)
process {
}
}
I have a function named "CTC", it has two parameter sets. The first one is "LocalBox" and I set it as default parameter set, the second one is "CloudTest". When I choose to run "CTC" without any parameters, it remind me to input the value like :
cmdlet CTC at command pipeline position 1
Supply values for the following parameters:
Local:
But suppose the meaningful action is the cmd choose to run "CloudTest" parameter set, right ?