The Powershell Scripting And Toolmaking Book Forever Edition Don Jones pdf download
The Powershell Scripting And Toolmaking Book Forever Edition Don Jones pdf download
https://ebookbell.com/product/the-powershell-scripting-and-
toolmaking-book-forever-edition-don-jones-43639080
https://ebookbell.com/product/the-powershell-scripting-and-toolmaking-
book-forever-edition-20200630-don-jones-jeff-hicks-22511552
The Powershell Scripting And Toolmaking Book Don Jones Jeff Hicks
https://ebookbell.com/product/the-powershell-scripting-and-toolmaking-
book-don-jones-jeff-hicks-29875700
The Powershell Scripting And Toolmaking Book Don Jones Jeff Hicks Don
Jones Jeff Hicks
https://ebookbell.com/product/the-powershell-scripting-and-toolmaking-
book-don-jones-jeff-hicks-don-jones-jeff-hicks-29875704
The Powershell Scripting And Toolmaking Book Don Jones Jeff Hicks Don
Jones And Jeff Hicks
https://ebookbell.com/product/the-powershell-scripting-and-toolmaking-
book-don-jones-jeff-hicks-don-jones-and-jeff-hicks-29875714
The Powershell Scripting And Toolmaking Book Leanpubcom Don Jones Jeff
Hicks
https://ebookbell.com/product/the-powershell-scripting-and-toolmaking-
book-leanpubcom-don-jones-jeff-hicks-11236190
https://ebookbell.com/product/powershell-essential-guide-master-the-
fundamentals-of-powershell-scripting-and-automation-english-edition-
jayaram-54937054
https://ebookbell.com/product/the-definitive-guide-to-powershell-
advanced-scripting-and-automation-solutions-for-modern-it-
environments-and-operations-wesley-dunne-58262498
https://ebookbell.com/product/windows-powershell-cookbook-the-
complete-guide-to-scripting-microsofts-command-shell-3rd-edition-lee-
holmes-49431868
https://ebookbell.com/product/powershell-cookbook-your-complete-guide-
to-scripting-the-ubiquitous-objectbased-shell-4th-edition-lee-
holmes-33990128
The PowerShell Scripting and Toolmaking
Book
Forever Edition
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Dedication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
Foreword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vi
Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viii
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
Pre-Requisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
Versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
The Journey . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x
Following Along . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x
Providing Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x
Our Take . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Comment-Based Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Where to Put Your Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Going Further with Comment-Based Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Broken Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Your Turn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Let’s Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Handling Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Understanding Errors and Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Bad Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Two Reasons for Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Handling Exceptions in Our Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Handling Exceptions for Non-Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Going Further with Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Deprecated Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Your Turn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Let’s Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Basic Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Two Kinds of Bugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
The Ultimate Goal of Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Developing Assumptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Debugging Tool 1: Write-Debug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Debugging Tool 2: Set-PSBreakpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Debugging Tool 3: The PowerShell ISE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Debugging Tool 4: VS Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Your Turn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Let’s Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
It Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
Mocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
Where to Mock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
How to Mock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
Verifiable Mocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
Parameter Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
Mocking the Unmockable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
If you purchased this book, thank you. Know that writing a book like this takes hundreds of hours
of effort, during which we’re not making any other income. Your purchase price is important to
keeping a roof over our families’ heads and food on our tables. Please treat your copy of the book as
your own personal copy - it isn’t to be uploaded anywhere, and you aren’t meant to give copies to
other people. We’ve made sure to provide a DRM-free file (excepting any DRM added by a bookseller
other than LeanPub) so that you can use your copy any way that’s convenient for you. We appreciate
your respecting our rights and not making unauthorized copies of this work.
If you got this book for free from someplace, know that you are making it difficult for us to write
books. When we can’t make even a small amount of money from our books, we’re encouraged to
stop writing them. If you find this book useful, we would greatly appreciate you purchasing a copy
from LeanPub.com or another bookseller. When you do, you’ll be letting us know that books like
this are useful to you, and that you want people like us to continue creating them.
Please note that this book is not authorized for classroom use unless a unique copy has been
purchased for each student. No-one is authorized or licensed to manually reproduce the PDF
version of this book for use in any kind of class or training environment.
¹https://leanpub.com
About This Book ii
This book is copyrighted (c)2017-2020 by Don Jones and Jeffery Hicks, and all rights are reserved.
This book is not open source, nor is it licensed under a Creative Commons license. This book is not
free, and the authors reserve all rights.
Dedication
This book is fondly dedicated to the many hardworking PowerShell users who have, for more than
a decade, invite us into their lives through our books, conference appearances, instructional videos,
live classes, and more. We’re always humbled and honored by your support and kindness, and you
inspire us to always try harder, and to do more. Thank you.
Acknowledgements
Thanks to Michael Bender, who has selflessly provided a technical review of the book. Any
remaining errors are, of course, still the authors’ fault, but Michael has been tireless in helping
us catch many of them.
About the Authors
Don Jones received the Microsoft MVP Award recipient for 16 consecutive years for his work with
Windows PowerShell and administrative automation. He has authored dozens of books, articles,
white papers, and instructional videos on information technology, and today is a Vice President
in the Content team at Pluralsight.com. Don was also a co-founder of The DevOps Collective²,
which offers IT education programs, scholarships, and which runs PowerShell.org and PowerShell
+ DevOps Global Summit³ and other DevOps- and automation-related events.
Don’s recent writing focuses on business, instructional design, self-improvement, and fiction, and
can be found at http://leanpub.com/u/donjones⁴. You can follow Don on Twitter @concentratedDon.
He blogs at DonJones.com.
Jeff Hicks is a grizzled IT veteran with almost 30 years of experience, much of it spent as an
IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in
automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award. He works
today as an independent author, teacher and consultant. Jeff has taught and presented on PowerShell
and the benefits of automation to IT Pros worldwide for over a decade. Jeff has authored and co-
authored a number of books, writes for numerous online sites, is a Pluralsight author, and a frequent
speaker at technology conferences and user groups world-wide.
You can keep up with Jeff on Twitter (@JeffHicks) and on his blog at https://jdhitsolutions.com⁵.
Additional Credits
Technical editing has been helpfully provided not only by our readers, but by Michael Bender. We’re
grateful to Michael for not only catching a lot of big and little problems, but for fixing most of them
for us. Michael rocks, and you should watch his Pluralsight videos. However, anything Michael
didn’t catch is still firmly the authors’ responsibility.
²https://devopscollective.org
³https://events.devopscollective.org/
⁴http://leanpub.com/u/donjones_
⁵https://jdhitsolutions.com
Foreword
After the success of Learn PowerShell in a Month of Lunches, Jeff and I wanted to write a book that
took people down the next step, into actual scripting. The result, of course, was Learn PowerShell
Toolmaking in a Month of Lunches. In the intervening years, as PowerShell gained more traction
and greater adoption, we realized that there was a lot more of the story that we wanted to tell. We
wanted to get into help authoring, unit testing, and more. We wanted to cover working with different
data sources, coding in Visual Studio, and so on. These were really out of scope for the Month of
Lunches series’ format. And even in the “main” narrative of building a proper tool, we wanted to go
into more depth. So while the Month of Lunches book was still a valuable tutorial in our minds, we
wanted something with more tooth.
At the same time, this stuff is changing really fast these days. Fast enough that a traditional
publishing process - which can add as much as four months to a book’s publication - just can’t keep
up. Not only are we kind of constantly tweaking our narrative approach to explaining these topics,
but the topics themselves are constantly evolving, thanks in part to an incredibly robust community
building add-ons like Pester, Platyps, and more.
So after some long, hard thinking, we decided to launch this effort. As an Agile-published book on
LeanPub, we can continuously add new content, update old content, fix the mistakes you point out
to us, and so on. We can then take major milestones and publish them as “snapshots” on places like
Amazon, increasing the availability of this material. We hope you find the project as exciting and
dynamic as we do, and we hope you’re generous with your suggestions - which may be sent to us
via the author contact form from this book’s page on LeanPub.com. We’ll continue to use traditional
paper publishing, but through a self-publishing outlet that doesn’t impose as much process overhead
on getting the book in print. These hard copy editions will be a “snapshot” or “milestone edition” of
the electronic version.
It’s important to know that we still think traditional books have their place. PowerShell Scripting in
a Month of Lunches, the successor to Learn PowerShell Toolmaking in a Month of Lunches, covers
the kind of long-shelf-life narrative that is great for traditionally published books. It’s an entry-level
story about the right way to create PowerShell tools, and it’s very much the predecessor to this book.
If Month of Lunches is about getting your feet under you and putting them on the right path, this
book is about refining your approach and going a good bit further on your journey.
Toolmaking, for us, is where PowerShell has always been headed. It’s the foundation of a well-de-
signed automation infrastructure, of a properly built DSC model, and of pretty much everything else
you might do with PowerShell. Toolmaking is understanding what PowerShell is, how PowerShell
wants to work, and how the world engages with PowerShell. Toolmaking is a big responsibility.
My first job out of high school was as an apprentice for the US Navy. In our first six weeks, we
rotated through various shops - electronics, mechanical, and so on - to find a trade that we thought
Foreword vii
we’d want to apprentice for. For a couple of weeks, I was in a machine shop. Imagine a big, not-
climate-controlled warehouse full of giant machines, each carving away at a piece of metal. There’s
lubrication and metal chips flying everywhere, and you wash shavings out of yourself every evening
when you go home. It was disgusting, and I hated it. It was also boring - you set a block of metal
into the machine, which might take hours to get precisely set up, and then you just sat back and
kind of watched it happen. Ugh. Needless to say, I went into the aircraft mechanic trade instead.
Anyway, in the machine shop, all the drill bits and stuff in the machine were called tools and dies.
Back in the corner of the shop, in an enclosed, climate-controlled room, sat a small number of nicely-
dressed guys in front of computers. They were using CAD software to design new tools and dies for
specific machining purposes. These were the tool makers, and I vowed that if I was ever going to
be in this hell of a workplace, I wanted to be a toolmaker and not a tool user. And that’s really the
genesis of this book’s title. All of us - including the organizations we work for - will have happier,
healthier, more comfortable lives as high-end, air-conditioned toolmakers rather than the sweaty,
soaked, shavings-filled tool users out on the shop floor.
Enjoy!
Don Jones
Feedback
We’d love your feedback. Found a typo? Discovered a code bug? Have a content suggestion? Wish
we’d answered a particular question? Let us know.
Zeroth, make sure you’re not using Leanpub’s online reader, as it omits some of the front matter
from the book. Use the PDF, EPUB, or MOBI version, or a printed edition you bought someplace
else.
First, please have a chapter name, heading reference, and a brief snippet of text for us to refer to.
We can’t easily use page numbers, because our source documents don’t have any.
Second, understand that due to time constraints like having full-time jobs, we can’t personally
answer technical questions and so forth. If you have a question, please hop on the forums at
PowerShell.org⁶, where we and a big community of enthusiasts will do our best to help.
Third, keep in mind that the EPUB and MOBI formats in particular allow little control over things
like code formatting. So we can’t usually address those for you.
Then, head to the LeanPub website and use their email link⁷ to email us. We can’t always reply
personally to every email, but know that we’re doing our best to incorporate feedback into the
book.
Finally, accept our thanks!
⁶http://powershell.org
⁷https://leanpub.com/powershell-scripting-toolmaking/email_author/new
Introduction
Pre-Requisites
We’re assuming that you’ve already finished reading an entry-level tutorial like, Learn Windows
PowerShell in a Month of Lunches, or that you’ve got some solid PowerShell experience already
under your belt. Specifically, nothing on this list should scare you:
If you’ve already done things like written functions in PowerShell, that’s marvelous - but, you may
need to be open to un-learning some things. Some of PowerShell’s best practices and patterns aren’t
immediately obvious, and especially if you know how to code in another language, it’s easy to go
down a bad path in PowerShell. We’re going to teach you the right way to do things, but you need
to be willing to re-do some of your past work if you’ve been following the Wrong Ways.
We also assume that you’ve read PowerShell Scripting in a Month of Lunches, a book we wrote for
Manning. It provides the core narrative of “the right way to write PowerShell functions and tools,”
this book essentially picks up where that one leaves off. Look for that book in late 2017 from Manning
or your favorite bookseller. Part 1 of this book briefly slams through this “the right way” narrative
just to make sure you’ve got it in your mind, but the Month of Lunches title really digs into those
ideas in detail.
Versioning
This book is primarily written against Windows PowerShell v5/v5.1 running on Microsoft Windows.
In January 2018, Microsoft announced the General Availability of PowerShell Core 6.0, which
is a distinct cross-platform “branch” of PowerShell. This branch has now become PowerShell 7,
which was released in early 2020. As far as we can tell, everything we teach in this book applies
to PowerShell 7, too - although some of our specific examples may still only work on Windows
PowerShell, the concepts and techniques are applicable to PowerShell 7. However, PowerShell 7
includes some new scripting features which we’ll cover in a dedicated chapter or two.
Introduction x
The Journey
This book is laid out into seven parts:
Following Along
We’ve taken some pains to provide review Q&A at the end of most chapters, and to provide lab
exercises (and example answers) at the end of many chapters. We strongly, strongly encourage you
to follow along and complete those exercises - doing is a lot more effective than just reading. And
if you get stuck, hop onto the Q&A forums on PowerShell.org and we’ll try and unstick you. We’ve
tried to design the labs so that they only need a Windows client computer - so you won’t need a
complex, multi-machine lab setup. Of course, if you have more than one computer to play with,
some of the labs can be more interesting since you can write tools that query multiple computers
and so forth. But the code’s the same even if you’re just on a single Windows client, so you’ll be
learning what you need to learn.
Providing Feedback
Finally, we hope that you’ll feel encouraged to give us feedback on this book. There’s a “Contact
the Authors” form on this book’s page⁸ on LeanPub.com, and you’re also welcome to contact us on
Twitter @concentratedDon and @JeffHicks. You can also post in the Q&A forums on PowerShell.org,
which frankly is a lot easier to respond to than Twitter. If you purchased the “Forever Edition” of
this book on LeanPub, then you’ll see us incorporating suggestions and releasing a new build of the
book all the time. If you obtained the book elsewhere, we can’t turn your purchase into a LeanPub
account for you. However, when the book changes enough for us to publish a new “edition” to other
booksellers, that might be a time to pick it up on LeanPub instead, provided you understand the
“Agile publishing” model and are comfortable with it.
⁸http://leanpub.com/powershell-scripting-toolmaking
A Note on Code Listings
The code formatting in this book only allows for about 60-odd characters per line. We’ve tried our
best to keep our code within that limit, although sometimes you may see some awkward formatting
as a result.
For example:
Here, you can see the default action for a too-long line - it gets word-wrapped, and a backslash
inserted at the wrap point to let you know. We try to avoid those situations, but they may sometimes
be unavoidable. When we do avoid them, it may be with awkward formatting, such as using
backticks (‘):
Here, we’ve given up on neatly aligning everything to prevent a wrap situation. Ugly, but oh well.
You may also see this crop up in inline code snippets, especially the backslash.
If you are reading this book on a Kindle, tablet or other e-reader, then we hope you’ll
understand that all code formatting bets are off the table. There’s no telling what the
formatting will look like due to how each reader might format the page. We trust you know
enough PowerShell to not get distracted by odd line breaks or whatever.
When you write PowerShell code, you should not be limited by these constraints. There’s no reason
for you to have to use a backtick to “break” a command. Simply type out your command. If you
want to break a long line to make it easier to read without a lot of horizontal scrolling, you can hit
Enter after any of these characters:
• Open parenthesis (
• Open curly brace {
• Pipe |
• Comma ,
• Semicolon ;
A Note on Code Listings xii
• Equal sign =
This is probably not a complete list, but breaking after any of these characters makes the most sense.
Anyway, we apologize for these artifacts. Keep in mind that you can, and should, use Install-Module
PowerShell-Toolmaking to download and install the code samples from the PowerShell Gallery.
They’ll end up in \Program Files\WindowsPowerShell\Modules\PowerShell-Toolmaking, typically
broken down by chapter. We try to update that download often, so if you don’t have the latest
version installed, do that.
Lab Setup
We hope that you plan to follow along with us in this book, and to help you do so we’ve provided
hands-on exercises at the end of most chapters. To complete those, you won’t need much of a lab
environment - just a Windows 10 (or later) computer to which you have Administrator access, and
which has Internet connectivity. Business editions (not “Home”) of Windows are recommended.
We’ve built the labs so that there’s no need for a domain controller, servers, or anything else.
We assume that your lab computer (or virtual machine) will have Internet access.
1. On your Windows computer, press Windows+R, type powershell, and press Enter.
2. Right-click the PowerShell icon on the Task Bar, and select Run as Administrator.
3. Click Yes.
4. In the new PowerShell window (which must say “Administrator: Windows PowerShell” in the
title bar), type Install-Module PowerShell-Toolmaking and press Enter.
5. You may be notified that “NuGet provider is required to continue.” Type Y and press Enter.
6. You may be notified of an “Untrusted repository.” Type Y and press Enter.
7. Type Set-ExecutionPolicy Bypass and press Enter.
8. Type Y and press Enter.
If the installation fails, or if you see an error or warning when setting the execution policy, ensure
that PowerShell is running as Administrator and that the computer has unrestricted Internet access.
On a company-owned computer, restrictions may be in place that prevent the installation of files or
the changing of the execution policy. You will need to consult your company’s IT administrators to
remedy that.
If you are new to scripting, you may also want to install the free Visual Studio Code, often referred to
as VS Code, from https://code.visualstudio.com/¹⁰. The PowerShell ISE that ships with Windows 10
isn’t going away, and we used it a lot to develop the material in this book. But it should be considered
deprecated. Microsoft is no longer developing it. All of their efforts are directed toward VS Code.
This product is updated frequently and has a rich ecosystem of extensions. In fact, after you install
it and launch it, click the gear icon in the lower left and then Extensions. Search for powershell and
install the extension.
We won’t lie. VS Code has a learning curve. But if you are new to scripting, you most likely would
have to learn the PowerShell ISE. If you have to spend time learning a tool you might as well learn
the tool that will be around for a while and as a bonus works cross-platform!
¹⁰https://code.visualstudio.com
¹¹https://www.manning.com/books/learn-windows-powershell-in-a-month-of-lunches-third-edition
Part 1: Review PowerShell
Toolmaking
This first Part of the book is essentially a light-speed refresher of what PowerShell Scripting in a
Month of Lunches covers. If you’ve read that book, or feel you have equivalent experience, then this
short Part will help refresh you on some core terminology and techniques. If you haven’t… well, we
really recommend you get that fundamental information under your belt first.
Functions, the Right Way
This chapter is essentially meant to be a warp-speed review of the material we presented in the core
narrative of Learn PowerShell Toolmaking in a Month of Lunches (and its successor, PowerShell
Scripting in a Month of Lunches). This material is, for us, “fundamental” in nature, meaning it
remains essentially unchanged from version to version of PowerShell. Consider this chapter a kind
of “pre-req check;” if you can blast through this, nodding all the while and going, “yup,” then you’re
good to skip to the next Part this book. If you run across something where you’re like, “wait, what?”
then a review of those foundational, prerequisite books might be in order, along with a thorough
reading of this Part of this book.
By the way, you’ll notice that our downloadable code samples for this book (the “PSTool-
making” module in PowerShell Gallery) contain the same code samples as the core “Part 2”
narrative from PowerShell Scripting in a Month of Lunches. Those code samples also align
to this book, and we use them in this chapter as illustrations.
Tool Design
We strongly advocate that you always begin building your tools by first designing them. What
inputs will they require? What logical decisions will they have to make? What information will
they output? What inputs might they consume from other tools, and how might their output be
consumed? We try to answer all of these questions - often in writing! - up front. Doing so helps us
think through the ways in which our tool will be used, by different people at different times, and to
make good decisions about how to build the tool when it comes time to code.
I need a PowerShell script that will check a complete DFS Root, and report all targets and
access based enumeration for each. I then need the script to check all NTFS permissions
on all the targets and list the security groups assigned. I then need this script to search 4
domains and report on the users in these groups.
Functions, the Right Way 3
And yup - that’s what “Start with a Command” means. We’d probably start by planning that out -
inputs are clearly some kind of DFS root name or server name, and an output path for the reports
to be written. Then the discovery process would begin: how can PowerShell connect to a DFS root?
How can it enumerate targets? How can it resolve the target physical location and query NTFS
permissions? Good ol’ Google, and past experience, would be our main tool here, and we wouldn’t
go an inch further until we had a text file full of answers, sample commands, and notes.
If you’re really reading that DFS example, we’d probably stop our function at the point
where it gets the permissions on the DFS targets. The results of that operation could be used
to unwind the users who were in the resulting groups - a procedure we’d write as a separate
tool, in all likelihood.
Functions, the Right Way 4
Comment-Based Help
We’d definitely “dress up” our code using comment-based help, if not full help (we cover that later
in the book). We’d make sure to provide usage examples, documentation for each parameter, and a
pretty detailed description about what the tool did.
Handling Errors
Finally, and again if we hadn’t habitually done so already, we’d anticipate errors and try to handle
them gracefully. “Permission Denied” querying permissions on a file? Handled - perhaps outputting
an object, for that file, indicating the error.
unit tests. This also means when someone reports a bug in your code, it will be easier to find and
resolve.
For example, suppose you decide to design a tool that will query a number of remote computers.
Within that tool, you might decide to implement a check to make sure each computer is online and
reachable, perhaps by pinging it with Test-NetConnection. It makes sense in your head. But it might
be a bad idea. First, your tool is now doing two things: querying whatever it is you’re querying, but
also pinging computers. That’s two distinct sets of functionality. The pinging part, in particular, is
likely to be code you’d use in many different tools, suggesting it should, in fact, be its own tool.
Having the pinging built into the same querying tool will make testing harder, too, because you’ll
have to explicitly write tests to make sure that the pinging part works the way it’s supposed to.
An alternate approach would be to write that “Test-PCConnection” functionality as a distinct tool.
This code could use existing PowerShell commands wrapped up with features that make sense
in your environment. So, if your “querying” tool is something like “Get-SystemData,” you might
concoct a pattern like:
The idea being that Test-PCConnection would filter out whatever computers weren’t reachable,
perhaps logging the failed ones in some fashion, so that Get-SystemData could just focus on its one
job of querying something. Both tools would then become easier to independently test, since they’d
each have a tightly scoped set of functionality.
If you needed to take this a step further, say by automating this task for the help desk, you
can put this pattern into a control script. Which is something we’ll cover later.
You also want to avoid building functionality into your tools that will be difficult to test. For example,
you might decide to implement some error logging in a tool. That’s great - but if that logging is
going to a SQL Server database, it’s going to be trickier to test and make sure that the logging is
working as desired. Logging to a file might be easier, since a file would be easier to check. An even
better approach would be to write a separate tool that handles logging. You could then test that tool
independently, and simply use it within your other tools. This gets back to the idea of having each
tool do one thing, and one thing only, as a good design pattern.
if you can” and “don’t worry about where they come from.” You want to enable, from a design
perspective, a variety of approaches.
It can help to actually sit down and write some examples of using your command that you intend to
work. These can become help file examples later, but in the design stage can help make sure you’re
designing to allow all of these. For example:
That third example is going to require some careful design, because you’re not going to be able to
pipe an Active Directory Computer object to the same -ComputerName parameter that also accepts a
String object from Get-Content! You may have identified a need for two parameter sets, perhaps one
using -ComputerName <string[]> and another using -InputObject <ADComputer> to accommodate
both scenarios. Now, creating two parameter sets is going to make the coding, and the automated
unit testing, a bit harder - so you’ll need to decide if the tradeoff is worth it. Will that third example
be used so frequently that it justifies the extra coding and test development? Or will it be a rare
enough scenario that you can exclude it, and instead rely on the similar second example?
The point is that every design decision you make will have downstream impact on your tool’s code,
its unit tests, and so on. It’s worth thinking about those decisions up front, which is why it’s called
the design phase!
When you run Get-Verb in PowerShell 7, Microsoft has added a description for each verb.
Don’t beat yourself up too hard over fine distinctions between approved verbs, like the difference
between Get and Read. If you check out that website, you’ll realize that Get-Content should probably
be Read-Content; likely a distinction Microsoft came up with after Get-Content was already in the
wild.
The noun portion of your name should be singular, although we have been known to bend
this rule from time to time. What you should consider is the potential for a naming conflict.
Yes, there are ways to call the right PowerShell command but it can be a bit cumbersome so
why not avoid it altogether. We suggest you get in the habit of using a short prefix on your
command’s noun. For example, if you work for DonCo, Inc., then you might design commands
named Get-DCISystemStatus rather than just Get-SystemStatus. The prefix helps prevent your
command name from conflicting with those written by other people and it will make it easier to
discover and identify commands and tools created for your organization.
Parameter names should also follow native PowerShell patterns. Whenever you need a parameter,
take a look at native PowerShell commands and see what parameter name they use for similar
purposes. For example, if you need to accept computer names, you should use -ComputerName (notice
it’s singular!) and not some variation like -MachineName. If you need a filename, that’s usually
-FilePath or -Path on most native commands. Later in the book we’ll show you how to create a
parameter alias. This let’s you use the official and proper parameter name, like -Computername but
still let users of your tool use something like -Server.
An Example
Before we even start thinking about our design decisions, we like to review the business requirements
for a new tool. We try to translate those business requirements to usage examples, so it’s clearer to us
how a tool might be used. If there are other stakeholders to involve - such as the people who might
consume this tool, once it’s done - we get them to sign off on this “functional specification,” so that
we can go into the design phase knowing with clear, mutual expectations for the new tool. We also
try to capture problem statements that this new tool is meant to solve, because those sometimes
offer a clearer business perspective than a specification that someone else may have written. The
discussion might go something like this:
We’ve been managing servers in our data center with PowerShell and PowerShell remoting.
With the arrival of PowerShell 7, we will be able to use SSH for PowerShell remoting.
We won’t switch every server to SSH because some servers may rely on Just Enough
Administration. We need to inventory servers and identify which servers have PowerShell
remoting enabled. And if it is enabled, is it also configured to use SSL. Long term, we need
to move servers that must use WSMan remoting to use SSL. We also need to know if a server
already has ssh installed.
PowerShell Tool Design 9
That statement would drive some more detailed questions from us, asking for specifics on what the
tool needs to query. Suppose the answer came back as:
There are a number of PowerShell commands to use, although Test-NetConnection might be a good
choice. We can plan on writing a tools called Get-RemoteListeningConfiguration. It is of course
going to have a -ComputerName parameter that accepts one or more computer names as strings.
Looking ahead with a PowerShell 7 migration coming, it might also be helpful to know the operating
system and current PowerShell version. That information could be part of this command, but since
the information doesn’t really apply to remoting, it might be best to keep that as a separate tool. We
might also take into account how computer names will be fed to the command and what we might
do with the output. We’ll assume that some computers won’t respond, so we’ll design a way to deal
with that situation.
Our design usage examples might be pretty simple:
The output of the command should be structured and predictable. That is, given a specific set of
inputs, we should get the same output, which will make this a fairly straightforward design to write
unit tests for. Our command is only doing one thing, and has very few parameters, which gives us
a good feeling about the design’s tight scope.
So we’d take that back to the team and ask what they thought. Almost invariably, that will generate
questions.
How will we know if a machine fails? Will the tool keep going? Will it log that information?
Does it need to log anything?
OK - we might need to evolve the design a bit. We know that we need to keep going in the event
of a failure, and give the user the option to log failures to, perhaps, a text file. Provided the team
was happy with a text file as the error log, we’re good including that in the design. If they wanted
something more complicated - the option to log to a database, or to an event log - then we’d design
a separate logging tool to do all of that. For the sake of argument, though, let’s say they’re OK with
the text file.
PowerShell Tool Design 10
Let’s say that the team is satisfied with these additions, and that we have our desired usage examples
locked down. We can now get into the coding. But before we do, why don’t you take a stab at your
own design exercise?
Your Turn
If you’re working with a group, this will make a great discussion exercise. You won’t need a computer,
just a whiteboard or some pen and paper. The idea is to read through the business requirements and
come up with some usage examples that meet the requirements. We’ll provide all of the business
requirements in a single statement, so that you don’t have to “go back to the team” and gather more
information.
Start Here
Your team has come to you and asked you to design a PowerShell tool that will help them automate
a repetitive and critical, task. Team members are skilled in using PowerShell, so they just need a
command, or set of commands, that will help automate this task.
Members of the HelpDesk, as well as the Server Management Team, periodically manually
run a set of commands to get performance information that can be used to assess the status
or health of system. This information is often used to generate reports and to populate a
trend database. The information gathered includes these values:
• Computer name
• Total number of processes
• Total processor load
• % free physical memory
• % free space on drive C:
• The computer’s uptime
It would be helpful to have a single command to run that would generate a unified result. It
needs to be able to process multiple computers. And it will need to take credentials into
account. It would also be helpful to have some sort of optional logging mechanism for
failures.
Your Task
Your job is to design the tool that will meet the team’s business requirements. You are not writing any
code at this point. When creating a new tool, you have to consider who will use the tool, how they
PowerShell Tool Design 11
might use it and their expectations. And the user might be you! The end-result of your design will be
a list of command usage examples (like we have shown you previously), which should illustrate how
each of the team’s business needs will be solved by the tool. It’s fine to include existing PowerShell
commands in your examples, if those commands play a role in meeting the requirements.
Stop reading here and complete the task before resuming.
Our Take
We’ll design the command name as Get-TMComputerStatus. The “TM” stands for “Toolmaking,”
which is part of this book’s name, since we don’t have a specific company or organizational name
to use. We’ll design the following use cases:
Get-TMComputerStatus SRV1,SRV2
Get-Content servers.txt | Get-TMComputerStatus -credential company\administrator
(Get-ADComputer -filter *).name | Get-TMComputerStatus -ErrorLogFilePath err.log
Our intent here is that -Verbose will generate on-screen warnings about failures, while -ErrorLogFilePath
will write failed computer names to a file. It might also be nice to append errors to the same log.
Notice that, to make this “specification” easier to read, we’ve put each parameter on its own line.
The command won’t actually execute exactly like that, but that’s fine - clarity is the idea at this
point.
This example uses -ErrorLogFilePath and -ErrorAppend to indicate logging errors to a text file and
appending.
And because the output might be used to generate reports or be consumed by other applications,
we’ll want to make sure our code will meet these scenarios:
We’re illustrating two things here. First is that we can accept an imported CSV file, assuming it has
a Computername column. Our output is also consumable by standard PowerShell commands like
ConvertTo-HTML, which implies that Format- commands and Export- commands will also work.
Let’s Review
Let’s quickly review some of the key concepts from this chapter, just to make sure you’ve got them
all firmly in mind. See if you can answer these questions:
Review Answers
Not all of our questions lend themselves to easy, black-and-white answers, so if you didn’t answer
exactly the way we did, it doesn’t necessarily mean you’re wrong. But here’s how we’d have
answered:
• Computer name
• Total number of processes
• Total processor load
• % free physical memory
• % free space on drive C:
• The computer’s uptime
• a time stamp
We’ll plan to use CIM or CIM-related commands for much of this. We also know we’re going to
need to write a text log file in the event of errors. There’s more in terms of the tool itself we’ll need
to do, like adding a datetime stamp, but these are the basic units of functionality we need to figure
out.
We’re going to assume that you already know how to run PowerShell commands. If that’s not your
strong suit, please stop and go read Learn Windows PowerShell in a Month of Lunches, because it’s
all about discovering and running commands. Our point here is that we want to test and make sure
we know how to actually accomplish everything our tool needs to accomplish, by manually running
commands in the command-line window.
In our specific case, we want to also make sure we know how to reliably retrieve all of the
information in our list, which is going to involve more than one WMI/CIM class. We’ll need
Win32_OperatingSystem and Win32_Processor at the least. We could use Win32_LogicalDisk to get
C: drive information. Or we can use CIM-related commands like Get-Volume. Again - you should
know how to do these things already if you’re reading this book, so we’re not going to walk through
that entire “discovery” process.
We’re planning to use Get-CimInstance to do the actual querying, and because we’ll eventually
end up querying multiple classes, we’ll use New-CimSession and Remove-CimSession to create (and
then remove) a persistent connection to each computer, so that we can run all the queries over one
Start with a Command 14
connection. We’ll need to be able to detect errors in case the connection doesn’t work. Review the
help for New-CimSession if you’re not familiar with those tasks.
For now we’ll assume that all computers can be reached using the CIM cmdlets. If they can’t, that
probably means the server is in need of a major upgrade, and we’ll want to be able to log those
failures.
Since so much of this tool is getting performance related data, you may be wondering about
Get-Counter. This is a command designed to get performance information and can probably
gather most of what we need. For us there are a few potential gotchas. First, Get-Counter
relies on legacy remoting protocols like RPC and DCOM. It is not firewall friendly. Second,
the output from Get-Counter is a little convoluted and would take a few extra steps to get
the values we’re looking for. Get-CimInstance is a better (read that as modern) approach.
And it will be easier to get the values we need. But this is exactly the type of discussion you
need to have. What are the best PowerShell tools to use? What are the implications if we go
with one over another?
Your Turn
The previous chapter also included an exercise for you, and this one picks up where it left off. This
is where you’ll get to practice what we’ve preached in this chapter: making sure you know how to
accomplish everything that your tool will need to do, by starting in the PowerShell command-line
window.
Start Here
Remember that you are designing a tool that will get remoting configuration information for a
remote server. You don’t have to write any code at this point. Instead, mock up a sample output.
What PowerShell cmdlets do you think you will need to run? Come up with a list of examples on
how the command might be used. List what requirements you might need in order for your code to
run.
Your Task
Stop reading here and complete the task before resuming.
Our Take
For our exercise, we are creating a tool to get status information about a server. We know that we
assume at a minimum that the command can be run passing a single computername and writing an
object to the pipeline.
Start with a Command 15
Computername : SRV1
Processes : 275
CPULoad : 13
PctFreeMemory : 50.9874975465509
PctFreeDrive : 39.1990451499511
Uptime : 1.20:48:21.2817144
Date : 7/20/2020 2:09:16 PM
We also assume the person running the script will import a list of computernames and want to be
able to export to a CSV file. If there are errors they should be written to a log file.
• Get-Date
• Get-Volume
• Get-CimInstance -class win32_OperatingSystem
• Get-CimInstance -class win32_processor
• Get-CimInstance -class win32_process
• Out-File
Let’s Review
Here are a few questions you can answer to make sure you’ve picked up the key elements of this
chapter:
1. What is the value of testing commands in the command-line before you start writing a script?
2. Why is it important to understand how your command might be used?
3. Why is it critical that your command write an object to the pipeline?
Review Answers
Here are our answers to the questions. Note that these aren’t necessarily the only correct answers,
but hopefully they’ll help you understand if you grasped the chapter’s material or not.
Start with a Command 16
1. Individual commands are easier to test and fix in the command-line window. Using tested
commands in your script will help reduce the number of bugs that you introduce while coding.
Remember, that if you command fails at the console it won’t work in your script any better.
2. If you don’t know who your command will be used, you won’t be able to include necessary
features like input from the pipeline.
3. If your command doesn’t write an object to the pipeline, than you can’t process the results
with other PowerShell commands like Where-Object, Sort-Object or Out-Gridview.
Build a Basic Function and Module
In this chapter, we’ll start creating the tool that we designed in “Tool Design,” using some of the
commands that we figured out and tested in the previous chapter. It’s very important to understand
that this chapter isn’t going to attempt to build the entire tool, or attempt to solve the entire business
statement from our Design chapter. We’ll be taking things one step at a time, because it’s really the
process of toolmaking that we want to demonstrate for you.
Function Get-TMComputerStatus {
Param(
[string[]]$Computername,
[string]$ErrorLogFilePath,
[switch]$ErrorAppend
)
}
Notice how careful we’re being with the formatting of this code? In order to conserve space in this
book, we’re only indenting the code a little bit within the function and within the Param() block,
but you’ll typically indent four spaces (which, in most code editors, is what the Tab key will insert).
Do not get lazy about your code formatting. Lazy formatting is a sign of the devil, and is a sure
sign of code that probably has bugs - and will be hard to debug.
In the Param() block, we’ve only had to declare a few parameters. The Computername is pretty
straightforward. Although thinking ahead, we might need to take alternate credentials into account.
for the computer to query. We also have parameters related to the logging we want to do.
For those of you looking for some nits to pick, yeah, there’s probably one here. An argument
could be made that logging is a second thing the command is doing and it should not be
part of this command. On the other hand, a counter argument might be that the task of
getting computer status information by design includes logging. Semantics. But if nothing
else, including these parameters offers us some “teachable moments” so we’re going to go
with it.
Parameter definitions are very simple declarations, and we’ll build on these in upcoming chapters.
For now, here are some things to notice:
• Data types are enclosed in square brackets. Common ones include [string], [int], and
[datetime].
• Parameters become variables inside the function, so their names are preceded with a $. And
for God’s sake, don’t try to create a parameter name with spaces!
• Each parameter is separated from the next by a comma. You don’t have to put them one-per-
line as we’ve done, but when we start building on these, it’ll be a lot easier to read if they’re
broken out one per line.
• The -ComputerName parameter will accept one or more values in an array, which is what
[string[]] denotes.
Function Get-TMComputerStatus {
Param(
[string[]]$Computername,
[string]$ErrorLogFilePath,
[switch]$ErrorAppend
)
} #Get-TMComputerStatus
Notice that we tagged a #Get-TMComputerStatus comment on the closing bracket of the function?
That’s a good habit to get into when you have a closing bracket, as it can help remind you which
construct the bracket closes. You’ll appreciate this when you have a script file with multiple functions
defined.
We know we’ll need to run Get-CimInstance and maybe Get-Volume. At some point all of these
outputs will need to be condensed down to a simple, unified object. The basic command also doesn’t
include any error handling at this point. But this should be enough to give us a feel for how the
command will be structured and how the execution will flow internally.
Our code uses a ForEach construct. It works a bit like PowerShell’s ForEach-Object command, but
it has a different syntax:
The second variable - $collection here, and $ComputerName in our preliminary function - is
expected to contain zero or more items. The ForEach loop will execute its {script block} one time
for each item that is contained in the second variable. So, in Get-RemoteListeningConfiguration,
if we provided three computer names to the -ComputerName parameter, the ForEach loop would run
three times. Each time the loop runs, one item is taken from the second variable and placed into the
first. So, within the script block above, $item will contain one thing at a time from $collection. In
the defined function, $computer will contain one string at a time, taken from $ComputerName.
You’ll often see people use singular and plural words in their ForEach loops:
Build a Basic Function and Module 20
That approach makes it easier to remember that $name contains one thing from $names, but that’s
purely for human readability. PowerShell doesn’t magically know that “name” is the singular of
“names,” and it doesn’t care. The above could easily be rewritten as:
And PowerShell would be perfectly happy. That code would be a lot harder to read and keep track
of, though. In our sample function, you’ll notice that our second variable is not plural, right?
That’s because $ComputerName is one of our function’s input parameters. PowerShell’s convention
is to use singular words for command and parameter names. You won’t see -ComputerNames; you
only see -ComputerName as a parameter. We stuck with the convention, and so our ForEach loop
doesn’t follow a singular/plural pattern. Again, PowerShell itself doesn’t care, and we feel it’s more
important that our “outward-facing” elements - command and parameter names - follow PowerShell
naming conventions. And, honestly, using $computer as our one-item-at-a-time variable name saves
a little space in this book.
Function Get-TMComputerStatus {
Param(
[string[]]$Computername,
[string]$ErrorLogFilePath,
[switch]$ErrorAppend
)
@{Name="PctFreeMemory";Expression = {($_.freephysicalmemory/`
($_.TotalVisibleMemorySize))*100}},
@{Name="Uptime";Expression = { (Get-Date) - $_.lastBootUpTime}}
$os,$cpu,$vol
} #foreach $computer
} #Get-TMComputerStatus
The output in the book has been formatted to fit the page.Normally you wouldn’t need to break
lines using the back tick. This code works and writes results to the pipeline. However, this version is
breaking the cardinal rule of writing more than 1 type of object to the pipeline. We’ll need to refine
this code to get a single object type out of it. That’s coming up in the next few chapters.
ebookbell.com