Trevor Sullivan's Tech Room

Minding the gap between administration and development

PowerShell: Dynamic Parameters and Parameter Validation

Posted by Trevor Sullivan on 2010/11/15


Background on Parameter Validation

PowerShell advanced functions allow their creators to specify a fair amount of metadata that describes their parameters. One huge benefit of parameter declarations in PowerShell is that it’s possible to validate input right at the parameter level, before you execute any code in the body of the function. This helps make code more readable, by keeping the parameter validation code up with the parameters themselves, rather than somewhere inside the function’s body.

There are around 10 different built-in parameter validation attributes, including validation against regular expressions, a pre-defined set of values, or $null values. As a catch-all, there is also an attribute (called ValidateScript) that allows you to validate a parameter against arbitrary code. This is a truly powerful feature, which also shares a significant drawback that I will cover in more detail below. For the most part, these validation parameters work great, and are very easy to implement. While this is all nice in theory, unfortunately there are circumstances where it’s necessary to use other validation measures. Here is a mindmap that I’ve been working on, part of which contains some tidbits of information about PowerShell validation attributes:

PowerShell Mindmap - Advanced Parameters

Failing the built-in validation attributes, parameter validation can also occur within a function body, ideally in the BEGIN {…} block where it occurs before any objects are processed through the pipeline. Another powerful method of parameter validation is the use of PowerShell’s dynamic parameters.

Dynamic parameters

Benefits

Dynamic parameters are great, in theory. You can write arbitrary PowerShell code inside the code block where the parameters are defined. Perhaps the most significant benefit, among the few that there are, is the inherent ability for dynamic parameters to see the values of their siblings: static parameters. This exposes the drawback to the ValidateScript attribute that I mentioned before — although using ValidateScript with a static parameter lets you define an arbitrary PowerShell code block to validate a parameter value, it does not allow you to see values of other static parameters. See the section below titled “Example of [ValidateScript()]’s Shortcoming” for a real-world example of how I ran into this issue.

Drawbacks

As great as dynamic parameters can be, you should exercise some caution when using them. First of all, dynamic parameters simply take more effort to implement than static parameters. As a side effect of this, they are also less structured, which makes code maintenance a bit harder and more confusing. The parameter declaration itself, along with any attributes you want to apply to the parameter, must be explicitly created using the New-Object cmdlet, or compatible .NET APIs.

More importantly though, besides the fact that they’re more complicated to write, they are not discoverable in the same way that static parameters are. Given that PowerShell is all about discovery (among other things), this is an important point. Static parameters are easily discovered, as they can be easily analyzed (“reflected” to use proper .NET terminology) by the PowerShell engine prior to execution time.

Example of [ValidateScript()]’s Shortcoming

I’ll describe a situation to you, where the ability of dynamic parameters to read static parameter values during validation is important. I was writing an advanced function that takes two parameters: a WMI namespace (eg. root\cimv2), and a computer name (eg. Gandalf.mydomain.loc). My goal was to validate the existence of the WMI namespace in the parameter declaration, so that I wouldn’t have to worry about checking for its existence later on in the function body. Initially, not knowing any better, I assumed that I could use the ValidateScript attribute on the –Namespace parameter, and build a WMI path including the –ComputerName property (eg. \\$ComputerName\$Namespace:__thisNAMESPACE).

        [ValidateScript({
            if (([wmiclass]"\\${ComputerName}\root\cimv2:__ThisNamespace").__namespace -eq $_)
            {
                return $true
            }
            else
            {
                return $false
            }
        })]
        ${Namespace} = 'root\subscription'

Note: If no value is passed to a parameter, and the parameter’s default value is used, the code block in [ValidateScript()] will not execute.

I quickly found out that this wouldn’t work, because the $ComputerName parameter was not available in the ValidateScript code block. Only the current parameter was accessible using the $_ automatic variable. With this in mind, I went ahead and moved the namespace validation into the BEGIN { … } block of my function — this was easy enough, but not quite how I wanted to structure it either. It was then that I remembered that dynamic parameters could read other parameter’s values, and went back to have a read about them.

Unfortunately I don’t have an example of using dynamic parameters, since I elected to stick with validating in the function body. The main reason I chose to go that route, was because I didn’t want to lose the discoverability of parameters on this function, or the other functions I’m writing that are similar to it. If you would like to better understand dynamic parameters, please see the links in the references section below.

Conclusion

As covered in this article, we have shown how parameter validation via the [ValidateScript()] attribute is both powerful and limited. We have also explored how the use of more flexible dynamic parameters can help us overcome these challenges, with some sacrifice in discoverability and complexity.

References
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: