[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|...]
Related
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?
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.
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.
I'm trying to validate whether a path exists before running a function.
There is no default for the folder path, but the file name default should be template.csv. Is there a way, through the ValidateScript attribute, to validate a parameter value based on another parameter value?
The below code returns the error that the variable $TemplateDir has not been set. I'm also not entirely sure if it would test for the default file name value.
function get-Template {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[ValidateScript({Test-Path $_})]
[string]$TemplateDir,
[Parameter(Mandatory = $false)]
[ValidateScript({Test-Path ($TemplateDir + "\" + $_)})]
[string]$TemplateFile = "template.csv"
)
...
}
Any advice?
You can set up a dynamic parameter with the DynamicParam block, that depends on the value of another mandatory parameter:
function Get-FilePath
{
Param (
[Parameter(Mandatory = $true, Position = 0)]
[ValidateScript({Test-Path $_})]
[string]$TemplateDir
)
DynamicParam {
# Set up parameter attribute
$fileParamAttribute = New-Object System.Management.Automation.ParameterAttribute
$fileParamAttribute.Position = 3
$fileParamAttribute.Mandatory = $false
$fileParamAttribute.HelpMessage = "Please supply a file name"
# Set up ValidateSet param with actual file name values
$fileValidateParam = New-Object System.Management.Automation.ValidateSetAttribute #(Get-ChildItem $TemplateDir -File |Select-Object -ExpandProperty Name)
# Add the parameter attributes to an attribute collection
$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($fileParamAttribute)
$attributeCollection.Add($fileValidateParam)
# Create the actual $TemplateFile parameter
$fileParam = New-Object System.Management.Automation.RuntimeDefinedParameter('TemplateFile', [string], $attributeCollection)
# Push the parameter(s) into a parameter dictionary
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('TemplateFile', $fileParam)
# Return the dictionary
return $paramDictionary
}
begin{
# Check if a value was supplied, otherwise set it
if(-not $PSBoundParameters.ContainsKey('TemplateFile'))
{
$TemplateFile = 'template.csv'
}
$myPath = Join-Path $TemplateDir -ChildPath $TemplateFile
}
end {
return $myPath
}
}
This will also give you automatic tab completion for the arguments to -TemplateFile
You can read more about DynamicParam with Get-Help about_Functions_Advanced_Parameters
I'm trying to get my powershell script to prompt for input, but still use a dynamicparam. If I pass the parameters through the command line (Example: DeployBuild "DEV02" "ClientPortal" "a" "b") , then the below code works fine, and I get prompted for the dynamic param. If I chose not to pass the parameters via command line (Example: DeployBuild), and instead let the script prompt for input, the dynamic parameter quits working. Does anyone have an idea why this is not working?
Function DeployBuild {
[CmdletBinding()]
Param
(
[ValidateSet("DEV01","DEV02","PEDEV01","QA01","QA02","UAT","PERF01","PROD")]
[Parameter(Mandatory=$true, Position=1)]
[String]$environment,
[Parameter(Mandatory=$true, Position=2)]
[ValidateSet("AuthenticationService","ClientPortal")]
[String]$application,
[Parameter(Mandatory=$true, Position=3)]
[String]$buildName,
[Parameter(Mandatory=$true, Position=4)]
[String]$buildNumber
)
DynamicParam{
if ($environment -eq "DEV02"){
#create a new ParameterAttribute Object
$buildVersionAttribute = New-Object System.Management.Automation.ParameterAttribute
$buildVersionAttribute.Position = 5
$buildVersionAttribute.Mandatory = $true
#create an attributecollection object for the attribute just created.
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attribute
$attributeCollection.Add($buildVersionAttribute)
#add our paramater specifying the attribute collection
$buildVersionParam = New-Object System.Management.Automation.RuntimeDefinedParameter('buildVersion', [double], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('buildVersion', $buildVersionParam)
return $paramDictionary
}
}
End{
Write-Host $environment
Write-Host $application
Write-Host $buildName
Write-Host $buildNumber
Write-Host $PSBoundParameters.buildVersion
}
}
Well this is really old so you probably don't need it anymore, but I'm pretty sure the reason this is happening is because of the conditional in the DynamicParam block that looks to see what the value of $environment is.
When the DynamicParam block is evaluated, $environment is blank, so the parameter is never added.
You can demonstrate this for yourself like this:
DynamicParam {
Write-Verbose "DynamicParam here, environment is: '$environment'" -Verbose
# ... rest of code here
}
Now run DeployBuild with no parameters and you'll see exactly when the block is being evaluated.