From Learn PowerShell Scripting in a Month of Lunches by Don Jones and Jeffery Hicks
We see a lot of people jump into PowerShell scripting much the same way they’d jump into batch files, VBScript, or Python. Nothing wrong with that – PowerShell is able to accommodate a lot of different styles and approaches, but you end up working harder than necessary unless you take a minute to understand how PowerShell really wants to work. We believe that toolmaking is the real way to use PowerShell.
What is toolmaking?
PowerShell has a strong ability to create highly reusable, context-independent tools, which it refers to as commands. Commands typically do one small thing, and they do it well. A command might not be terribly useful by itself, but PowerShell is designed to make it easy to “snap” commands together. A single LEGO block might not be all that much fun (if you’ve ever stepped on one in bare feet, you know what we mean), but a box of those blocks, when snapped together, can be amazing (hello, Death Star!). That’s the approach we take to scripting, and it’s why we use the word toolmaking to describe that approach. We believe that your effort is best spent making small, self-contained tools that can “snap on” to other tools. This approach makes your code usable across more situations, which saves you work. This approach also reduces debugging and maintenance overhead, which saves your sanity.
Before you dive into scripting and toolmaking, it’s worth having a conversation about “The Right Way To Do Things.” One of PowerShell is advantages, and biggest disadvantages, is that it’s happy to let you take a variety of approaches when you code. If you’re an old-school VBScript person, PowerShell lets you write scripts that look a lot like VBScript. If you’re a C# person, PowerShell happily runs scripts that bear a strong resemblance to C#. But PowerShell is neither VBScript nor C#; if you want to take the best advantage of it, and let it do as much heavy lifting for you as possible, you need to understand The PowerShell Way of doing things.
Think of it this way: a car is useful for getting from point A to point B, but there are many different ways in which you could do this. You could, for example, put the car in neutral, get out, and push it to point B. Your ancestors were great at walking from place to place, and if it was good enough for them, it’s good enough for you. Or, you could hitch a horse to the car, and let the horse pull it. Horses have been a great approach to transportation for centuries; why change? But the most efficient way to use the car is as it was meant to be used: fill it with gas, get in, and step on the accelerator. You’ll go faster than the horse could, you’ll expend less effort than pushing, and overall be a happier, healthier traveler.
That’s what we want to do with PowerShell. Unhitch the horse, get in the car, and go.
Writing Single-Task Tools
PowerShell is predicated on the idea of small, single purpose tools (you know them as cmdlets and functions) that you can string together in a pipelined expression to achieve amazing results with minimal effort. If you ever wrote a VBScript querying information from WMI you’ll realize how wonderful it is to run a command like this:
Get-wmiobject win32_logicaldisk -filter 'drivetype=3' -computername SRV1 | [CA] Select PSComputername,DeviceID,Size,FreeSpace
Instead of writing a twenty line VBScript.
You should embrace this principal in your own scripting and toolmaking. This is critical. Don’t try to write the mother of all tools that does six different things. Write small, single-purpose tools that do one thing. The tools you create should be no different than the PowerShell commands you get out-of-the-box.
When it comes time to name your tools, what names should you choose? A tool named QueryUserDataFromDatabase might be pretty self-explanatory, but it doesn’t fit the PowerShell model. PowerShell’s “verb-noun” naming syntax has a simple pattern:
- Start with a verb. Specifically, start with one of the approved verbs revealed by running
Get-Verb. Although honestly, we tend to use the page at https://msdn.microsoft.com/en-us/library/ms714428 instead, as it lists the verbs and provides some good examples and guidance on which one to choose. Don’t be tempted to localize verbs into a language other than English.
- For the noun, always use a singular noun: user not users.
- Prefix the noun with something meaningful to your company (and never “PS”), to help set your command apart from others. Get-GloboUser is good for a company named Globomantics, for example.
Why be this picky? Because PowerShell has a lot of code built around this naming convention, and around the specific approved verbs.
Get-Command, for example, understands the difference between a verb and a noun, and can help locate commands based on either.
Import-Module, as another example, knows the approved verb list and issues warnings when attempting to load unapproved verbs. Perhaps most importantly, all the cool kids in the PowerShell community will chuckle at you for using improperly constructed command names.
Parameter naming is even more important, believe it or not, than command naming. Parameter naming is a key to enabling commands to connect to each other in the pipeline. Parameter naming is also important for command discovery by using
Get-Command. Try the following quick quiz:
If you write a command that can connect to remote computers, what parameter name would accept those remote computer names or addresses?
If you write a command that can output to a data file, what parameter name would accept the file location and name?
If you write a command that can work over an existing PowerShell Remoting session, what parameter name might accept the session object to use?
You may need to research a bit – and that’s the point. When deciding on a parameter name, try to focus on the core, native PowerShell commands (rather than add-in modules like ActiveDirectory or something). What would they use in the same situation?
Core commands invariable use
–ComputerNamerather than an alternative like –Host, -MachineName, or something else.
Core commands are inconsistent here, but most of them use either
–Path. We’d go with a command like
Out-File, which uses
–FilePath, as our exemplar.
The core Remoting commands, like
Invoke-Command, perform this task, and they do this using a
The idea is to be consistent. Again, you’ll see how this becomes crucial when wiring up commands to connect to in the pipeline. A lot of under-the-hood stuff relies on consistent parameter naming; don’t think you’ve got a great reason to diverge from the norm.
This is an area where observing PowerShell’s native approach to things can be misleading, because a lot goes on under the hood with PowerShell output. If you’ve read Learn PowerShell in a Month of Lunches, then you know some of this; if you haven’t, we heartily recommend you do this. In brief:
PowerShell commands, produce objects as output. Objects are a form of structured data not unlike an Excel spreadsheet. An object represents a row in the sheet, and each column in the sheet is a property of the object. By referring to the property names, you can access their contents. Structured data output –objects – is at the deep core of what PowerShell is. If you ignore this maxim, your PowerShell experience will be miserable.
Objects are output and placed into the PowerShell pipeline, which ferries the objects to the next command in the pipeline. Commands need to, in many cases, accept input from the pipeline, to work in this execution model.
When the last command has output its objects to the pipeline, the pipeline carries the objects to the formatting system. At this point, the objects are still structured data. Their properties don’t appear in any particular order, and they aren’t specifically destined to be displayed in any particular way.
The formatting system, through a fairly complex set of rules we covered in Learn PowerShell in a Month of Lunches, decides how to draw an on-screen display for the objects. This involves deciding to display a list or a table, coming up with column headers, and so on.
The result of the formatting system is a bunch of specialized formatting directives, meaning the original structured data is now gone. These directives are basically useful only for drawing an on-screen display, or sending an equivalent to a text file, a printer, and other output devices.
Your tools shouldn’t be doing any of the work in step four or five. You should focus on outputting useful, structured data in the form of objects – and explicitly not worry about what the results will look like on the screen. We can’t tell you how many people we’ve seen bang their heads against their desk trying to create “attractive” output. We’re going to show you how to do that the PowerShell way, which involves educating the formatting system that fires off in step four. But for your tools, focus on getting the right data into the output, and worry not about how that will look like on the screen.
We’ve spent years teaching, writing and speaking PowerShell to IT Pros literally all over the world. If there’s one constant challenge we see people encounter, it’s making assumptions about what PowerShell is and how it should behave. To quote an ancient Greek philosopher, Epictetus
“It is impossible to begin to learn that which one thinks one already knows.”
As you work with PowerShell, especially if you have other programming or scripting experience, you’ll recognize many patterns. This is to be expected. When PowerShell was being developed the product team looked at many, many languages to adopt ideas and principles that fit the paradigm they were building. But, recognizing something that looks like Python doesn’t mean it behaves like Python. We find that the people who approach PowerShell thinking they can treat it like another language they know are the most frustrated.
We’ll leave you with this related piece of advice: don’t try to invent new ways of doing things. The whole strength of PowerShell – quite literally the entire reason for its existence – is to create a consistent administrative surface from a sea of chaos. Don’t contribute to the chaos by coming up with some novel new approach. You may think to yourself, “well, Microsoft really missed the boat on this one – I’ve got a much better way of doing this!” Stop thinking that to yourself. The goal of creating tools in PowerShell isn’t to do it better than Microsoft; it’s to remain consistent with what has come before.
If you’re interested in learning PowerShell Scripting and Toolmaking while you eat your lunch each day (or whenever you have an hour, here and there), download the free first chapter of Learn PowerShell Scripting in a Month of Lunches.