Parameter, if switch is present, don't permit other parameters - powershell

I have a function with parameters like this:
param(
[string[]]$ComputerName,
[string]$exepath,
[string[]]$exeargs,
[switch]$help
)
If the User who uses this function uses the -help switch, there must not be any other parameters declared to call the function. I want to prevent anyone using -help alongside other parameters.
How can I achieve that? I read about_Functions_Advanced_Parameters but it doesn't really help me. I first thought what I need is a ValidateSet but that's different from what I need.

You could use the ParameterSetName attribute:
param(
[Parameter(ParameterSetName='default')]
[string[]]$ComputerName,
[Parameter(ParameterSetName='default')]
[string]$exepath,
[Parameter(ParameterSetName='default')]
[string[]]$exeargs,
[Parameter(ParameterSetName='help')]
[switch]$help
)

Related

Parameter set cannot be resolved with mutually exclusive non default parameters

Following is full param block signature, I tried almost every possible solution that I'm aware of such as
adding Mandatory to File and Thumbprint parameters sets, removing default parameter set etc, none of which works.
Problem description and desired functionality is this:
Domain parameter is always required while File and ThumbPrint are optional but mutually exclusive.
I run this test as follows:
.\Set-WinRM* -Domain VM-PRO
Parameter set cannot be resolved using the specified named parameters.
function Set-WinRM
{
[CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "None")]
param (
[Parameter()]
[ValidateSet("HTTP", "HTTPS", "Any")]
[string] $Protocol = "HTTPS",
[Parameter(Mandatory = $true)]
[Alias("ComputerName", "CN")]
[string] $Domain,
[Parameter(ParameterSetName = "File")]
[string] $CertFile,
[Parameter(ParameterSetName = "ThumbPrint")]
[string] $CertThumbPrint,
[Parameter()]
[switch] $SkipTestConnection,
[Parameter()]
[switch] $ShowConfig
)
}
EDIT:
I never use dynamic parameters, if this can't be done normally maybe you can provide an example on how to define them in this example, that would be great.
I copy and pasted your code into Powershell ISE, and added echo $domain to the end to test the parameter. It returns the value normally, without errors:
I don't see any issues with your parameter block, which leads me to believe something else is at fault. If you type out .\Set-WinRM.ps1 -Do, or Set-WinRM -Do can you tab-complete it successfully? If you run Set-WinRM without parameters at all, does it prompt you for Domain:?
I would only expect to see that error if you had additional parameter sets with $domain doing different things, or maybe if a module you have loaded has the Set-WinRM command and it's taking precedence. Try Get-Command Set-WinRM and make sure the CommandType is Function and the Source is blank.

Trying to use parameters dynamically using powershell

I am trying to setup dynamic parameters that vary depending on if you are adding or modifying/removing a drone. Ex: If you are adding a drone you would need its IP/Name/Location.. To remove the drone you would only need its name. I have tried looking online and try various examples I've seen but I am completely stuck here. Any help to steer me in the right direction would be appreciated. I am somewhat new to powershell. Here's what I have.
[CmdletBinding(SupportsShouldProcess=$True)]
Param( [Parameter(Mandatory=$true,
HelpMessage = "Add remove or Modify a drone?")]
[ValidateSet("Add", "Remove", "Modify")]
[String]$Action)
DynamicParam{
if ($action = "Add"){
Param( [Parameter(Mandatory)]
[ValidateSet("NorthAmerica", "SouthAmerica", "NorthernEurope","UK", "CEE", "FRMALU", "SouthernEurope", "AsiaPacific")]
[String]$curRegion,
[Parameter(Mandatory)]
[IPAddress]$ip,
[Parameter(Mandatory)]
[String]$droneName)
}
if ($action = "Remove"){
Param(
[Parameter(Mandatory)]
[string]$droneRemoveName)
}
}
Consider driving your parameter constraints with named Parameter Sets instead. I'm suggesting this because dynamic parameters don't work quite like you think they do, but named parameter sets are an easier way to solve your problem. In case you're interested, here's a blog post explaining how to use dynamic parameters and it winds up being pretty manual parameter handling.
You can add a parameter to more than one parameter set depending on the contexts in which each parameter is required. Instead of using -Action ACTION as a driver for a dynamic parameter, use a [switch] instead, such as -Add and -Remove, and have each switch part of its own parameter set. For example, when defining your parameters, it may look something like this:
Param(
[Parameter(ParameterSetName='Remove')]
[switch]$Remove,
[Parameter(ParameterSetName='Add')]
[switch]$Add,
[Parameter(ParameterSetName='Remove', Mandatory)]
[Parameter(ParameterSetName='Add', Mandatory)]
[string]$IPAddress
)
In this example, -IPAddress is valid when you use the -Add or -Remove switch, but won't be relavant outside of this context. Of course, if a parameter should only be valid for a certain parameter set, don't define it under more than one parameter set name.
If you want to make sure at least one "action" switch is defined before executing, you can check that one of those parameters was used when invoking the cmdlet by checking $PSBoundParameters:
('Add' -in $PSBoundParameters.Keys) -Or ('Remove' -in $PSBoundParameters.Keys)

Handling boolean values for String parameters in Powershell

Using Powershell I'd like to call my script as follows:
myscript.ps1 -device1 enable -device2 disable -device3 enable
Each device paramenter is defined as String followed by a bool value.
param(
[string] $device1,
[string] $device2,
[string] $device3
)
Does PowerShell support this with some predefined functions or parameters or would you implement this in a totally different way? I'd like to avoid a parsing for enable and disable.
I would implement this using a switch:
param(
[switch] $device1,
[switch] $device2,
[switch] $device3
)
So you can invoke your script using:
myscript.ps1 -device1 -device3

Applying an attribute to all parameters

I've got the following list of parameters for a SQL script. They're all mandatory, as you can see.
param(
[Parameter(Mandatory)][string]$dropLoc,
[Parameter(Mandatory)][string]$manifest,
[Parameter(Mandatory)][string]$accountName,
[Parameter(Mandatory)][string]$appSqlServer,
[Parameter(Mandatory)][string]$appDatabaseName,
[Parameter(Mandatory)][string]$oltpDatabaseName,
[Parameter(Mandatory)][string]$oltpSqlServerName,
[Parameter(Mandatory)][string]$appRootURL,
[Parameter(Mandatory)][string]$AppServiceDBKey,
[Parameter(Mandatory)][string]$reportDBSqlServerName,
[Parameter(Mandatory)][string]$udmInstanceName,
[Parameter(Mandatory)][string]$reportDBDataPath,
[Parameter(Mandatory)][string]$reportDBLogPath,
[Parameter(Mandatory)][string]$maxETL
)
But this is really lengthy and I don't like how I set each one mandatory individually. Does Powershell have a way to set an attribute to all parameters?

How do I declare a parameter type as a .NET framework type that must be explicitly loaded?

I want to be able to pass a Color type from System.Drawing into my PowerShell script. However, unless I explicitly load the System.Drawing.dll prior to running my script and I don't want to have people do that.
I get this error unless I load the dll outside of the script:
Unable to find type [System.Drawing.Color]
My PowerShell Param declaration looks like this:
Param(
[Parameter(Mandatory = $true)][string] $TargetFile,
[Parameter()][int] $Thickness = 1,
[Parameter()][Switch] $UseTopLeftColor = $false,
[Parameter()][System.Drawing.Color] $BorderColor = [System.Drawing.Color]::FromArgb(195,195,195)
)
The code to load the DLL reference is this:
[Void][System.Reflection.Assembly]::LoadFile("C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll")
How can I define a dependency on the DLL for the script and have it automatically reference the DLL?
Add-Type is just a more convenient way of writing [Reflection.Assembly]::LoadFile(). Neither can be used before Param() in a script.
If you want to be able to run the PowerShell script from anywhere, I'd suggest specifying one parameter per basic color, and then calculating the color in the script. That way you can also validate each value:
Param(
[Parameter(Position=0)]
[ValidateRange(0,255)]
[int]$BorderRed = 195,
[Parameter(Position=1)]
[ValidateRange(0,255)]
[int]$BorderGreen = 195,
[Parameter(Position=2)]
[ValidateRange(0,255)]
[int]$BorderBlue = 195
)
Add-Type -Assembly 'System.Drawing'
[Drawing.Color]::FromArgb($BorderRed, $BorderGreen, $BorderBlue)
Trying to specify the color as a list of 3 values to a single parameter would be difficult to implement. From outside PowerShell you can't really pass an array to a parameter, so you'd have to specify something like a comma-separated string and parse that into 3 integer values.
Passing color names as a single parameter would work, though:
Param(
[Parameter()]
[string]$BorderColor = 'Blue'
)
Add-Type -Assembly 'System.Drawing'
[Drawing.Color]::$BorderColor