Show a Help message (Syntax) instead of an error when no parameters specified

I wrote a script to retrieve VM metrics from VMware vCenter.

A user needs to provide 2 arguments/parameters. One is the vCenter server and the other is 1 of the following : Single VM name or a file containing a list of VMs.

The script works fine, but if someone tries to run this script at the PS console without providing any arguments, it throws an exception. (shown below)

Questions :

  1. Is the exception pointing to an issue I don't know ?
  2. I would rather, the user be shown the Syntax instead of an exception. Is that possible ?
[CmdletBinding()]

Param (

<span class="pun">[</span><span class="typ">Parameter</span><span class="pun">(</span> <span class="typ">Mandatory</span> <span class="pun">=</span><span class="pln"> $true</span><span class="pun">,</span> <span class="typ">ParameterSetName</span> <span class="pun">= </span><span class="str">'List'</span> <span class="pun">)]</span>
<span class="pun">[</span><span class="typ">Parameter</span><span class="pun">(</span> <span class="typ">Mandatory</span> <span class="pun">=</span><span class="pln"> $true</span><span class="pun">,</span> <span class="typ">ParameterSetName</span> <span class="pun">= </span><span class="str">'OneVM'</span> <span class="pun">)]</span>
<span class="pun">[</span><span class="pln">string</span><span class="pun">]</span><span class="pln">$vCenterServer</span><span class="pun">,</span>

<span class="pun">[</span><span class="typ">Parameter</span><span class="pun">(</span> <span class="typ">Mandatory</span> <span class="pun">=</span><span class="pln"> $true</span><span class="pun">,</span> <span class="typ">ParameterSetName</span> <span class="pun">= </span><span class="str">'List'</span> <span class="pun">)]</span>
<span class="pun">[</span><span class="typ">ValidateNotNullOrEmpty</span><span class="pun">()]</span>
<span class="pun">[</span><span class="typ">ValidateScript</span><span class="pun">(</span> <span class="pun">{</span> <span class="typ">Test</span><span class="pun">-</span><span class="typ">Path</span> <span class="pun">-</span><span class="typ">LiteralPath</span><span class="pln"> $_ </span><span class="pun">-</span><span class="typ">Type</span> <span class="typ">Leaf</span> <span class="pun">})]</span>
<span class="pun">[</span><span class="pln">string</span><span class="pun">]</span><span class="pln">$vmListFile</span><span class="pun">,</span>

<span class="pun">[</span><span class="typ">Parameter</span><span class="pun">(</span> <span class="typ">Mandatory</span> <span class="pun">=</span><span class="pln"> $true</span><span class="pun">,</span> <span class="typ">ParameterSetName</span> <span class="pun">= </span><span class="str">'OneVM'</span> <span class="pun">)]</span>
<span class="pun">[</span><span class="pln">string</span><span class="pun">]</span><span class="pln">$MyVM

)


When I run this without providing arguments/parameters, here is what I see.

PS> c:\Temp\vCenter_RT_PerfData.ps1
C:\Temp\vCenter_RT_PerfData.ps1 : Parameter set cannot be resolved using the specified named parameters.
    + CategoryInfo          : InvalidArgument: (:) [vCenter_RT_PerfData.ps1], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : AmbiguousParameterSet,vCenter_RT_PerfData.ps1

Here is what I wish they would see.

SYNTAX
    C:\Temp\vCenter_RT_PerfData.ps1 -vCenterServer <String> -MyVM <String> [<CommonParameters>]

    C:\Temp\vCenter_RT_PerfData.ps1 -vCenterServer <String> -vmListFile <String> [<CommonParameters>]

Any suggestions on presenting this another/better way are also welcome.

You could use the help message parameter attribute in combination with mandatory to guide the user on what to input.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-7
Otherwise you should drop mandatory and in your function check if there are no arguments and display the syntax help to them

Multiple parametersets are defined, you need to set a default parameterset to tell it the default parameters to ask for:

function Test-It {
    [CmdletBinding(DefaultParameterSetName = 'OneVM')]
    param (
        [Parameter( Mandatory = $true, ParameterSetName = 'List' )]
        [Parameter( Mandatory = $true, ParameterSetName = 'OneVM' )]
        [string]$vCenterServer,

        [Parameter( 
            Mandatory = $true, 
            ParameterSetName = 'List',
            HelpMessage="Enter path to vmlist file."
         )]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( { Test-Path -LiteralPath $_ -Type Leaf })]
        [string]$vmListFile,

        [Parameter( 
            Mandatory = $true, 
            ParameterSetName = 'OneVM',
            HelpMessage="Enter the VM name"
        )]
        [string]$MyVM
    )
    begin{}
    process{}
    end{}
}

Test-It

However, the parameters are not normally how you would handle providing a file to a function. Use other cmdlets as examples for parameter names as something like ‘MyVM’ could be an Alias, but Name or VMName would be a better descriptive name:

function Test-It {
    [CmdletBinding()]
    param (
        [Parameter( 
            Mandatory = $true,
            HelpMessage="Enter the VCenter Server name"
        )]
        [string]$vCenterServer,

        [Parameter( 
            Mandatory = $true, 
            HelpMessage="Enter the VM name"
        )]
        [string[]]$VMName
    )
    begin{}
    process{
        foreach ( $vm in $vmname ) {
            Write-Verbose -Message ('Processing {0} on vcenter server {1}' -f $vm,$vCenterServer)
        }
    }
    end{}
}

Test-It -vCenterServer Server -VMName vm1,vm2 -Verbose

#or

$vms = Get-Content -Path C:\Script\vmlist.txt
Test-It -vCenterServer Server -VMName $vms -Verbose

Thank you Rob!

DefaultParameterSet did the trick.