PowerShell How-To

Make Your PowerShell Scripts Talk Back

Being able to receive real-time feedback as you work is a huge factor in writing script that won't break.

Your script could be the most awesome automation tool on the planet. It could ring all the bells and blow all the whistles in the world until ... it hits a condition you never accounted for and throws some nasty, obscure error message. What happened? Where in that massive script did the error actually occur anyway? Right at the top when checking your prerequisites or line 1,342 buried in a couple nested for loops? The standard exception object might tell you a line number but not necessarily where the error occurred.

This is what you have to look forward to if you don't code in any feedback to the console, a text log, the event log, somewhere! The user of the script and you, the author, are essentially blind to what's going on under the hood of your super-duper, knock-your-socks-off, script. What can I do about this, you ask? Well, I'm glad you asked so let me tell you!

The answer is to use a PowerShell Write cmdlet. The PowerShell Write cmdlets (Debug, Verbose, Warning and Error) all exist to give you some kind of feedback as to the goings on of your script. I'm excluding Write-Host and Write-Output here because those technically aren't "logging" cmdlets, per se. We're lucky that the PowerShell team gave us so many options (it's not cool to use Write-Host for everything!). In this article, my aim is to first demonstrate not all default Write cmdlets are created equal and when an appropriate time is to use each one.

Syslog Roots
Before we get started discussing PowerShell-specific cmdlet,s let me first take you back to the old days of plain ol' syslog. Syslog's been around since 1980 and was originally developed for *nix operating systems. When thinking about logging, syslog has been the standard for a very long time. Syslog (RFC 5424) comes with eight severity levels each representing a severity of an event in which it was triggered. Aren't we lucky we only really have five in PowerShell!

Although I wasn't privy to the PowerShell team's inspiration for their Write cmdlets I'm sure that it has some roots in syslog; nearly all logging mechanisms do these days. PowerShell implements the syslog-like behavior of severities like this severity pyramid "stolen" from a great Pluralsight course module entitled Logging with PowerShell from a well-known, reputable author.

Matching PowerShell Write Cmdlets to Severity Levels
Depending on the type of event that your script generates depends on the severity you choose. From the pyramid above, we have four rough levels of severity; debug, verbose, warning and error each with a conveniently named matching PowerShell cmdlet.

With four options to choose from where in your script do you use each of these cmdlets?

Write-Debug
Write-Debug is the lowest severity rating amongst of four cmdlets. Write-Debug is typically used when you, the script author, are in a debugging session. When you insert a Write-Debug statement in a script and have the appropriate settings in place every time a Write-Debug line is found, the PowerShell host will halt the script completely and allow you to examine the current script environment.

Jason Morgan explains the case to use Write-Debug in a great post entitled Where and Why To Use Write-Debug on his blog.

Write-Verbose

Write-Verbose is the next step up the severity pyramid. Write-Verbose relaxes control a little but and has no control over the execution of the script unlike Write-Debug does. It cannot stop the actual execution of the script but should be added in a script to write informational messages to the console when an event occurs.

Write-Verbose can be thought of as user-level rather than an author-level Write cmdlet. What do I mean by this? Think of Write-Verbose this way. Let's say I had a script that set and read a couple variables; something like this:

$MyVariable = 'some value'
Write-Verbose "My variable's value was initially set to be $MyVariable"
## <Do something in here that may or may not change the value of $MyVariable>
Write-Verbose "My variable's value is now $MyVariable"

If verbose output were set to be displayed, we'd see what the value of $MyVariable was when it was initially set and what the value was after some other code had been executed already. Using Write-Verbose as a means to track variable assignment works great.

Write-Warning

Write-Warning crosses the threshold from a "nice to see" message to something that needs to be seen because something went awry. Write-Warning, along with Write-Verbose, displays yellow text in the console. Write-Warning is used for conditions in your script that is bad enough to notify the user but not quite bad enough to stop script execution.

For example, let's say you've got a script that copies a few files from one folder to another. You obviously want all these files copied over but it's not the end of the world if a couple fail. In this instance a Write-Warning line would be warranted.

$SourceFiles = Get-ChildItem -Path C:\SourceFolder -File
foreach ($File in $SourceFiles) {
    Copy-Item -Path $File.FullName -Destination D:\SomeFolder
    if (Test-Path -Path "D:\SomeFolder\$($File.Name)" {
        Write-Warning -Message "The file D:\SomeFolder\$($File.Name) was not copied to D:\SomeFolder"
      } else {
        Write-Verbose -Message "The file D:\SomeFolder\$($File.Name) was successfully copied to D:\SomeFolder"
 }
}

Write-Error
Finally, we have Write-Error. This cmdlet is at the top of the severity pyramid for one reason. When a very important event doesn't happen as the author thinks it should Write-Error should be used. This cmdlet should be used when you intend to call the script or function a "failure." It is an indicator that something bad happened and should be looked at immediately.

Using our previous example script snippet, let's say you don't necessarily care if one or more files fail to copy but if they all fail then that's a problem. In this instance, using Write-Error would be appropriate.

>$SourceFiles = Get-ChildItem -Path C:\SourceFolder -File
$i = 0
foreach ($File in $SourceFiles) {
    Copy-Item -Path $File.FullName -Destination D:\SomeFolder
    if (Test-Path -Path "D:\SomeFolder\$($File.Name)" {
        Write-Warning -Message "The file D:\SomeFolder\$($File.Name) was not copied to D:\SomeFolder"
  } else {
        Write-Verbose -Message "The file D:\SomeFolder\$($File.Name) was successfully copied to D:\SomeFolder"
        $i++
   }
}
if ($SourceFiles.Count -ne $i) {
    Write-Error -Message "All files failed to copy"
}

As you can see, PowerShell gives you a few different methods to give feedback to us humans. It's now up to us script authors to tell our scripts to talk back at the appropriate times and how loud to scream in order to get a clear picture of the health of our scripts.

About the Author

Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.


comments powered by Disqus
Most   Popular