Cmdlet Using Advanced Parameter Sets in a Function with Multiple Unique Switches

Tagged: 

This topic contains 7 replies, has 4 voices, and was last updated by Profile photo of Max Kozlov Max Kozlov 3 months, 1 week ago.

  • Author
    Posts
  • #26724
    Profile photo of cyborgcommando
    cyborgcommando
    Participant

    I am using Powershell 4.0.

    I am attempting to write a cmdlet and in this cmdlet I would like to use Advanced Parameter sets to have certain options available based on what parameter(s) is/are chosen. This particular function will essentially and eventually be Get-ADComputer but with the -SearchBase preprogrammed for certain options.

    I have 6 parameters total. 2 are strings ($ComputerName or $IpAddress), 1 is an integer ($OULevel) and 3 switches ($ComputerOU, $AllCompany, $List).

    I have a parameter set each for ComputerName and IPAddress, I would like the user to be able to input one or the other – I think I have this figured out its pretty simple. However, I would like $OULevel, $ComputerOU and $AllCompany to be exclusive meaning if one is used the other should not be able to be used. $List should remain available in each scenario.

    I have tried different variations of the parameter sets to no avail. This is what my script looks like now, with some trimmed back:

        function Get-CompanyADComputer{
             [CmdletBinding(DefaultParametersetName="ComputerName")]
    
        Param(
            [Parameter(Mandatory=$true,
                        ParameterSetName="ComputerName",
                        Position=0,
                        ValueFromPipeline=$false,
                        HelpMessage='Enter a computer name to search in ActiveDirectory.')]
            [Alias('Computer','CN')]
            [string]$ComputerName,
    
            [Parameter(Mandatory=$true,
                        ParameterSetName="IPAddress",
                        Position=0,
                        ValueFromPipeline=$false,
                        HelpMessage='Enter an IP address to search in ActiveDirectory.')]
            [Alias('IPv4Address','IPv6Address')]
            [string]$IPAddress,
    
            [Parameter(Mandatory=$false,
                        HelpMessage='Enter a number between 0 and 8. 0 is your current OU Container.')]
            [ValidateRange(0,8)]
            [int]$OULevel = 0,
    
            [Parameter()]
            [Switch]$ComputerOU,
    
            [Parameter()]
            [Switch]$AllCompany,
    
            [Parameter()]
            [Switch]$List
        )
    

    If you are curious our AD is organized by Location, then category (computer, user, groups, contacts, etc) and then it gets more granular in each OU below. This script detects your computer's OU and starts the search there. The purpose of $OULevel is if the user specifies a different number the search will start in a different OU and then search recursively. The purpose of $ComputerOU is to have your search go to the default Computers OU instead of the entire domain or your location. The purpose of $AllCompany is to have the search default to the entire domain instead of any other choice or OU.

    Any guidance is appreciated. I can't seem to get the hang of this one without my script getting all convoluted.

  • #26725
    Profile photo of Don Jones
    Don Jones
    Keymaster

    So, you can potentially go with dynamic parameters, but I'm not sure that's the right thing.

    If I'm understanding the question, then you basically have to factor out all the possible combinations. Remember that a param can belong to 1+ param sets. So, you might have a set with Computername and OULevel, Computername and ComputerOU, and Computername and AllCompany. That's three. Then those three again for IPAddress. I know it's not elegant, but look at the help for Where-Object – not elegant, either, by far. List would then not belong to a set, which means it would belong to all.

  • #26755
    Profile photo of cyborgcommando
    cyborgcommando
    Participant

    Thanks Don! I ended up simplifying my script so I need one less parameter but I'll include the solution that would have been if I had kept my cmdlet exactly the same. Hopefully this will benefit someone else.

        function Get-CompanyADComputer{
         [CmdletBinding(DefaultParametersetName="ComputerName")]
    
    Param(
        [Parameter(Mandatory=$true,
                    ParameterSetName="ComputerName",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter a computer name to search in ActiveDirectory.')]
        [Parameter(Mandatory=$true,
                    ParameterSetName="ComputerNameOULevel",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter a computer name to search in ActiveDirectory.')]
        [Parameter(Mandatory=$true,
                    ParameterSetName="ComputerNameComputerOU",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter a computer name to search in ActiveDirectory.')]
        [Parameter(Mandatory=$true,
                    ParameterSetName="ComputerNameAllCompany",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter a computer name to search in ActiveDirectory.')]
        [Alias('Computer','CN')]
        [string]$ComputerName,
    
        [Parameter(Mandatory=$true,
                    ParameterSetName="IPAddress",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter an IP address to search in ActiveDirectory.')]
        [Parameter(Mandatory=$true,
                    ParameterSetName="IPAddressOULevel",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter an IP address to search in ActiveDirectory.')]
        [Parameter(Mandatory=$true,
                    ParameterSetName="IPAddressComputerOU",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter an IP address to search in ActiveDirectory.')]
        [Parameter(Mandatory=$true,
                    ParameterSetName="IPAddressAllCompany",
                    Position=0,
                    ValueFromPipeline=$false,
                    HelpMessage='Enter an IP address to search in ActiveDirectory.')]
        [Alias('IPv4Address','IPv6Address')]
        [string]$IPAddress,
    
        [Parameter(Mandatory=$false,
                    ParameterSetName="ComputerNameOULevel",
                    HelpMessage='Enter a number between 0 and 8. 0 is your current OU Container.')]
        [Parameter(Mandatory=$false,
                    ParameterSetName="IPAddressOULevel",
                    HelpMessage='Enter a number between 0 and 8. 0 is your current OU Container.')]
        [ValidateRange(0,8)]
        [int]$OULevel = 0,
    
        [Parameter(ParameterSetName="ComputerNameComputerOU")]
        [Parameter(ParameterSetName="IPAddressComputerOU")]
        [Switch]$ComputerOU,
    
        [Parameter(ParameterSetName="ComputerNameAllCompany")]
        [Parameter(ParameterSetName="IPAddressAllCompany")]
        [Switch]$AllCompany,
    
        [Parameter()]
        [Switch]$List
    )
    
  • #26807
    Profile photo of Max Kozlov
    Max Kozlov
    Participant

    do you strictly need to differentiate computer name from ip address ?
    And even it is need it can be simpified by script
    something like this

    param([string]$Computername)
    try {
     $IP=[System.Net.IPAddress]::Parse($ComputerName)
    'this can be parsed as ip address, it is ip address'
    $ip
     }
    catch {
    'This is string, assume it is computer name'
    }
    
  • #26824
    Profile photo of cyborgcommando
    cyborgcommando
    Participant

    Thanks for the information. Yes in this case I do have to specify one over the other because this cmdlet essentially is just a shell for Get-ADComputer or Get-ADUser with some of its parameters pre-configured for the company I work with. Get-ADComputer is pretty picky if you use IPAddress over ComputerName so that is the need for different parameters on my custom cmdlet.

    Thank you for the info though.

  • #26844
    Profile photo of Max Kozlov
    Max Kozlov
    Participant

    Even if you get ip and can't use it directly with (Get-ADComputer), you always can resolve it with
    [Net.Dns]::Resolve()
    btw, it even better than my previous suggestion, because it can resolve any of computername or ipaddress to hostname (but you can get problems with cluster or other one-to-many resolvable objects)

  • #63970
    Profile photo of Jim
    Jim
    Participant

    The following script is failing due to the named parameter sets...
    Test-Params : Parameter set cannot be resolved using the specified named parameters.

    Function Test-Params
    {
        [CmdletBinding()]
    	Param(
            [Parameter(Mandatory=$true)]
            [string]$ValueKey,
    
            [Parameter(Mandatory=$true, ParameterSetName="ComputerNameAttemptOnline")]
            [Parameter(Mandatory=$true, ParameterSetName="ComputerNameLookupOrder")]
            [switch]$SearchByComputerName,
    
            [Parameter(Mandatory=$false, ParameterSetName="ComputerNameAttemptOnline")]
            [Parameter(Mandatory=$false, ParameterSetName="ComputerNameLookupOrder")]
            [string]$ComputerName=$env:ComputerName,
    
            [Parameter(Mandatory=$true, ParameterSetName="SearchByOUAttemptOnline")]
            [Parameter(Mandatory=$true, ParameterSetName="SearchByOULookupOrder")]
            [switch]$SearchByOU,
    
            [Parameter(Mandatory=$false, ParameterSetName="SearchByOUAttemptOnline")]
            [Parameter(Mandatory=$false, ParameterSetName="SearchByOULookupOrder")]
            [string]$MachineOU,
    
            [Parameter(Mandatory=$false)]
            [ValidateNotNullOrEmpty()]
            [string]$DefaultValue,
    
            [Parameter(Mandatory=$false)]
            [ValidateNotNullOrEmpty()]
            [string]$OfflineSettingsFile="$scriptParentPath\OfflineSettings.ini",
    
            [Parameter(Mandatory=$false, ParameterSetName="ComputerNameAttemptOnline")]
            [Parameter(Mandatory=$false, ParameterSetName="SearchByOUAttemptOnline")]
            [switch]$AttemptOfflineModeFirst,
    
            [Parameter(Mandatory=$false, ParameterSetName="ComputerNameLookupOrder")]
            [Parameter(Mandatory=$false, ParameterSetName="SearchByOULookupOrder")]
            [string[]]$LookupOrder=@("Server", "Local"),
    
            [Parameter(Mandatory=$false)]
            [switch]$Decrypt
        )
    
            "ParameterSetName: $($PSCmdlet.ParameterSetName)"
    }
    

    However, Get-Help indicates that it should work ...

    Test-Params –ValueKeySearchByComputerName [-ComputerName ] [-DefaultValue ] [-OfflineSettingsFile ] [-LookupOrder ] [-Decrypt ] []

    Test-Params –ValueKeySearchByComputerName [-ComputerName ] [-DefaultValue ] [-OfflineSettingsFile ] [-AttemptOfflineModeFirst ] [-Decrypt ] []

    Test-Params –ValueKeySearchByOU [-MachineOU ] [-DefaultValue ] [-OfflineSettingsFile ] [-LookupOrder ] [-Decrypt ] []

    Test-Params –ValueKeySearchByOU [-MachineOU ] [-DefaultValue ] [-OfflineSettingsFile ] [-AttemptOfflineModeFirst ] [-Decrypt ] []

    Thanks in advance for your assistance

  • #64077
    Profile photo of Max Kozlov
    Max Kozlov
    Participant

    you listing indicate only that Test-Params have several parameter sets and it doesn't have default parameter set

    until you list the command you use to invoke Test-Params function noone can help you

You must be logged in to reply to this topic.