From Learn PowerShell Scripting in a Month of Lunches by Don Jones and Jeffery Hicks

This article is about learning to draw the line between two equally important kinds of script – tools and controllers. Specific techniques are suitable for tools, and different ones are suitable for controllers. Each set of techniques is designed to reduce your workload, debugging, maintenance, and increase readability and reusability. Knowing which kind of script that you’re writing helps direct you to the right set of techniques, and this is the key to being a successful scripter and toolmaker!


Save 37% on Learn PowerShell Scripting in a Month of Lunches. Just enter code fccjones into the discount box at checkout at manning.com.


Tools vs. controllers

Think about a hammer. A hammer is a tool, and it’s probably one you’ve at least seen before, even if you’ve never wielded one. A hammer is a self-contained thing; it only does one thing: strike other things. A hammer has no context about its life, and no clue about its destiny. A hammer may be used one day to help build a house, another day to break a window, and another day to smash your thumb. A hammer, sitting alone in a toolbox, is useless. It takes up space, and doesn’t do anything.

You, in this analogy, are a controller. You give the hammer meaning, and a purpose. You give it context. You decide if it strikes a nail, or someone’s head. You give the hammer input – how hard it’s being swung or what it’s being swung at. You take the hammer’s output, like how loud a noise it makes, and you do something with it, like decide to go buy earplugs.

This is how things are meant to work in PowerShell. What PowerShell calls a command – a catchall word referring to cmdlets, functions, and other executable artifacts – we call a tool. A tool should do one thing and one thing only. This is why we have tools named Get-Process, Start-Process, and Stop-Process– each of them does one thing, and one thing only. We don’t have a tool called Manage-Processes, capable of starting, stopping, or listing processes depending on the parameters you provide. Such a super-tool goes against the PowerShell ethos of single-task-ed-ness.

Think about Stop-Process. What good is it? No good at all on its own. Like a hammer, it needs to be given context and purpose. It needs to be controlled. When used as part of a controller script, the tool gains meaning and purpose.

Thinking about tools

Tools have important characteristics in the PowerShell world:

  • Tools do one thing, which should be described by the verb portion of their name. It’s better to make five small tools which each do one thing than it is to make one big tool which does five things. Smaller, more tightly scoped tools are easier to write, easier to test, and easier to debug and maintain.

  • Tools don’t know where their input data has generated, any more than a hammer knows in advance if it’ll be held in a hand, or duct-taped to some robotic contraption. Tools accept all input only from their parameters, as a hammer accepts input only from its handle. (Yeah, we’re playing pretty loose with the metaphor, but you get the idea.) Other tools may be used to create the input, which is then fed to a tool’s parameters.

  • Tools don’t know how their output will be used, and they don’t care, any more than a hammer cares if it hits a nail or a thumb. Tools don’t worry about making their output pretty – other tools can handle that. Tools don’t worry about where their output goes – again, other tools can handle that.

We tend to informally think about several different types of tools. This isn’t a strict taxonomy, but it helps give you an idea of how they can relate to one another.

  • Input tools are designed to create data which is primarily consumed by other tools. You might write a tool that gets a bunch of computer names from a database, for example. Get is a common verb for input tool names, but you’ll also see Import and ConvertFrom.

  • Action tools usually require some additional input before they do something – and that “something” can be anything you imagine. You know plenty of commands with verbs like Set and Remove.

  • Output tools are usually designed to take the output of an input tool or an action tool, and render it for some specific purpose. They might create a specially formatted data file or render a particular kind of on-screen display. Verbs like Out, Format, ConvertTo, and Export are common for output tools.

Imagine that you have some line-of-business application that tracks customer records. You’ve been asked to write a script which generates a list of customers whose records have gone for a year or more without any activity. That list has to be formatted in a CSV file which can be fed to other processes, and in an HTML report that can be posted on the company intranet. How many tools do you need to write? You must start by thinking of the discrete tasks involved, and look to see what tasks are already solved by a PowerShell tool.

  • You’ll have to write a Get-CustomerRecord tool. Its output should include a date of each customer’s last activity, plus whatever other data is needed for that CSV file and that HTML report. You’ll probably be including data like customer name, last activity date, ID number, and so on.

  • You’ll need a way to filter the results of Get-CustomerRecord to only those customers who’ve had no activity for a year. Fortunately, the native Where-Object command can do that, and you shouldn’t need to write a thing. Although depending on the code you’re running to query the customer information, if there’s any way to filter or limit data as it’s collected, that’d be preferable.

  • You’ll need to convert those results to CSV and save to a file, and the native Export-CSV command can do that for you – no work for you, here!

  • You’ll also need a way to make an HTML report, and if the native ConvertTo-HTML command isn’t sufficient, then the EnhancedHTML2 module from PowerShellGallery.com includes ConvertTo-EnhancedHTML, which should do the trick. You’ll need to learn to use it, but you won’t have to code anything.

For all of that, you only need to write one tool. This is the beauty of the tool-based approach: many great, generic tools already exist in PowerShell, and out in the broader world, that you often only need to focus on the stuff which is entirely specific to your environment. Do that the right way, and your custom tools will connect seamlessly to everything that already exists.

But your prospective Get-CustomerRecord tool is useless by itself. It needs to be given purpose, and a context. It needs a controller.

Thinking about controllers

While tools are generic and lack context, controllers are all about context. The purpose of a controller is to put a tool to a specific use, in a specific kind of situation. We don’t use command-style, verb-noun names for controllers; we give them more English-like names. For example, CreateStaleCustomerHTMLReport.ps1 is the script we might create to generate that HTML report of customers who’ve been inactive for a year or more. That script might be really simple, containing only a single pipeline:

 
 Get-CustomerRecord |
 Where-Object { $_.LastActivity –lt (Get-Date).AddDays(-365) } |
 ConvertTo-HTML |
 Out-File \\intranet\www\reports\inactive-customers.html
 

It’s not a complex script, and that’s the idea. Controllers often are simple, because they’re just stringing together some tools. None of these tools knew beforehand that they’d be involved in creating HTML customer reports, but this controller gave them purpose. We’d probably have another one, CreateStaleCustomerCSVDataFile.ps1, that would take care of generating the required CSV data file. Just for fun, we might also create DisplayStaleCustomers.ps1, which simply queries inactive customers and formats the output for an attractive on-screen display. It never hurts to go above and beyond!

Like tools, controllers have some specific characteristics:

  • A controller is tied to a context. It automates a business process, interacts with a human being, or some other situational-specific thing.

  • A controller often has hardcoded data, such as a filename that will be read as input, or a database connection string that will give output a place to go.

  • A controller is responsible for putting its output into a particular form, which may not be structured data. For example, a controller may display information on-screen, or send it to a printer.

  • While a tool performs a task, a controller solves a problem.

People often ask us about writing “graphical scripts” in PowerShell, using either .NET Framework’s Windows Forms library or its newer Windows Presentation Framework (WPF) library. You can do it, and we consider such scripts to be controllers. They should contain minimal code, and mainly rely on running tools. They put those tools to a specific purpose, tied to the eyes and fingers of human beings.

Controllers from Commands – If you look at the sample controller script, it is just a PowerShell command. Your “controller” can simply be you typing a command interactively in the console. This is a great way to make sure your tool does what you intend. Putting the commands in a controller script saves a ton of typing and makes running your command consistent. A controller script can also be a bit more complex if you need it to be.

Comparing tools and controllers

Think about an automotive assembly line. These days, they’re largely staffed by very specialized robots. One robot paints the car; another one welds two pieces together. Those robots are tools: in a warehouse all by themselves, they’re useless. It’s when you add a controller – the production line itself, which places the robots in sequence and coordinates their activities – that you have something useful.

Table 1 Comparing tools and controllers

Tools

Controllers

Do one thing and one thing only

Connect multiple tools

Accept input on parameters

May have hardcoded input, and may use tools to retrieve data which will be fed to other tools

Produce data as objects

May produce any kind of output, including formatted data, special files, and so on

Complete a task

Solve a problem or meet a need

Are often useless or minimally useful on their own

Are self-contained

Are useable across a variety of situations

Are used only for a specific situation

Hopefully, this article has gotten you thinking about the most important top-level element of scripting: what kind of script to make. Hopefully, you’re starting to think about tools and controllers in the right way, and you’re starting to see how they work together to accomplish business tasks. If you can truly embrace the distinction between the two, and respect their individual purposes, then you’ll absolutely be set to succeed in PowerShell scripting.

If you’re interested in learning a lot more about PowerShell Scripting and Toolmaking while you eat your lunch each day (or whenever you have an hour, here and there),check out Learn PowerShell Scripting in a Month of Lunches on liveBook and see this Slideshare presentation.