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?
Ok, I think I understand this. The possible combinations you want are:
-RetainGroups
-RemoveFromAllGroups
-RetainGroups plus -TransferHomeDrive
-RemoveFromAllGroups plus -TransferHomeDrive
Only -UserName
-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.
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
)
}
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
...
}
Related
OK, so I'm trying to write an advanced function that uses two different parameter set names, one is Default the other is TestAccountsOnly.
Most of this works fine, however, here's my problem:
The output of Get-Help New-SecondaryAccount gives me this in the SYNTAX section:
SYNTAX
New-SecondaryAccount [-Name] <String> [-AccountType] <String> [-Password] <String> [-Description] <String> [-OwnerEmployeeID] <String>
[[-AdditionalDescription]] [<CommonParameters>]
New-SecondaryAccount [-Name] <String> [-AccountType] <String> [-Password] <String> [-CoreOrReserved] <String> [-Description] <String>
[-OwnerEmployeeID] <String> [[-AdditionalDescription]] [<CommonParameters>]
From the looks of it, this is exactly what I want - one parameter set where I can validate a list of a handful of different -AccountTypes and move along where I have passwords, descriptions, etc., and the other where I validate just one value for AccountType and have a CoreOrReserve parameter that only belongs to the TestAccountsOnly parameter set.
Unfortunately, when trying to test this in the ISE, if I type:
New-SecondaryAccount -Name mehSomeAccount -AccountType, the only suggestion I get from IntelliSense is Test.
Can you not use [ValidateSet()] the way I'm trying to, or am I just doing it wrong?
Would really appreciate it if someone could point this out!
Function New-SecondaryAccount(DefaultParameterSetName="Default")
{
<#
.Synopsis
Creates a new secondary account based on the parameters
.DESCRIPTION
Creates a secondary AD user account based on parameters
specified. This includes several different types of accounts,
and determines the employeeType, OU, and description values
of the account created.
The CoreOrReserved parameter can only be used for accounts
where AccountType is set to Test
.INPUTS
[String]
.OUTPUTS
[ADObject]
.NOTES
.COMPONENT
MyModule_Part1
.FUNCTIONALITY
Active Directory Things
#>
[cmdletBinding(DefaultParameterSetName="Default")]
param(
[Parameter(Mandatory=$True,
Position=0,
ParameterSetName="Default",
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory=$True,
Position=0,
ParameterSetName="TestAccountsOnly",
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$Name,
[Parameter(Mandatory=$True,
Position=1,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateSet('ADAdmin','ServerAdmin','ServiceAccount','ChuckNorris')]
[Parameter(Mandatory=$True,
Position=1,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateSet("Test")]
[String]$AccountType,
[Parameter(Mandatory=$True,
Position=2,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_.Length -ge 12)
{
$True
}
else
{
throw "Password must be at least 12 characters"
$False
}
})]
[Parameter(Mandatory=$True,
Position=3,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_.Length -ge 12)
{
$True
}
else
{
throw "Password must be at least 12 characters"
$False
}
})]
[String]$Password,
[Parameter(Mandatory=$True,
Position=2,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateSet("Core","Reserved")]
[String]$CoreOrReserved,
[Parameter(Mandatory=$True,
Position=3,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_ -match "^TASK\d{7}\b")
{
$True
}
else
{
throw "Description must be a TASK number only`nEx. TASK1234567"
$False
}
})]
[Parameter(Mandatory=$True,
Position=4,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($_ -match "^TASK\d{7}\b")
{
$True
}
else
{
throw "Description must be a TASK number only`nEx. TASK1234567"
$False
}
})]
[String]$Description,
[Parameter(Mandatory=$True,
Position=4,
ParameterSetName="Default")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($(Get-ADUser -Filter {EmployeeID -eq $_ -and EmployeeType -eq "E"}) -ne $NULL)
{
$True
}
else
{
throw "$_ must correspond to a valid FTE user's employeeID number"
$False
}
})]
[Parameter(Mandatory=$True,
Position=5,
ParameterSetName="TestAccountsOnly")]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateScript(
{
if($(Get-ADUser -Filter {EmployeeID -eq $_ -and EmployeeType -eq "E"}) -ne $NULL)
{
$True
}
else
{
throw "$_ must correspond to a valid FTE user's employeeID number"
$False
}
})]
[String]$OwnerEmployeeID,
[Parameter(Mandatory=$False,
ParameterSetName="Default",
Position=5)]
[Parameter(Mandatory=$False,
ParameterSetName="TestAccountsOnly",
Position=6)]
[Switch]$AdditionalDescription
)
BEGIN{}
PROCESS{# implementation doing all the things here}
END{}
Unfortunately, you cannot declare more than one validate set attribute per parameter, which is one reason why its designation is separate.
You might be able to play around with dynamic parameters to get what you want. I stripped out a lot of stuff for clarity.
function New-SecondaryAccount() {
[cmdletBinding()]
param (
[Parameter(Mandatory,
Position = 0,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string] $Name,
[Parameter(Mandatory, Position = 1)]
[string] $Password,
[Parameter(Position = 2)]
[switch] $TestAccount
)
DynamicParam {
$attribute = New-Object System.Management.Automation.ParameterAttribute
$attribute.Mandatory = $true
$collection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$collection.Add($attribute)
if ($TestAccount) {
$validationSet = #("Test")
} else {
$validationSet = #("ADAdmin", "ServerAdmin", "ServiceAccount", "ChuckNorris")
}
$collection.Add((New-Object System.Management.Automation.ValidateSetAttribute($validationSet)))
$param = New-Object System.Management.Automation.RuntimeDefinedParameter('AccountType', [string], $collection)
$dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$dictionary.Add('AccountType', $param)
return $dictionary
}
PROCESS {
<# implementation doing all the things here #>
}
}
I'm writing a function for which two parameters should be exclusive and optional.
Here are valid inputs:
new-event -Title sometitle -Text sometext -TimestampHappened 1234567 -SomeOtherOptionalParam somestring
new-event -Title sometitle -Text sometext -DateHappened (get-date) -SomeOtherOptionalParam somestring
new-event -Title sometitle -Text sometext -SomeOtherOptionalParam somestring
new-event -Title sometitle -Text sometext
Here is an invalid input:
new-event -Title sometitle -Text sometext -DateHappened (get-date) -TimestampHappened 1234567 -SomeOtherOptionalParam somestring
Here is my code so far:
[CmdletBinding()]
# Most parameters belong to Default, New-Event:ByDate and New-Event:ByTimestamp parameter sets
param (
[Parameter(
Position=0,
Mandatory=$True,
ParameterSetName="Default"
)]
[Parameter(
Position=0,
Mandatory=$True,
ParameterSetName="New-Event:ByDate"
)]
[Parameter(
Position=0,
Mandatory=$True,
ParameterSetName="New-Event:ByTimestamp"
)]
[ValidateNotNullOrEmpty()]
[String]$Title,
[Parameter(
Position=1,
Mandatory=$True,
ParameterSetName="Default"
)]
[Parameter(
Position=1,
Mandatory=$True,
ParameterSetName="New-Event:ByDate"
)]
[Parameter(
Position=1,
Mandatory=$True,
ParameterSetName="New-Event:ByTimestamp"
)]
[ValidateNotNullOrEmpty()]
[String]$Text,
[Parameter(
Position=2,
Mandatory=$False,
ParameterSetName="New-Event:ByDate"
)]
[ValidateNotNullOrEmpty()]
[datetime]$DateHappened,
[Parameter(
Position=2,
Mandatory=$False,
ParameterSetName="New-Event:ByTimestamp"
)]
[ValidateNotNullOrEmpty()]
[Double]$TimestampHappened,
[Parameter(
Position=3,
Mandatory=$False,
ParameterSetName="Default"
)]
[Parameter(
Position=3,
Mandatory=$False,
ParameterSetName="New-Event:ByDate"
)]
[Parameter(
Position=3,
Mandatory=$False,
ParameterSetName="New-Event:ByTimestamp"
)]
[String]$SomeOtherParam,
...
Here is what I get when I call Get-Help:
PS> get-help New-Event
NAME
New-Event
SYNOPSIS
Post an event to the stream.
SYNTAX
New-Event [-Title] <String> [-Text] <String> [[-TimestampHappened] <Double>] [[-Priority] <String>] [[-Hostname] <String>] [[-Tags] <String[]>] [[-AlertType] <String>] [<CommonParameters>]
New-Event [-Title] <String> [-Text] <String> [[-DateHappened] <DateTime>] [[-Priority] <String>] [[-Hostname] <String>] [[-Tags] <String[]>] [[-AlertType] <String>] <String>] [<CommonParameters>]
New-Event [-Title] <String> [-Text] <String> [[-Priority] <String>] [[-Hostname] <String>] [[-Tags] <String[]>] [[-AlertType] <String>] [<CommonParameters>]
However here is the error I get when I try to call the function with only the two mandatory parameters:
New-Event -Title test -Text text
New-Event : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:1
+ New-Event -Title test -Text text
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-Event], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,New-Event
I'm missing something here, but I can't figure out what...
How can I get two parameters that are mutually exclusive and optional?
This makes perfect sense. You have 3 parameter sets, and the 2 mandatory parameters are included on every set. How could PowerShell determine which set you meant to use?
Luckily the [CmdletBinding()] attribute can take a parameter that helps with this exact case: DefaultParameterSetName. Setting this allows PowerShell to use this set in the case of (certain) ambiguities. Use it like so:
[CmdletBinding(DefaultParameterSetName='Default')]
Note that in this case, you named it default; it could have been named anything.
So I have this script that creates a snap mirror on our dr server based on its location. Below is just a small part of a the script. I need to write an if statement so if location='uk' then to not run the below function otherwise if location='us' then create snap-mirror.
function Create-SnapMirror {
[CmdletBinding(PositionalBinding=$false,
HelpUri='http://www.microsoft.com/',
ConfirmImpact='Medium')]
[OutputType([Boolean])]
Param(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=0)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$SourcePath,
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=1)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$DestinationPath,
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=2)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$LogName
)
$success = $null
$error.Clear()
}
Assuming that the logic is required outside of the function you can achieve this by simply encapsulating the function call within the if statement as follows:
if($Location -eq 'us') { Create-SnapMirror -SourcePath $MySourcePath -DestinationPath $MyDestinationPath -LogName $MyLogName }
If however you want to check the location within the function you will first need to receive the location either from the input parameters or some other method. Assuming that you have the location in a variable named $Location you can simply add the following within your function before any other action:
if($Location -ne 'us') { return }
This will exit the function; you can add other actions, for instance logging within the parenthesis.
I was struggling in a situation where I need to make some parameters mandatory ONLY if a switch or combination of switches is used. Below is the example of what I am trying to do:
[CmdletBinding(DefaultParameterSetName='DefaultConfiguration')]
Param
(
[Parameter(Mandatory=$true)][String]$Location,
[Parameter(Mandatory=$true)][String]$DPMServername,
[Parameter(Mandatory=$False, ParameterSetName='CustomConfiguration')]
[Switch]$CustomizeDPMSubscriptionSettings,
[Parameter(Mandatory=$True, ParameterSetName='CustomConfiguration')]
[String]$StagingAreaPath,
[Parameter(Mandatory=$False, ParameterSetName='EncryptionSettings')]
[Parameter(ParameterSetName='CustomConfiguration')]
[Switch]$SetEncryption,
[Parameter(Mandatory=$true, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$False, ParameterSetName='CustomConfiguration')]
[String]$EncryptionPassPhrase,
[Parameter(Mandatory=$False, ParameterSetName='ProxyEnabled')]
[Parameter(ParameterSetName='CustomConfiguration')]
[Switch]$SetProxy,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(ParameterSetName='CustomConfiguration')]
[String]$ProxyServerAddress,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$False, ParameterSetName='CustomConfiguration')]
[String]$ProxyServerPort
)
Here, I need to follow below conditions:
If -CustomizeDPMSubscriptionSettings (Switch) parameter is used,
it must ask for the Staging AreaPath ---- This is Working fine
ONLY when -CustomizeDPMSubscriptionSettings (Switch) parameter is
used with -SetEncryption, it must ask for -EncryptionPassPhrase
And ONLY when -CustomizeDPMSubscriptionSettings (Switch) parameter is
used with -SetProxy, it must ask for -ProxyServerAddress and -ProxyServerPort
Sorry if this sounds like a repeated question but other posts I found here are not helping me solve my issue. I am confused :-(
NOTE: Above code is part of what I was trying with different combinations. Please correct as necessary.
Here is a solution that seems to do what you expect.
What I did was create a parameter set for each possible combination.
- CustomConfiguration
- EncryptionSettings
- ProxyEnabled
- EncryptionAndProxy
One limitation is that it will not prompt for specific missing parameters unless using EncryptionAndProxy, but will instead state that it cannot resolve the parameter set.
[CmdletBinding(DefaultParameterSetName='DefaultConfiguration')]
Param
(
[Parameter(Mandatory=$true)][String]$Location,
[Parameter(Mandatory=$true)][String]$DPMServername,
[Parameter(Mandatory=$True, ParameterSetName='CustomConfiguration')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[Switch]$CustomizeDPMSubscriptionSettings,
[Parameter(Mandatory=$True, ParameterSetName='CustomConfiguration')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$StagingAreaPath,
[Parameter(Mandatory=$True, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[Switch]$SetEncryption,
[Parameter(Mandatory=$true, ParameterSetName='EncryptionSettings')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$EncryptionPassPhrase,
[Parameter(Mandatory=$True, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[Switch]$SetProxy,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$ProxyServerAddress,
[Parameter(Mandatory=$true, ParameterSetName='ProxyEnabled')]
[Parameter(Mandatory=$True, ParameterSetName='EncryptionAndProxy')]
[String]$ProxyServerPort
)
I'm looking into a second potential solution based on dynamic parameters.
Edit: As promised, here's a solution based on dynamic parameters
[CmdletBinding(DefaultParameterSetName='DefaultConfiguration')]
Param
(
[Parameter(Mandatory=$true)][String]$Location,
[Parameter(Mandatory=$true)][String]$DPMServername,
[Switch]$CustomizeDPMSubscriptionSettings,
[Switch]$SetEncryption,
[Switch]$SetProxy
)
DynamicParam
{
$paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$attributes = New-Object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Mandatory = $true
$attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
# If "-SetEncryption" is used, then add the "EncryptionPassPhrase" parameter
if($SetEncryption)
{
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("EncryptionPassPhrase", [String], $attributeCollection)
$paramDictionary.Add("EncryptionPassPhrase", $dynParam1)
}
# If "-SetProxy" is used, then add the "ProxyServerAddress" "ProxyServerPort" and parameters
if($SetProxy)
{
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("ProxyServerAddress", [String], $attributeCollection)
$paramDictionary.Add("ProxyServerAddress", $dynParam1)
$dynParam2 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("ProxyServerPort", [String], $attributeCollection)
$paramDictionary.Add("ProxyServerPort", $dynParam2)
}
# If "-CustomizeDPMSubscriptionSettings" is used, then add the "StagingAreaPath" parameter
if($CustomizeDPMSubscriptionSettings)
{
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("StagingAreaPath", [String], $attributeCollection)
$paramDictionary.Add("StagingAreaPath", $dynParam1)
}
return $paramDictionary
}
Process{
foreach($key in $PSBoundParameters.keys)
{
Set-Variable -Name $key -Value $PSBoundParameters."$key" -Scope 0
}
}
What this one does is dynamically add parameters to your function based on the presence of each switch.
This supports autocompletion, and has better support for missing parameters. It will explicitly ask for missing parameters if the corresponding switch is used.
Second edit: I added the Process section that's mandatory with this construct, as well as the variable creation bit, which makes things much easier.
I am putting together a set of tools for internal account management. This particular cmdlet will prep an account for a transfer out, which includes an option to remove or retain their groups. My Param statement has become quite lengthy and has ended up with 6 parameter sets to cover every scenario. All combinations of parameters are working except for one. Here is my code:
function Move-AccountOut {
[CmdletBinding(DefaultParameterSetName='NoTransferHomeDrive')]
Param(
[Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True,Position=1)]
[string]$Username,
[Parameter(ParameterSetName='RetainGroups',Position=2)]
[Parameter(ParameterSetName='RetainGroupsWTran',Position=2)]
[switch]$RetainGroups,
[Parameter(ParameterSetName='RemoveFromAllGroups',Position=2)]
[Parameter(ParameterSetName='RemoveFromAllGroupsWTran',Position=2)]
[switch]$RemoveFromAllGroups,
[Parameter(ParameterSetName='TransferHomeDrive', Position=3)]
[Parameter(ParameterSetName='RetainGroupsWTran', Position=3)]
[Parameter(ParameterSetName='RemoveFromAllGroupsWTran', Position=3)]
[switch]$TransferHomeDrive,
[Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True, Position=4)]
[Parameter(ParameterSetName='RetainGroupsWTran', Mandatory=$True, Position=4)]
[Parameter(ParameterSetName='RemoveFromAllGroupsWTran', Mandatory=$True, Position=4)]
[string]$OldServer,
[Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True, Position=5)]
[Parameter(ParameterSetName='RetainGroupsWTran', Mandatory=$True, Position=5)]
[Parameter(ParameterSetName='RemoveFromAllGroupsWTran', Mandatory=$True, Position=5)]
[string]$NewServer
)
}
Which will generate the following Get-Help output:
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>]
The parameter I am having trouble with is the bottom one (transfer only). I can run the command with username, username+retain, username+remove, username+retain+transfer and username+remove+transfer. But transfer without a retain or remove does not work. When run, it throws the following error:
Move-AccountOut : Parameter set cannot be resolved using the specified named parameters.
At line:33 char:1
+ Move-AccountOut -Username X -TransferHomeDrive -OldServer X -NewServer Y
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Move-AccountOut], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Move-AccountOut
I can't figure out why, given that it appears to be a valid parameter set according to my Get-Help output.
Thank you for the output, here is the key (emphasis mine):
Remaining valid parameter set: RetainGroupsWTran
Remaining valid parameter set: RemoveFromAllGroupsWTran
Remaining valid parameter set: TransferHomeDrive
This happens after all parameters have been bound, and it shows that there are 3 valid parameter sets remaining.
TransferHomeDrive is the parameter set you want.
So why are the other ones there?
RetainGroupsWTran should only be possible if -RetainGroups is specified, and RemoveFromAllGroupsWTran should only be possible if -RemoveFromAllGroups is specified.
However, you can also see in the Get-Help output that there is no parameter set where those switches are mandatory, and this is the problem.
Your updated definition looks like the following:
function Move-AccountOut {
[CmdletBinding(DefaultParameterSetName='NoTransferHomeDrive')]
Param(
[Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True,Position=1)]
[string]$Username,
[Parameter(ParameterSetName='RetainGroups',Position=2)]
[Parameter(Mandatory=$true,ParameterSetName='RetainGroupsWTran',Position=2)]
[switch]$RetainGroups,
[Parameter(ParameterSetName='RemoveFromAllGroups',Position=2)]
[Parameter(Mandatory=$true,ParameterSetName='RemoveFromAllGroupsWTran',Position=2)]
[switch]$RemoveFromAllGroups,
[Parameter(ParameterSetName='TransferHomeDrive', Position=3)]
[Parameter(ParameterSetName='RetainGroupsWTran', Position=3)]
[Parameter(ParameterSetName='RemoveFromAllGroupsWTran', Position=3)]
[switch]$TransferHomeDrive,
[Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True, Position=4)]
[Parameter(ParameterSetName='RetainGroupsWTran', Mandatory=$True, Position=4)]
[Parameter(ParameterSetName='RemoveFromAllGroupsWTran', Mandatory=$True, Position=4)]
[string]$OldServer,
[Parameter(ParameterSetName='TransferHomeDrive', Mandatory=$True, Position=5)]
[Parameter(ParameterSetName='RetainGroupsWTran', Mandatory=$True, Position=5)]
[Parameter(ParameterSetName='RemoveFromAllGroupsWTran', Mandatory=$True, Position=5)]
[string]$NewServer
)
}
I've made them mandatory within the parameter sets that were showing up in the trace output.
I think that you should also make them mandatory in the RetainGroups and RemoveFromAllGroups parameter sets as well, but it's not shown above.