Parameters sets with "optional but mandatory" parameters

I’m sorry for the confusing title but I really don’t know how to title this…

 

Basically I have a parameter block that looks like this:

	[CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Path")]
	Param (
		[Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Path")]
		[SupportsWildcards()]
		[Alias("FilePath")]
		[string] $Path,
	[Parameter(Mandatory = $true, ParameterSetName = "Literal")]
	[Alias("LP")]
	[string] $LiteralPath,

	[Parameter(Position = 1)]
	[Parameter(ParameterSetName = "Literal")]
	[string] $ArgumentList,

	[Parameter()]
	[switch] $NoNewWindow,

	[Parameter()]
	[ValidateRange(-1, [int32]::MaxValue)]
	[int32] $Timeout = 10000,

	[Parameter()]
	[switch] $Async,

	[Parameter()]
	[switch] $Raw,

	[Parameter()]
	[Parameter(Mandatory = $true, ParameterSetName = "Credential")]
	[PSCredential] $Credential,

	[Parameter(ParameterSetName = "Credential")]
	[switch] $LoadUserProfile,

	[Parameter()]
	[Parameter(Mandatory = $true, ParameterSetName = "Credential")]
	[string] $WorkingDirectory
)
 
The -Path and -LiteralPath are mutually exclusive and either one is always required
The -Credential is optional, however IF specified then I need to ensure that -WorkingDirectory is specified as well.
Problem is that if I specify -Credential this will let me omit both the Path and -LiteralPath which is not acceptable, however I need a solution such that -Credential if specified works with either Path or LiteralPath but never on it's own.
How do I solve this?

Add both Literal and Path parameter sets to credential and workingdirectory parameter making credential as not mandatory. Parameters can be in multiple parameter sets.

Thank you for response.

I did as you said, this results in following syntax:

Invoke-Process [-Path] <string> [[-ArgumentList] <string>] [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [-Credential <pscredential>] [-WorkingDirectory <string>] [<CommonParameters>]

Invoke-Process [[-ArgumentList] <string>] -LiteralPath <string> [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [-Credential <pscredential>] [-WorkingDirectory <string>] [<CommonParameters>]

Invoke-Process [[-ArgumentList] <string>] -Credential <pscredential> -WorkingDirectory <string> [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [-LoadUserProfile] [<CommonParameters>]

Problem is that you can specify -Credential without -WorkingDirectory, however -Credential parameter requires -WorkingDirectory to be set.

On the other side, -WorkingDirectory can be set without specifying -Credential which is just fine.

There doesn’t seem to be a solution to this with just parameter sets, what is the conventional way to deal with this?

I have try to solve this with the help of your suggestion but another way around, instead of specifying Path and LiteralPath sets to both credential and workingdirectory, I’ve put them to WorkingDirectory only, relevant part looks like this:

 

		[Parameter(Mandatory = $true, ParameterSetName = "Credential")]
		[PSCredential] $Credential,
	[Parameter(ParameterSetName = "Credential")]
	[switch] $LoadUserProfile,

	[Parameter(ParameterSetName = "Path")]
	[Parameter(ParameterSetName = "Literal")]
	[Parameter(Mandatory = $true, ParameterSetName = "Credential")]
	[string] $WorkingDirectory
This resulted in following syntax however now -Credential can be specified without Path and LiteralPath:
Invoke-Process [[-ArgumentList] <string>] -Credential <pscredential> -WorkingDirectory <string> [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [-LoadUserProfile] [<CommonParameters>]

Invoke-Process [-Path] <string> [[-ArgumentList] <string>] [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [-WorkingDirectory <string>] [<CommonParameters>]

Invoke-Process [[-ArgumentList] <string>] -LiteralPath <string> [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [-WorkingDirectory <string>] [<CommonParameters>]


I don’t see a solution for this.

If you need four different options, specify four different parameter sets. If WorkingDirectory can be used on its own without Credential, that’s one parameter set. Credential being used requiring WorkingDirectory, that’s a second parameter set. Third and Fourth are related to Literal path and Path.

Function Invoke-Process {
    Param
    (
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Path")]
        [SupportsWildcards()]
        [Alias("FilePath")]
        [string] $Path,
 
        [Parameter(Mandatory = $true, ParameterSetName = "Literal")]
        [Alias("LP")]
        [string] $LiteralPath,
 
        [Parameter(Position = 1, ParameterSetName = "Literal")]
        [string] $ArgumentList,
 
        [Parameter()]
        [switch] $NoNewWindow,
 
        [Parameter()]
        [ValidateRange(-1, [int32]::MaxValue)]
        [int32] $Timeout = 10000,
 
        [Parameter()]
        [switch] $Async,
 
        [Parameter()]
        [switch] $Raw,
 
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = "Path")]
        [Parameter(Mandatory = $true, ParameterSetName = "Literal")]
        [Parameter(Mandatory = $true, ParameterSetName = "Credential")]
        [PSCredential] $Credential,

        [Parameter(Mandatory = $true, ParameterSetName = "Credential")]
        [switch] $LoadUserProfile,
 
        [Parameter(ParameterSetName="WorkingDirectory")]
        [Parameter(Mandatory = $true, ParameterSetName = "Credential")]
        [string] $WorkingDirectory
    )
}

Get-Command Invoke-Process -Syntax

Invoke-Process [-Credential] <pscredential> [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [<CommonParameters>]

Invoke-Process [[-ArgumentList] <string>] -LiteralPath <string> -Credential <pscredential> [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [<CommonParameters>]

Invoke-Process -Credential <pscredential> -LoadUserProfile -WorkingDirectory <string> [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [<CommonParameters>]

Invoke-Process [-NoNewWindow] [-Timeout <int>] [-Async] [-Raw] [-WorkingDirectory <string>] [<CommonParameters>]

If you’re not able to get exactly what you want from parameter sets then you’ll probably need to look at dynamic parameters.

Good point that I need 4 parameter sets to try to solve this, however problem is that either Path or LiteralPath must always be specified but must not be used together, this means -Credential must not be used on it’s own, this means turning 2 parameter sets into 4 which is just not possible and I’ve tried all possible combinations with the help of suggestions you guys gave so far.

Using dynamic parameters would surely solve this problem but I find it too arcane to write all the code for dynamic params.

Right now my solution is to not define LiteralPath parameter.

 

Thank you for help, it will surely help me because I encountered this problem many times over but never come to idea to define additional parameter set, so that’s useful information to know!