Get-Command -syntax returns just the first set of parameters when using dynamic parameters

Viewed 70

I am having a hard time understanding how Powershell resolves syntax when using dynamic parameters.

A simple, reproducible example:

using System;
using System.Management.Automation;

namespace SOQuestion
{
    [Cmdlet(VerbsCommon.Get, "Greeting")]
    public class GetGreeting : PSCmdlet, IDynamicParameters
    {
        [Parameter(Position = 0, Mandatory = true)]
        public NameType NameType { get; set; }

        private object context;
        public object GetDynamicParameters()
        {
            switch (NameType)
            {
                case NameType.First:
                    context =  new DynParamsFirst();
                    return context;
                case NameType.Full:
                    context = new DynParamsFull();
                    return context;
                default:
                    throw new ArgumentException("Invalid type specified");
            }
        }
        protected override void ProcessRecord()
        {
            string name;
            switch (NameType)
            {
                case NameType.First:
                    DynParamsFirst first = context as DynParamsFirst;
                    name = first.FirstName;
                    break;
                case NameType.Full:
                    DynParamsFull full = context as DynParamsFull;
                    name = full.First + " " + full.Last;
                    break;
                default:
                    throw new ArgumentException("Invalid type specified");
            }
            WriteObject($"Hello {name}!");
        }
    }

    public enum NameType
    {
        First = 0,
        Full = 1
    }

    public class DynParamsFirst
    {
        [Parameter(Position = 1, Mandatory = true)]
        public string FirstName { get; set; }
    }

    public class DynParamsFull
    {
        [Parameter(Position = 1, Mandatory = true)]
        public string First { get; set; }
        [Parameter(Position = 2, Mandatory = true)]
        public string Last { get; set; }
    }
}

This runs as expected when supplying the parameters in the CLI, my parameters are nicely dynamic, and I get the expected output.

But when I run Get-Command Get-Greeting -syntax I get:

Get-Greeting [-NameType] <NameType> [-FirstName] <string> [<CommonParameters>]

Those are the parameters that belong to DynParamsFirst. There is no indication of parameters being dynamic. I found that the syntax being pulled is the syntax for the first match in the NameType enum - if I set Full to 0 in the NameType enum, then Get-Command Get-Greeting -syntax will output the parameters for DynParamsFull instead. Also, most likely related, if I enter Get-Greeting without parameters and hit Enter, I always get just FirstName, which throws an error in combination with the Full NameType.

I find that surprising and it is giving a hard time documenting the command. I also find it non-trivial to formulate a proper question.

Here goes: How do I change the code so that when entering the command without parameters, Powershell is aware that there are dynamic parameters involved?

1 Answers

-NameType is your cmdlet's only static parameter. Its default value is implicitly [NameType]::new(), i.e., the [NameType] type's implicit default value: as an enum-derived type, i.e. as a .NET value type, it implicitly has a default constructor that initializes the value to all zeros, which in your case translates to NameType.First:

Therefore, without (implied) additional context, Get-Command Get-Greeting -Syntax behaves as if you had passed -NameType First to Get-Greeting, and that's the syntax diagram you see: it shows the corresponding -FirstName dynamic parameter:

PS> Get-Command Get-Greeting -syntax # implies: -Args '-NameType', First - see below.

Get-Greeting [-NameType] <NameType> [-FirstName] <string> [<CommonParameters>]

In order to see what other syntax diagrams may apply situationally, you have to pass pass-through arguments to Get-Command -Syntax via its -ArgumentList / -Args parameter, which then shows the situationally appropriate syntax, _as if those arguments were passed to the target command, Get-Greeting:

PS> Get-Command Get-Greeting -syntax -Args '-NameType', Full

Get-Greeting [-NameType] <NameType> [-First] <string> [-Last] <string> [<CommonParameters>]

Note the perhaps surprising need to quote -NameType, lest it be interpreted as another parameter name rather than as a parameter value passed to -Args - see GitHub issue #4624.

Related