Summary: Microsoft Scripting Guy, Ed Wilson, talks about best practices for Windows PowerShell advanced functions.
Microsoft Scripting Guy, Ed Wilson, is here. This morning, I am again enjoying a cup of my Darjeeling Earl Grey tea. Today I added a bit of rose petal, lemon grass, and a cinnamon stick to the mixture. Match it with a homemade cinnamon raisin scone with a little locally sourced butter, and I am certainly in the mood to work on Scripting Guy blog posts.
I was up last night reading Piers Plowman in the original old English. The poem (written more than 650 years ago) is certainly interesting, and it has inspired lots of other writers. It can be interpreted primarily as a political document, and many of the passages deal with the difference between what is said, what is done, and what is ideal.
So what does that have to do with Windows PowerShell? Well when dealing with Windows PowerShell best practices, there is often a difference between what is said, what is done, and what is ideal. But in contrast to Piers Plowman, these differences are more a function of expedience than of nefarious intent.
When talking about Windows PowerShell advanced functions, there is a perceived disconnect between the assumed complexity, and the potential benefit of such an exercise. I do not think that an advanced function needs to implement every potential feature, such as the 77-line advanced function snip here:
There are some things, however, that should happen in an advanced function: comment-based Help is the minimum. Named parameters that use type constraints is another. Structured error handling is a good one to also implement. One of the reasons for writing an advanced function is that it is perfect for adding to a module.
Here are some best practices for advanced functions:
- All advanced functions should implement a minimal level of comment-based Help. The minimum items to add are the Synopsis, Description, and Example nodes.
- All advanced functions should implement named parameters. At a minimum you should provide types for the parameters. Parameter validation should be used to simplify error handling when it makes sense.
- You should consider adding parameter aliases if your parameter names are long.
- The examples you use in your comment-based Help should be examples that actually work in a wide variety of environments.
- You should implement structured error handling in advanced functions by using Try/Catch and Finally.
- In your Finally block, you should set your environment back to its base configuration—that is, remove any changes you added.
- You should use cmdlet binding and add support for the –debug parameter at a minimum.
- Consider adding support for the –verbose and –whatif parameters if it makes sense for your situation.
- Give your advanced function a good descriptive Verb-Noun name.
- Consider creating aliases for your advanced function that will make it easier to use.
- If you have a group of related advanced functions, consider adding them to a module.
- In your module, create a letter range for your aliases, so all are three, four, or five letters that make sense. You can use a letter for each major syllable, such as GPS for Get-Process.
- If you create a module and write an advanced function that does something, consider also writing a function that reports that something, and that undoes the thing that was done. This would typically include the verbs Get, New, and Remove.
- When picking out verbs, always use standard verbs. Use Get-Verb to see what verbs are available. Refer to system cmdlets for examples of usage and follow the Windows PowerShell team examples.
- Always return an object from your advanced function, not only text.
- If you create a module with your advanced functions, make sure you also create a module manifest.
- Make sure your script is lined up, indented properly, and easy to read. If you can read and understand your script, you will simplify your debugging process.
- Make sure your script fits on a single screen without scrolling. Keep in mind that script that is too wide is difficult to read and to debug.
- Use regions to simplify debugging.
- Select and run script in sections when debugging a complicated function.
- Make sure you test your function in a pristine environment with no dependencies on profiles, admin rights, and versions to ensure the greatest compatibility.
- If you are unsure if your function runs in a down-level environment, use the #Requires tag for your current version of Windows PowerShell.
- Use Strict mode for your version of Windows PowerShell, and ensure that if you do, it matches the version set for #Requires.
- Test your function in a non-elevated environment to see if it requires elevation. If it does, use the Test-IsAdmin function to verify that the environment is elevated.
- Create a clean virtual machine for various versions of the operating system, and test your function there to see if there are operating system dependencies in addition to Windows PowerShell version dependencies.
That is all there is to using advanced functions in Windows PowerShell. It also closes Windows PowerShell Best Practices Week. Join me tomorrow when I will have a guest blog post written by Gary Jackson, who will talk about automating user creation.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at firstname.lastname@example.org, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy