Create switch parameter using DynamicParam - powershell

Is it possible to create a [switch]-like parameter using DynamicParam? I know that I can just create a Boolean parameter, but in this case I will be forced to initialize its value like -BooParam $true, but I want to just type -BooParam. Why I need it - I would like to expose one switch parameter using Tab only if second is defined.

Yes, it is possible using DynamicParam:
function Test-Function
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$false)]
[switch]$flag1
)
DynamicParam
{
if ($flag1)
{
$flag2 = New-Object System.Management.Automation.ParameterAttribute
$flag2.Mandatory = $false
$flag2.HelpMessage = "Only available if flag1 is set"
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($flag2)
$flag2param = New-Object System.Management.Automation.RuntimeDefinedParameter('flag2', [switch], $attributeCollection)
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('flag2', $flag2param)
return $paramDictionary
}
}
}

Parameter sets might be a better (simpler) way to achieve your goal.
function Do-Something {
[CmdletBinding(DefaultParameterSetName='none')]
Param(
...
[Parameter(ParameterSetName='set1', Mandatory=$true)]$Foo,
[Parameter(ParameterSetName='set1')][Switch]$Bar
)
...
}

Related

Dynamic Parameter won't appear when a static parameter uses SecureString

Other scripts with dynamic parameters have never given me any issues. The one thing that is different is this is the first time I am using a SecureString type on a static parameter.
If I call this paramenter, the Dynamic one will not appear. I'm not doing anything special to hide the dynamic parameter. It should always appear. The issue appears on both 5.1 and PowerShell core.
I would be interested to know if there is a solution to this problem.
The dynamic parameter looks at available system services and allows you to enter only those services as part of the parameter.
This is only an example to replicate what I'm experiencing. Calling the Name parameter and supplying the information has no effect on the Service Dynamic Parameter. As soon as Password Parameter is called, you cannot call the Service Parameter if you hadn't already.
function test-command {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
$Name,
[Parameter(Mandatory)]
[SecureString]
$Password
)
DynamicParam {
$ParameterName = 'Service'
$RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $true
$ParameterAttribute.HelpMessage = "Parameter Help."
$AttributeCollection.Add($ParameterAttribute)
$arrSet = (Get-Service).Name
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
$AttributeCollection.Add($ValidateSetAttribute)
$AttributeAlias = New-Object System.Management.Automation.AliasAttribute('s', 'Serv')
$AttributeCollection.Add($AttributeAlias)
$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [array], $AttributeCollection)
$RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
return $RuntimeParameterDictionary
}
begin {
$Service = $PsBoundParameters[$ParameterName]
}
process {
$Service
}
end {
}
}
Yes, I could call Service Parameter first and then the remaining ones, but if there is an answer as to why it's behaving this way I'd certainly like to know. Plus, if someone else uses my script, they wouldn't automatically know to do this. Is this a bug or am I doing something wrong?

Does a DynamicParameter Switch not read like a Static Parameter [switch]?

Hopefully the Title is clear enough but, I am having some trouble understanding on how to evaluate against a DynamicParameter Switch, compared to a Static (type casted) switch.
In the following code block, there are 2 switches that become available only if the other 2 parameters are not null, and/or, are not empty:
Add
Remove
Function Test-DynamParam {
Param (
# Input Parameters
[Parameter(Mandatory = $false,
HelpMessage='Enter. Workflow. Name.')]
[Alias('OMB','MailBox')]
[string]$Workflow,
[Parameter(Mandatory = $false)]
[Alias('EDIPI','DisplayName')]
[string[]]$UserName
)
DynamicParam {
if ($Workflow -ne $null -and $UserName -ne $null) {
$parameterAttribute = [System.Management.Automation.ParameterAttribute]#{
ParameterSetName = "AddingMembers"
Mandatory = $false
}
$attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$attributeCollection.Add($parameterAttribute)
$dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new(
'Add', [switch], $attributeCollection
)
$paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
$paramDictionary.Add('Add', $dynParam1)
$parameterAttribute1 = [System.Management.Automation.ParameterAttribute]#{
ParameterSetName = "RemovingMembers"
Mandatory = $false
}
$attributeCollection1 = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$attributeCollection1.Add($parameterAttribute1)
$dynParam11 = [System.Management.Automation.RuntimeDefinedParameter]::new(
'Remove', [switch], $attributeCollection1
)
$paramDictionary.Add('Remove', $dynParam11)
return $paramDictionary
}
}
Process {
$Add.IsPresent
}
}
Running:
Test-DynamParam -Workflow 'd' -UserName 'a' -Add
returns empty.
Unfortunately, $Add.IsPresent is not evaluated to any boolean value regardless if the switch is present or not. Yet in this function it is (which makes sense):
Function Test-StaticParam {
Param (
[switch]$Add
)
$Add.IsPresent
}
Running:
Test-StaticParam -Add
returns True.
Question
How can I evaluate against dynamic parameter chosen?
Use the $PSBoundParameters automatic variable:
Process {
$PSBoundParameters['Add'].IsPresent
}

Dynamic parameters in Powershell based on value of other parameter

[CmdletBinding()]
param (
[Parameter(Mandatory=$true,Position=0)]
[string]
$subscription,
[Parameter(Mandatory=$false)]
[string]
$applicationId,
[Parameter(Mandatory=$false)]
[string]
$clientSecret,
[Parameter(Mandatory=$true,Position=1)]
[string]
$resourceGroupName,
[Parameter(Mandatory=$true,Position=2)]
[string]
$apimServiceName,
[Parameter(Mandatory=$true,Position=3)]
[string]
$policyfilePath,
[Parameter(Mandatory=$true,Position=4)]
[ValidateSet('global','product','api','operation', IgnoreCase = $true)]
[string]
$scope='global',
[Parameter(Mandatory=$true,Position=5)]
[string]
$apiIdOrProductId
#THIS IS THE PARAMETER WHICH I WANT TO MAKE MANDATORY IF VALUE OF PREVIOUS PARAMETER $scope = "OPEARTION"
#[Parameter(Mandatory=$false)]
#[string]
#$operationname
)
DynamicParam {
if ($scope -eq "OPERATION" -or $scope -eq "operation") {
#create a new ParameterAttribute Object
Write-Host "Called test"
Write-Host $scope
$operationAttribute = New-Object System.Management.Automation.ParameterAttribute
$operationAttribute.Mandatory = $true
$operationAttribute.Position = 6
#create an attributecollection object for the attribute we just created.
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attribute
$attributeCollection.Add($operationAttribute)
#add our paramater specifying the attribute collection
$operationName = New-Object System.Management.Automation.RuntimeDefinedParameter('operationname', [String], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('operationname', $operationName)
return $paramDictionary
}
}
I cannot get the dynamic parameter to work which is dependent on a previous parameter "scope". It just skips over when I run the PS script. Please advice what I'm doing wrong?
Your code only works if you pass an argument to your -scope parameter directly on the command line.
If you let PowerShell prompt you for a -scope argument, based on the parameter's Mandatory property, your code cannot work, because the dynamicparam block runs before such automatic prompts.
Since you're assigning a default value to $scope, 'global' (which doesn't make sense in combination with Mandatory=$true), one option is to simply remove the Mandatory property:
This will default to 'global' in the absence of a (possibly positional) -scope argument, in which case you know that the additional parameter isn't required.
If an argument is specified, then your dynamicparam block will work as intended.
However, this also requires you to place the mandatory $apiIdOrProductId parameter declaration before $scope (swap Position values 4 and 5)
Based on the title, there is at least one way to have a dynamic param offer a ValidateSet for that dynamic parameter based on the value of a previously entered regular parameter.
In this case the regular parameter could be a folder.
But you would have to enter it without quotes.
For instance:
Function Get-SubFolderDynamically {
[CmdletBinding()]
param($path)
dynamicparam
{
Write-Verbose "DynamicParam triggered. PSBoundParameters `$path is currently: $($PSBoundParameters['path'])" -Verbose
#Parameter Definition
$attributes = New-Object -Type System.Management.Automation.ParameterAttribute
$attributes.Mandatory = $true
$attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
#AcceptedValues for Parameter Definition
#Note: Only Works if path is entered in the command line without quotes
$subFolders = (Get-ChildItem $path).Name
$attributeCollection.add((New-Object System.Management.Automation.ValidateSetAttribute($subFolders)))
#ParameterDictionary
$dynParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("subFolder", [string], $attributeCollection)
$paramDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add("subFolder", $dynParam)
return $paramDictionary
}
}
#RUNIT - Create a Folder with subFolders to test.
Get-SubFolderDynamically -path C:\PathWithoutQuotes -subFolder [TAB|TAB|...]

How to implement `DynamicParam` through dynamic parameters?

Assumption:
$a = 1,2,3
if $a =1,2
Intent:Display parameter b
PS >test -<tab>
a
PS >test -a 1 -<tab>
b
PS >test -a 3 -<tab>
PS >test -a 3
How to achieve the following intent
Function test {
[CmdletBinding()]
Param()
DynamicParam {
parameter a
Dynamic parameter b (a is 1 =$true)
Dynamic parameter c (b is 3)
???
}
}
You don't have to assume. Research it.
What Dynamics Params are and how to use them if a fully documented use case via the MS docs site and many blog posts.
Searching for 'powershell dynamics parameters', will give you a solid list. Be sure to read the details, so as to limit any confusion about their use case.
Examples:
How can I pass dynamic parameters to powershell script and iterate
over the list?
Cmdlet dynamic parameters
PowerShell Deep Dive: Discovering dynamic parameters
Dynamic Parameters in PowerShell
How To Implement Dynamic Parameters in Your PowerShell Functions
enter link description here
# Example from the above link.
function Get-ConfigurationFile
{
[OutputType([System.IO.FileInfo])]
[CmdletBinding()]
param
()
DynamicParam
{
$ParamAttrib = New-Object System.Management.Automation.ParameterAttribute
$ParamAttrib.Mandatory = $true
$ParamAttrib.ParameterSetName = '__AllParameterSets'
$AttribColl = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$AttribColl.Add($ParamAttrib)
$configurationFileNames = Get-ChildItem -Path 'C:\ConfigurationFiles' | Select-Object -ExpandProperty Name
$AttribColl.Add((New-Object System.Management.Automation.ValidateSetAttribute($configurationFileNames)))
$RuntimeParam = New-Object System.Management.Automation.RuntimeDefinedParameter('FileName', [string], $AttribColl)
$RuntimeParamDic = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$RuntimeParamDic.Add('FileName', $RuntimeParam)
return $RuntimeParamDic
}
process
{
$configFileFolder = 'C:\ConfigurationFiles'
Get-ChildItem -Path $configFileFolder -Filter "$($PSBoundParameters.FileName).txt"
}
}
As for this...
As you can see,tabs cannot return a parameter
..., what you are showing is not the way this use case works.
Based on what you seem to be after, this sampler should get you there. please read the whole article for a better understanding.
Dynamic Parameters in PowerShell
Function Get-Order {
[CmdletBinding()]
Param(
[Parameter(
Mandatory=$true,
Position=1,
HelpMessage="How many cups would you like to purchase?"
)]
[int]$cups,
[Parameter(
Mandatory=$false,
Position=2,
HelpMessage="What would you like to purchase?"
)]
[ValidateSet("Lemonade","Water","Tea","Coffee")]
[string]$product="Lemonade"
)
Process {
$order = #()
for ($cup = 1; $cup -le $cups; $cup++) {
$order += "$($cup): A cup of $($product)"
}
$order
}
}
Or this one...
Using Dynamic Parameters
function Test-Department
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)]
[ValidateSet('Microsoft','Amazon','Google','Facebook')]
$Company
)
dynamicparam
{
# this hash table defines the departments available in each company
$data = #{
Microsoft = 'CEO', 'Marketing', 'Delivery'
Google = 'Marketing', 'Delivery'
Amazon = 'CEO', 'IT', 'Carpool'
Facebook = 'CEO', 'Facility', 'Carpool'
}
# check to see whether the user already chose a company
if ($Company)
{
# yes, so create a new dynamic parameter
$paramDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary
$attributeCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute]
# define the parameter attribute
$attribute = New-Object System.Management.Automation.ParameterAttribute
$attribute.Mandatory = $false
$attributeCollection.Add($attribute)
# create the appropriate ValidateSet attribute, listing the legal values for
# this dynamic parameter
$attribute = New-Object System.Management.Automation.ValidateSetAttribute($data.$Company)
$attributeCollection.Add($attribute)
# compose the dynamic -Department parameter
$Name = 'Department'
$dynParam = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter($Name,
[string], $attributeCollection)
$paramDictionary.Add($Name, $dynParam)
# return the collection of dynamic parameters
$paramDictionary
}
}
end
{
# take the dynamic parameters from $PSBoundParameters
$Department = $PSBoundParameters.Department
"Chosen department for $Company : $Department"
}
}
As well as this step by step one with full explanations...
How To: Add Dynamic Parameters to Your PowerShell Functions
As noted in my initial response, there are plenty of examples of this use case, but it requires study to fully grasp the concept, and how to use it/them.

Powershell function parameters proper use

PARAM (
[parameter(Mandatory=$true)]
[string]$Poolname,
[array]$Ports = 443,
[parameter(Mandatory=$true)]
[ValidateSet("ELB","ALB")]
$Loadbalncertype,
[parameter(Mandatory=$true)]
[ValidateSet("Ping","HTTPGet")]
$HealthCheckConfigType,
[parameter(Mandatory=$true)]
[array]$LBSubnets,
[parameter(Mandatory=$true)]
[string]$SecGroupID,
[int]$IdleTimeoutsec = 60,
[bool]$SSLPassthrough = $false,
string]$SSLCertificateName,
[string]$HealthCheckPath,
[string]$SSLPolicyName,
[bool]$ConfigureProxyProtocol = $true
)
In the above I would like to use Parameter $HealthCheckConfigType only if the $Loadbalncertype = ELB. I am not sure how to create this logic in Powershell function parameter section.
To do that in the param definition specifically, you could use DynamicParam to create a dynamic parameter, but that's a lot of work and probably overkill.
The most straightforward method I can think of if you must leave $LoadBalancerType as a [string] is to use [ValidateScript()] like so:
param(
[ValidateSet("ELB","ALB")]
$LoadBalancerType ,
[ValidateScript( { $LoadBalancerType -eq 'ELB' } )]
[ValidateSet("Ping","HTTPGet")]
$HealthCheckConfigType
)
This will give a crappy error message, which you could override with a well-placed throw:
[ValidateScript( { $LoadBalancerType -eq 'ELB' -or $(throw 'A better error message') } )]
Another option is to change your $LoadBalancerType parameter into separate switch parameters, and use them to define parameter sets:
[CmdletBinding(DefaultParameterSet='ALB')]
param(
[parameter(Mandatory=$true)]
[string]$Poolname,
[Parameter(Mandatory, ParameterSetName = 'ALB')]
[Switch]$ALB ,
[Parameter(Mandatory, ParameterSetName = 'ELB')]
[Switch]$ELB ,
[Parameter(Mandatory, ParameterSetName = 'ELB')
[ValidateSet("Ping","HTTPGet")]
$HealthCheckConfigType
)
This lets the parameter parser enforce this restriction, and you can see it in the automatically generated parameter sets by calling Get-Help on your function.
And, even though this isn't the usual way, in your case if you name the parameter sets with the names of the values you wanted, you could recreate $LoadBalancerType without conditionals:
$LoadBalancerType = $PSCmdlet.ParameterSetName
(assuming of course, that the only possible parameter sets are load balancer names directly; be careful with this)
But if you never really needed that string value; i.e. if you were only ever going to do:
if ($LoadBalancerType -eq 'ALB') {
} elseif ($LoadBalancerType -eq 'ELB') {
}
or something like that, then you don't need to recreate it, just do:
if ($ALB) {
} elseif ($ELB) {
}
Alternatively, you don't have to do this check in the param block at all; you can do it in your function body, or begin/process blocks where appropriate.
Use a DynamicParam block: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-6#dynamic-parameters
I don't have a ton of experience with them myself, but here is the code sample from that MS doc. You can process and add any parameters you need:
function Get-Sample {
[CmdletBinding()]
Param ([String]$Name, [String]$Path)
DynamicParam
{
if ($path -match ".HKLM.:")
{
$attributes = New-Object -Type `
System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Mandatory = $false
$attributeCollection = New-Object `
-Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
$dynParam1 = New-Object -Type `
System.Management.Automation.RuntimeDefinedParameter("dp1", [Int32],
$attributeCollection)
$paramDictionary = New-Object `
-Type System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add("dp1", $dynParam1)
return $paramDictionary
}
}
}
Another option would be to not muck with DynamicParam and in your code body, simply ignore $HealthCheckConfigType if $LoadBalancerType does not equal ELB, but if you want to use parameter validators to check this, DynamicParam is the answer.