Interesting finds around PowerShell Mandatory Parameters
Posted 20 June 2011, 15:47 | by Ben Duguid | Perma-link
I noticed an interesting thing today while attempting to make some parameters on a PowerShell script required.
As I'm just starting to get the hang of PowerShell, you'll have to bear with my script-kiddie tendencies, however, here's the background:
I'm creating a script that will take the following parameters (can you tell I'm still working with TFS?):
-
[string] WorkItemType
-
[string] ProjectsToUse
-
[switch] ExportWitd
I wanted to make the WorkItemType parameter required, and searching around the internet led me to the following code:
[string] $WorkItemType = $(throw 'workItemType is required')
Which does indeed "work" if you're happy with exceptions being thrown if you don't set it:
workItemType is required
At C:\Scripts\UpdateTFSWorItems.ps1:17 char:37
+ [string] $WorkItemType = $(throw <<<< "workItemType is required"),
+ CategoryInfo : OperationStopped: (workItemType is required:String) [], RuntimeException
+ FullyQualifiedErrorId : workItemType is required
And it also doesn't help if more than one parameter is required (you only get the first error), and get-help
doesn't help:
PS C:\Scripts> get-help .\UpdateTFSWorItems.ps1
UpdateTFSWorItems.ps1 [[-WorkItemType] <String>] [[-ProjectsToUse] <String>] [-ExportWitd]
As you can see, get-help
thinks that WorkItemType is optional (the square brackets around the name and the type).
The actual answer came in the form of the parameter
attribute:
[parameter(Mandatory = $true)]
[string] $WorkItemType
Now when I run the script without the WorkItemType parameter I get prompted to supply the required parameters as a read prompt, rather than nasty exceptions:
cmdlet UpdateTFSWorItems.ps1 at command pipeline position 1
Supply values for the following parameters:
WorkItemType:
However, this also has some fairly major consequences:
It converts your script into an "Advanced Function", as some of the documentation on TechNet for this states:
[T]o be recognized as an advanced function (rather than a simple function), a function must have either the
CmdletBinding
attribute or theParameter
attribute, or both.
There doesn't appear to be a way to apply one or other of these attributes and not be recognised as an advanced function.
Because your script is now "advanced", get-help
is a bit different:
PS C:\Scripts> get-help .\UpdateTFSWorItems.ps1
UpdateTFSWorItems.ps1 [-WorkItemType] <String> [[-ProjectsToUse] <String>] [-ExportWitd]
[-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>]
[-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>]
[-OutBuffer <Int32>]
Where did all those extra parameters come from? Your function now supports all the "Common Parameters", this is more clearly stated if you add some help documentation to your script:
<#
.Parameter WorkItemType
Required. The name of the work item type you want to update in TFS.
.Parameter ProjectsToUse
New or Legacy, defaults to New.
#>
Calling get-help
now results in the following syntax line being auto-generated:
SYNTAX
C:\Scripts\UpdateTFSWorItems.ps1 [-WorkItemType] <String> [[-ProjectsToUse] <String>]
[-ExportWitd] [<CommonParameters>]
In general though I think this is a bonus, as it allows me to call the write-verbose
and write-debug
methods to get additional output and breakpoints.
If you're going to go to the effort of adding parameter documentation, you might as well also supply the HelpMessage
parameter of the Parameter
attribute:
[parameter(Mandatory = $true, HelpMessage = "The name of the work item type you want to update in TFS.")]
[string] $WorkItemType
Which then allows the user to get some help as they are prompted:
cmdlet UpdateTFSWorItems.ps1 at command pipeline position 1
Supply values for the following parameters:
(Type !? for Help.)
WorkItemType: !?
The name of the work item type you want to update in TFS
WorkItemType:
Gotcha with Write-Verbose
Write-host
appears to take an array of objects and write a string representation of them out to the screen, which can be (ab)used in "interesting" ways:write-host "Exporting WIT Definition from TFS Project" $selectedProjects[$i]
Results in output like:
Exporting WIT Definition from TFS Project Generic
Write-verbose (and indeed write-debug) explicitly only take a single string however, and calling them in the same way results in a nasty exception to that effect, so make sure you concatenate them:
write-verbose ("Project folder " + $selectedProjects[$i] + " already exists")
Which is slightly tedious.
Filed under: PowerShell, TFS