2

I'm attempting to create a function for a script module that verifies the previous parameter has been set before it shows the next parameter.

The parameters I need are Identity, Share and Quota. I always want Identity to show, I don't want Share to show until the Identity has been set and I don't want Quota to show until the Share has been set.

I'm able to easily access $Identity, I'm not able to access $Share from within DynamicParam{}. I stepped through the script using PowerGUI and I was only able to see $Share when I hit Begin{}.

I have a way to workaround this by just showing Share/Quota if Identity is set, but ultimately I would like to learn how to keep adding additional parameters based on the previously set parameter.

A copy of the function is below. personfileutility.exe is just an executable we use to interact with the various systems to provision and gather information on a user.

function New-PersonalFiles
{
    [CmdletBinding()]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true)]
        $Identity
    )

    DynamicParam
        {
            $paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary

            if (($Identity -notlike "") -or ($Identity -notlike $null)){
                $attributes = new-object System.Management.Automation.ParameterAttribute
                $attributes.ParameterSetName = "__AllParameterSets"
                $attributes.Mandatory = $true

                $lookup = [xml](\\servername\personalfiles\personfileutility.exe -a $Identity)

                $type = $lookup.User.user_directory.type.type

                if ($type -like "other" -or $type -like "staff") {
                    $arguments = @()
                    $arguments += "\\fileserver\sharename"
                }
                elseif ($type -like "faculty") {
                    $arguments = @()
                    $arguments += "\\fileserver\sharename"
                    $arguments += "\\fileserver\sharename2"

                }
                elseif ($type -like "student") {
                    $arguments = @()
                    $arguments += "\\fileserver2\sharename"
                }

                $ParamOptions = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $arguments

                $attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
                $attributeCollection.Add($attributes)
                $attributeCollection.Add($ParamOptions)


                $dynParam1 = new-object -Type System.Management.Automation.RuntimeDefinedParameter("Share", [String], $attributeCollection)

                $paramDictionary.Add("Share", $dynParam1)
            }

            if (($Share -like "\\fileserver\*"))
            {
                $attributes2 = new-object System.Management.Automation.ParameterAttribute
                $attributes2.ParameterSetName = "__AllParameterSets"
                $attributes2.Mandatory = $true

                $lookup = [xml](\\servername\personalfiles\personfileutility.exe -a $Identity)

                $type = $lookup.User.user_directory.type.type

                if ($type -like "other" -or $type -like "staff") {
                    $arguments = @()
                    $arguments += "15GB"
                    $arguments += "20GB"
                }
                elseif ($type -like "faculty") {
                    $arguments = @()
                    $arguments += "10GB"
                    $arguments += "15GB"

                }
                elseif ($type -like "student") {
                    $arguments = @()
                    $arguments += "5GB"
                    $arguments += "10GB"
                }

                $ParamOptions2 = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $arguments2

                $attributeCollection2 = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
                $attributeCollection2.Add($attributes2)
                $attributeCollection2.Add($ParamOptions2)


                $dynParam2 = new-object -Type System.Management.Automation.RuntimeDefinedParameter("Quota", [String], $attributeCollection2)

                $paramDictionary.Add("Quota", $dynParam2)
            }

            return $paramDictionary
        }

    <#
    .Synopsis
       Short description
    .DESCRIPTION
       Long description
    .EXAMPLE
       Example of how to use this cmdlet
    .EXAMPLE
       Another example of how to use this cmdlet
    #>

    Begin
    {
    }
    Process
    {
        \\servername\personalfiles\personfileutility.exe -a $Identity -c -q ((Invoke-Expression $PSBoundParameters.Quota)  / 1KB) -s $Share
    }
    End
    {
    }
}

2 Answers 2

3

I'm trying to replicate the "workaround" you're using, but I'm only seeing that I have access to the dynamic parameter Share (not Quota). In order to reproduce your setup, since I don't have access to personfileutility.exe I have commented out two lines and added a third where I hardcode $type to "faculty". For example, in two places I have updated the code to be:

#$lookup = [xml](\\servername\personalfiles\personfileutility.exe -a $Identity)
#$type = $lookup.User.user_directory.type.type
$type = "faculty"

With these changes in place, I can access the Share parameter after I specify an Identity. However, I cannot access the Quota. Do you expect Quota to be available?

If I'm understanding the problem you're asking about, you do not want Share to be accessible until Identity is accessible (which you currently have working). But you in addition, you don't want Quota to be accessible until both Identity and Share are filled out and you don't know how to make that work. Is that correct?

If I'm understanding the problem correctly, I don't believe that PowerShell offers a mechanism to achieve that with Commandlet binding. I think you could make that work by either using a GUI application, or interactively prompting the user for inputs.

Sign up to request clarification or add additional context in comments.

2 Comments

I think you are correct on this, it appears that I can only add dynamic parameters once. I'm able to add multiple dynamic parameters but it's all or nothing.
Would it be possible to use PSBoundParameters to pass your external parameters to an inner function that also has a dynamicParam defined?
0

Good news guys! I was facing a problem like this myself and didn't know what to do. I searched several forums but found nothing.

But the good news is: it works in PowerShell. You can define multiple dynamic parameters depending on the values (not the parameters themselves). You just have to take two steps back and rethink what the actual problem is.

So my function has three parameters: Team, Channel and Message. The Team parameter is the first parameter and is declared directly in the function. The Channel and Message parameters are dynamic parameters that are added to the function at runtime.

The Team parameter accepts only predefined values (from a hashtable) that can be completed with [TAB]. Only if this parameter was set, the second parameter Channel is added to the function, which also accepts only valid values (can also be completed with [TAB]). Finally, if the second parameter also has a valid value, the third parameter Message is added to the function.

The list of parameters could theoretically go on forever, but three is enough for me :-) So here is the code:

$MSTeamsWebhooks= @{
    "Team 1" = @{
        "Channel T1-1" = "123"
        "Channel T1-2" = "456"
        "Channel T1-3" = "789"
    }
    "Team 2" = @{
        "Channel T2-1" = "123"
        "Channel T2-2" = "456"
    }
    "Team 3" = @{
        "Channel T3-1" = "123"
        "Channel T3-2" = "456"
    }
}


function Send-MessageToMSTeams
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$Team
    )

    dynamicparam
    {
        $paramDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary

        if ($PSBoundParameters.ContainsKey('Team') -and $MSTeamsWebhooks.ContainsKey($PSBoundParameters['Team']))
        {
            $selectedTeam = $PSBoundParameters['Team']
            $teamChannels = $MSTeamsWebhooks[$selectedTeam].Keys
            $channelAttribute = New-Object -TypeName System.Management.Automation.ParameterAttribute
            $channelAttribute.Mandatory = $true
            $channelParameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('Channel', [string], $channelAttribute)
            $channelParameter.Attributes.Add((New-Object System.Management.Automation.ValidateSetAttribute($teamChannels)))
            $paramDictionary.Add('Channel', $channelParameter)
        }

        if ($paramDictionary.ContainsKey('Channel'))
        {
            $messageAttr = New-Object -TypeName System.Management.Automation.ParameterAttribute
            $messageAttr.Mandatory = $true
            $messageParam = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('Message', [string], $messageAttr)
            $paramDictionary.Add('Message', $messageParam)
        }

        return $paramDictionary
    }

    process
    {
        Write-Host $PSBoundParameters
    }
}

So the trick is not to use $PSBoundParameters or the parametername itself (like you did in your code with $Share) but to use $paramDictionary as you add it in the if-block before.

As a result, you first have to enter a valid team:

Send-MessageToMSTeams -Team #[TAB]
Send-MessageToMSTeams -Team "Team 1"

Send-MessageToMSTeams -Team "Team 1" -Channel
Send-MessageToMSTeams -Team "Team 1" -Channel #[TAB]
Send-MessageToMSTeams -Team "Team 1" -Channel "Channel T1-3"

Send-MessageToMSTeams -Team "Team 1" -Channel "Channel T1-3" -Message #type your text here

1 Comment

I don't seem to be getting the first tab completion with this code, am I missing something obvious?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.