Summary: Microsoft Windows PowerShell MVP, Sean Kearney, continues a series of guest blogs detailing building your own cmdlet.
Microsoft Scripting Guy, Ed Wilson, is here. Guest blogger and Windows PowerShell MVP, Sean Kearney, has written a series about building cmdlets. For more about Sean, see his previous guest blog posts.
Note This is Part 6 of a nine-part series about building your own Windows PowerShell cmdlet. Read the entire series as it unfolds.
Here’s Sean…
Parameters within cmdlets
If you don’t enable [CmdletBinding()], you can still extend features of your advanced function as a cmdlet. This is done by preceding a parameter name or names with the argument [parameter()].
The additional properties you can send with [parameter()] are:
-
Mandatory
-
Position
-
ValueFromPipeline
-
ValueFromPipelineByPropertyName
-
ValueFromRemainingArguments
-
HelpMessage
-
Alias
Each of these properties has a purpose reflective of its name. This makes them quite easy to use and remember their purpose.
Mandatory
As you would have guessed, Mandatory means that this parameter absolutely must be in the pipeline. If it does not receive data, the cmdlet will prompt for the value. A good example of a mandatory parameter would be the path in Remove-Item. This cmdlet cannot function unless it knows what to remove. This would be mandatory for it to function.
If we want to make the $FolderName parameter mandatory in our cmdlet, we would modify it in the following fashion.
function global:ADD-LOGFILE{
[CmdletBinding(
DefaultParameterSetName=”Folder”,
SupportsShouldProcess=$True,
ConfirmImpact=’High’
)]
PARAM(
[parameter(Mandatory=$True)
[STRING[]]$Folder="C:\PowerShell",
[STRING[]]$Preface="Logfile",
[STRING[]]$Extension=".log"
)
Position
Position is simpler to deal with. If you would like users to run your cmdlet without keying in the parameter names, you can specify positions for certain parameters. For example, even though I can execute the following cmdlet…
GET-CHILDITEM –literalpath C:\MyFolder
…I can equally execute this cmdlet:
GET-CHILDITEM C:\MyFolder
Why is that? Because the assigned position of the LiteralPath parameter is 1, as we can see from Get-Help.
With the position parameter, we can make it easier for users to execute cmdlets with parameters they might typically use. We can do the same thing to our current set of parameters.
By default, if you don’t define this, Windows Powershell will assign a position based on the order that you define the parameters. It is recommended as a best practice to always assign the position when defining the parameters. You’ll find things don’t break later on when you go to add additional parameters as you need changes.
function global:ADD-LOGFILE{
[CmdletBinding(
DefaultParameterSetName=”Folder”,
SupportsShouldProcess=$True,
ConfirmImpact=’High’
)]
PARAM(
[parameter(Mandatory=$True),
Position=0]
[STRING[]]$Folder="C:\PowerShell",
[parameter(Position=1)]
[STRING[]]$Preface="Logfile",
[parameter(Position=2)]
[STRING[]]$Extension=".log"
)
ValueFromPipeline
You may or may not (because the design is completely in your hands) want to have a parameter accept data from the pipeline. This property is strictly a Boolean True or False setting. It specifies that a parameter can accept input from the pipeline. We can leverage this feature in our cmdlet by changing our parameters in the following manner.
function global:ADD-LOGFILE{
[CmdletBinding(
DefaultParameterSetName=”Folder”,
SupportsShouldProcess=$True,
ConfirmImpact=’High’
)]
PARAM(
[parameter(Mandatory=$True,
ValueFromPipeline=$True,
Position=0)]
[STRING[]]$Folder="C:\PowerShell",
[parameter(ValueFromPipeline=$True,
Position=1)]
[STRING[]]$Preface="Logfile",
[parameter(ValueFromPipeline=$True,
Position=1)]
[STRING[]]$Extension=".log"
)
ValueFromPipelineByPropertyName
This is similar to the prior parameter, but it specifies the actual property name that the parameter can accept from the pipeline. A good example to work with is Get-ChildItem. When the Get-ChildItem cmdlet is run against the file system, it returns multiple properties such as FullName, Extension, DirectoryName, and LastAccessTime amongst others as shown in the following example.
If we want to ensure that a parameter only accepts data from the pipeline when the property name is Extension, we can make this change to a parameter. We can tell the Add-LogFile cmdlet that we are building to accept the Extension property if passed through the pipeline.
function global:ADD-LOGFILE{
[CmdletBinding(
DefaultParameterSetName=”Folder”,
SupportsShouldProcess=$True,
ConfirmImpact=’High’
)]
PARAM(
[parameter(Mandatory=$True,
ValueFromPipeline=$True,
Position=0)]
[STRING[]]$Folder="C:\PowerShell",
[parameter(ValueFromPipeline=$True,
Position=1)]
[STRING[]]$Preface="Logfile",
[parameter(ValueFromPipeline=$True,
Position=1,
ValueFromPipelineByPropertyName=$True)]
[STRING[]]$Extension=".log"
)
ValueFromRemainingArguments
When you use this parameter, it identifies that any data coming down the pipeline that is not already assigned to other parameters will be accepted by this variable.
HelpMessage
There is nothing better for a user than being a little bit helpful. The Help message is a simple bit of text that you can provide to explain what this parameter is. In the case of the cmdlet we are building, it can be as simple as, “Folder to place log files in.” Here is a simple example of the HelpMessage put to use in our cmdlet.
function global:ADD-LOGFILE{
[CmdletBinding(
DefaultParameterSetName=”Folder”,
SupportsShouldProcess=$True,
ConfirmImpact=’High’
)]
PARAM(
[parameter(Mandatory=$True,
ValueFromPipeline=$True,
Position=0,
HelpMessage=’Folder to Store Logfiles in’)]
[STRING[]]$Folder="C:\PowerShell",
[parameter(ValueFromPipeline=$True,
Position=1,
HelpMessage=’TEXT to prepend all logfiles with’)]
[STRING[]]$Preface="Logfile",
[parameter(ValueFromPipeline=$True,
Position=1,
ValueFromPipelineByPropertyName=$True,
HelpMessage=’File Extension for Logfiles’)]
[STRING[]]$Extension=".log"
)
If we ran our cmdlet now and did not provide any information for the Folder parameter (because it is a MandatoryParameter), we would see the Help message echoed in this fashion.
Our Folder parameter now has a simple line of text to explain its purpose if needed. As part of the features of the cmdlet, Windows Powershell allows the user to enter “!?” to pull up that simple line of Help.
Alias
An alias allows you to provide alternate names that a parameter can accept data with. The parameter name itself will not change; but in the case of AcceptValueByPropertyName, we can give a parameter multiple names to accept the data with automatically.
In the case of Folder, we could choose to use an alias of Path or Destination if that made more sense.
Validation Parameters
You can apply various attributes that allow you to indicate how values sent into the parameters should be dealt with or how they can be referred to alternate. Validation attributes are added like the other parameter attributes, and they can allow us to validate data being passed into the cmdlet or to specify what the cmdlet does not permit for certain parameters. The validation attributes that are available to us are:
-
AllowNull
-
AllowEmptyString
-
AllowEmptyCollection
-
ValidateCount
-
ValidateLength
-
ValidatePattern
-
ValidateRange
-
ValidateScript
-
ValidateSet
-
ValidateNotNull
-
ValidateNotNullOrEmpty
~Sean
Thank you, Sean. You absolutely ROCK! Guest Blogger Week will continue tomorrow when Sean will bring us Part 7.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy