Dynamic Parameter question

Hi everyone,

I have a quick question here. Attached is the beginnings of a custom set of WSUS related functions to help us patch.

To use this function, you will need a WSUS Server with at least 1 computer checking into it as well as a WSUS group that has a space in the name. You will also need WMI and ICMP access to all computers you are targeting with this function.

A simple example of the function is:

 Update-WSAPatchStatus -Server "MyWSUSServer.Domain.Com" -WsusGroup "My Group" | Select-Object * 

The intent of -WSUSGroup being a dynamic parameter is that it automatically pulls the group names from wsus as a ValidateSet and thus, you can tab through them or if you are in ISE, you can select what you are looking for from a drop menu. The problem I have is when you tab or select from a drop menu, Powershell isnt wrapping names with spaces in them in quotes. So my team has to go back after they selected a group, and quote it.

Does anyone have any idea how I can fix this?

I’m thinking it might make most sense to make another function that ‘Get-WSUSGroups’ to return objects that your WSUSGroups parameter. That way you can pipe or use where clauses.

That’s a limitation of PowerShell’s current tab expansion functionality, unfortunately. I don’t think there’s anything you can do about it.

There is a bug filed on the Connect site, but it’s been there for over a year now. Not sure if / when this will become a priority: https://connect.microsoft.com/PowerShell/feedback/details/812233/auto-completed-values-with-spaces-do-not-have-quotes-around-them

I had considered that, but I don’t know that it would change the ability for me to import a list dynamically as a parameter set for a parameter. I would still think I would have the same problem with powershell not parsing my dynamic parameter’s data set correctly.

Darn, thanks Dave.

Actually, I just fiddled around with this a bit, and I was able to get it working by putting a modified TabExpansion2 function into my profile. However, this is something that would be tricky to deploy with your cmdlet without potentially stomping on any other custom modifications (such as someone using the TabExpansion++ module, etc.)

Here’s the code:

function TabExpansion2

    [CmdletBinding(DefaultParameterSetName = 'ScriptInputSet')]
        [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 0)]
        [string] $inputScript,
        [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 1)]
        [int] $cursorColumn,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 0)]
        [System.Management.Automation.Language.Ast] $ast,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 1)]
        [System.Management.Automation.Language.Token[]] $tokens,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 2)]
        [System.Management.Automation.Language.IScriptPosition] $positionOfCursor,
        [Parameter(ParameterSetName = 'ScriptInputSet', Position = 2)]
        [Parameter(ParameterSetName = 'AstInputSet', Position = 3)]
        [Hashtable] $options = $null

        if ($psCmdlet.ParameterSetName -eq 'ScriptInputSet')
            $completion = [System.Management.Automation.CommandCompletion]::CompleteInput(
            $completion = [System.Management.Automation.CommandCompletion]::CompleteInput(

        $count = $completion.CompletionMatches.Count
        for ($i = 0; $i -lt $count; $i++)
            $result = $completion.CompletionMatches[$i]

            if ($result.CompletionText -match '\s')
                $completion.CompletionMatches[$i] = New-Object System.Management.Automation.CompletionResult(

        return $completion

That code is a very simple implementation that just puts single quotes around anything containing whitespace. There are other things it should check for, technically (such as commas, etc), but it at least proves that this can be done.

Edit: And of course, our forum is stripping out anything in angle brackets because it thinks they should be HTML, not a PowerShell comment. :stuck_out_tongue: Oh well, at least all the executable code is there, even if it lines up weirdly.

This is similar to the problem from this post: https://powershell.org/forums/topic/cant-get-dynamicparam-to-work-when-set-values-include-commas/

Like Dave said, you can modify TabExpansion2 (which is a cool solution, but, like Dave says, can mess up other custom modifications), or you can try to use a C# helper class to put the quotes in for you. Here’s an example that creates a function named TestFunction with a dynamic parameter named -DynamicParam. Try it out and see if it works and makes sense:

Add-Type @"
    public class DynParamQuotedString {
        public DynParamQuotedString(string quotedString) : this(quotedString, "'") {}
        public DynParamQuotedString(string quotedString, string quoteCharacter) {
            OriginalString = quotedString;
            _quoteCharacter = quoteCharacter;

        public string OriginalString { get; set; }
        string _quoteCharacter;

        public override string ToString() {
            if (OriginalString.Contains(" ")) {
                return string.Format("{1}{0}{1}", OriginalString, _quoteCharacter);
            else {
                return OriginalString;

$__DynamicParamName = "DynamicParam"
$__DynamicParamValidateSet = @(
    "A string with spaces"
    "Another string with spaces"
function TestFunction {

    dynamicparam {
        $ParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $Attributes = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $Attributes.Add( (New-Object System.Management.Automation.ParameterAttribute) )

        # Convert each object into a DynParamQuotedString:
        $Attributes.Add( (New-Object System.Management.Automation.ValidateSetAttribute($__DynamicParamValidateSet | % { [DynParamQuotedString] $_.ToString() })) )

        $ParamDictionary.$__DynamicParamName = New-Object System.Management.Automation.RuntimeDefinedParameter (
            [DynParamQuotedString],  # Notice the type here

        return $ParamDictionary

    process {
        $ParamValue = $null
        if ($PSBoundParameters.ContainsKey($__DynamicParamName)) {
            # Get the original string back:
            $ParamValue = $PSBoundParameters.$__DynamicParamName.OriginalString

        "$__DynamicParamName = $ParamValue"

You guys are awesome. I will test the C# class implementation in a few, and if I cant get that working, I will override the tabexpansion with Dave’s solution (this function runs from a box that doesn’t have any other scripting/development going on, so it will be okay, although I will likely try just putting it into the module).

So the [DynParamQuotedString] class worked awesomely, even when I declared it inside of my DynamicParam scriptblock. It even plays nice when I declare it as an array.

Thanks, you guys are a life saver.