user4872340
user4872340

Reputation:

Multiple parameter sets and PowerShell

I am building a function which will have three distinct parameter sets, and two of those sets will overlap with the third. The options would look like this:

A B
A C
A (D E F)
A B (D E F)
A C (D E F)

To make it a little more clear, here is a partially completed version the function:

function Move-AccountOut {

    [CmdletBinding(DefaultParameterSetName='NoTransferHomeDrive')]
    Param(
        [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        [string]$Username,

        [Parameter(ParameterSetName='RetainGroups')]
        [switch]$RetainGroups,

        [Parameter(ParameterSetName='RemoveFromAllGroups')]
        [switch]$RemoveFromAllGroups,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$False)]
        [switch]$TransferHomeDrive,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [string]$OldServer,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [string]$NewServer
    )
}

The purpose of the function is to automate the process of transferring out an AD account to another location within the company. RetainGroups would automatically retain the users groups when set, and RemoveFromAllGroups would automatically remove the user from their groups. The two switches should not be able to be used together. Additionally, if TransferHomeDrive is set, it will call a function to schedule a transfer using an internal tool.

To put it another way, RetainGroups and RemoveFromAllGroups should be a member of all parameter sets (similar to Username), but should not be able to be used together.

I have tried two ways. The first:

function Move-AccountOut {

    [CmdletBinding(DefaultParameterSetName='NoTransferHomeDrive')]
    Param(
        [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        [string]$Username,

        [Parameter(ParameterSetName='RetainGroups')]
        [switch]$RetainGroups,

        [Parameter(ParameterSetName='RemoveFromAllGroups')]
        [switch]$RemoveFromAllGroups,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$False)]
        [Parameter(ParameterSetName='RetainGroups')]
        [Parameter(ParameterSetName='RemoveFromAllGroups')]
        [switch]$TransferHomeDrive,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [Parameter(ParameterSetName='RetainGroups')]
        [Parameter(ParameterSetName='RemoveFromAllGroups')]
        [string]$OldServer,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [Parameter(ParameterSetName='RetainGroups')]
        [Parameter(ParameterSetName='RemoveFromAllGroups')]
        [string]$NewServer
    )
}

Using this technique, retain and remove cannot be used together, but OldServer and NewServer are no longer mandatory. If I change them to:

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [Parameter(ParameterSetName='RetainGroups', Mandatory=$True)]
        [string]$OldServer,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [Parameter(ParameterSetName='RetainGroups', Mandatory=$True)]
        [string]$NewServer

They will be mandatory, but it no longer cares whether TransferHomeDrive is set.

If I set it up the opposite way:

function Move-AccountOut {

    [CmdletBinding(DefaultParameterSetName='NoTransferHomeDrive')]
    Param(
        [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        [string]$Username,

        [Parameter(ParameterSetName='RetainGroups')]
        [Parameter(ParameterSetName='TransferHomeDrive')]
        [switch]$RetainGroups,

        [Parameter(ParameterSetName='RemoveFromAllGroups')]
        [Parameter(ParameterSetName='TransferHomeDrive')]
        [switch]$RemoveFromAllGroups,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$False)]
        [switch]$TransferHomeDrive,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [string]$OldServer,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [string]$NewServer
    )
}

Then OldServer and NewServer will be mandatory, but RetainGroups and RemoveFromAllGroups can be used together. Additionally, if I use retain and remove together, then OldServer and NewServer become mandatory, but not when they are used on their own.

How do I make this work?

Upvotes: 22

Views: 19991

Answers (3)

briantist
briantist

Reputation: 47872

Ok, I think I understand this. The possible combinations you want are:

  1. -RetainGroups
  2. -RemoveFromAllGroups
  3. -RetainGroups plus -TransferHomeDrive
  4. -RemoveFromAllGroups plus -TransferHomeDrive
  5. Only -UserName
  6. -UserName plus -TransferHomeDrive

I am assuming that -OldServer and -NewServer only apply when moving the home drive, so whenever you are moving the home drive, they are mandatory. If that is not the case, let me know.

So what you have here are 6 parameter sets. As powerful as powershell's parameter set magic is, there isn't a good way to say "these 2 switches are mutually exclusive but should also be available in all parameter sets" so you have to multiplex it and repeat every parameter set with one or the other.

function Move-AccountOut {
[CmdletBinding(DefaultParameterSetName='OnlyUser')]
Param( 
    [Parameter(
        Mandatory=$True, 
        ValueFromPipeline=$True, 
        ValueFromPipelineByPropertyName=$True
    )]
    [string]
    $Username,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='RetainOnly'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='RetainAndTransfer'
    )]
    [switch]
    $RetainGroups,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='RemoveOnly'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='RemoveAndTransfer'
    )]
    [switch]
    $RemoveFromAllGroups,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='RetainAndTransfer'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='RemoveAndTransfer'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='TransferOnly'
    )]
    [switch]
    $TransferHomeDrive,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='RetainAndTransfer'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='RemoveAndTransfer'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='TransferOnly'
    )]
    [string]
    $OldServer,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='RetainAndTransfer'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='RemoveAndTransfer'
    )]
    [Parameter(
        Mandatory=$True,
        ParameterSetName='TransferOnly'
    )]
    [string]
    $NewServer
)

}

The output of Get-Help Move-AccountOut:

Move-AccountOut -Username <string>  [<CommonParameters>]

Move-AccountOut -Username <string> -RetainGroups -TransferHomeDrive -OldServer <string> -NewServer <string>  [<CommonParameters>]

Move-AccountOut -Username <string> -RetainGroups  [<CommonParameters>]

Move-AccountOut -Username <string> -RemoveFromAllGroups -TransferHomeDrive -OldServer <string> -NewServer <string>  [<CommonParameters>]

Move-AccountOut -Username <string> -RemoveFromAllGroups  [<CommonParameters>]

Move-AccountOut -Username <string> -TransferHomeDrive -OldServer <string> -NewServer <string>  [<CommonParameters>]

Simplifying It

If you want to make it less daunting, you might consider consolidating the remove and retain switches into a single parameter, something like this:

[Parameter(
    Mandatory=$false # you can leave this out
)]
[ValidateSet(
    'None',
    'Retain',
    'RemoveAll'
)]
[String]
$GroupAction = 'None'

This would reduce your parameter sets down to 2, and make your entire definition look like this:

function Move-AccountOut {
[CmdletBinding(DefaultParameterSetName='OnlyUser')]
Param( 
    [Parameter(
        Mandatory=$True, 
        ValueFromPipeline=$True, 
        ValueFromPipelineByPropertyName=$True
    )]
    [string]
    $Username,

    [ValidateSet(
        'None',
        'Retain',
        'RemoveAll'
    )]
    [String]
    $GroupAction = 'None' ,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='TransferOnly'
    )]
    [switch]
    $TransferHomeDrive,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='TransferOnly'
    )]
    [string]
    $OldServer,

    [Parameter(
        Mandatory=$True,
        ParameterSetName='TransferOnly'
    )]
    [string]
    $NewServer
)

}

With the following Get-Help output:

Move-AccountOut -Username <string> [-GroupAction <string> {None | Retain | RemoveAll}]  [<CommonParameters>]

Move-AccountOut -Username <string> -TransferHomeDrive -OldServer <string> -NewServer <string> [-GroupAction <string> {None | Retain | RemoveAll}]  [<CommonParameters>] 

I do want to point out that although that's simpler to define that doesn't mean it's better. It may be that you want to optimize your parameter sets for the caller which can be especially important if this is a function you plan on using interactively a lot from the shell, rather than calling from other scripts (and it seems like this may be the case).

So adding some complexity in the definition to make it easier to use might be the right thing to do.

Upvotes: 23

AntonV
AntonV

Reputation: 11

You can also use [ValidateScript()], if have two Mandatory = false parameters in all parameter sets, and you need to use them only together, e.g.:

function SomeFunction {
[CmdletBinding()]
Param(
[Parameter (Mandatory = $true,
ParameterSetName = "A")]
[Parameter (Mandatory = $true,
ParameterSetName = "B")]
[Parameter (Mandatory = $true,
ParameterSetName = "C")]
[switch]$Param1,
[Parameter (Mandatory = $true,
ParameterSetName = "A")]
[switch]$Param2,
[Parameter (Mandatory = $true,
ParameterSetName = "B")]
[string]$Param3, 
[Parameter (Mandatory = $true,
ParameterSetName = "C")]
[string]$Param4,
[Parameter (Mandatory = $false,]
[ValidateScript({
                    if ($Param6) {
                    $True                         
                    }
                    else {
                    throw "This parameter will work only with parameter [Param6]"
                    } 
                }
)]
[string]$Param5,
[Parameter (Mandatory = $false)]
[ValidateScript({
                    if ($Param5) {
                    $True                         
                    }
                    else {
                    throw "This parameter will work only with parameter [Param5]"
                    } 
                }
)]
[string]$Param6
...
}

Upvotes: 1

TheMadTechnician
TheMadTechnician

Reputation: 36342

By adding two more Parameter Sets you can do what you want. This is needed because you have 3 sets now, plus a non-set parameter (which technically puts it in the __AllParameterSets set if I remember right). So that's 4 ways of doing it. You need 6 ways of doing it if I am reading your question correctly. You want all of the following options:

Move-AccountOut -Username <string>  [<CommonParameters>]
Move-AccountOut -Username <string> [-RetainGroups] [-TransferHomeDrive] [-OldServer <string>] [-NewServer <string>]  [<CommonParameters>]
Move-AccountOut -Username <string> [-RetainGroups]  [<CommonParameters>]
Move-AccountOut -Username <string> [-RemoveFromAllGroups] [-TransferHomeDrive] [-OldServer <string>] [-NewServer <string>]  [<CommonParameters>]
Move-AccountOut -Username <string> [-RemoveFromAllGroups]  [<CommonParameters>]
Move-AccountOut -Username <string> -OldServer <string> -NewServer <string> [-TransferHomeDrive]  [<CommonParameters>]

So we will add the RemoveFromAllGroupsWTran and RetainGroupsWTran Parameter Sets, add them both to $TransferHomeDrive, $OldServer, and $NewServer (removing the other related set names from them), then add each to its respective switch parameter. It ends up looking like this:

function Move-AccountOut {

    [CmdletBinding(DefaultParameterSetName='NoTransferHomeDrive')]
    Param( 
        [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
        [string]$Username,

        [Parameter(ParameterSetName='RetainGroups')]
        [Parameter(ParameterSetName='RetainGroupsWTran')]
        [switch]$RetainGroups,

        [Parameter(ParameterSetName='RemoveFromAllGroups')]
        [Parameter(ParameterSetName='RemoveFromAllGroupsWTran')]
        [switch]$RemoveFromAllGroups,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$False)]
        [Parameter(ParameterSetName='RetainGroupsWTran')]
        [Parameter(ParameterSetName='RemoveFromAllGroupsWTran')]
        [switch]$TransferHomeDrive,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [Parameter(ParameterSetName='RetainGroupsWTran')]
        [Parameter(ParameterSetName='RemoveFromAllGroupsWTran')]
        [string]$OldServer,

        [Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True)]
        [Parameter(ParameterSetName='RetainGroupsWTran')]
        [Parameter(ParameterSetName='RemoveFromAllGroupsWTran')]
        [string]$NewServer
    )
}

Upvotes: 8

Related Questions