#PSTip Taking control of verbose and debug output, part 1

Note: This tip requires PowerShell 2.0 or later.

One of the PowerShell features that makes writing scripts and functions easier is ability to produce different types of output. We can generate errors, warnings, verbose, and debug messages. For warnings and errors, we don’t have to do anything extra. By default, both are presented to users. The verbose and debug messages are different. A user has to enable these messages first. This is expected and logical behavior. A user runs a command with a goal to get results back. If we don’t provide a user with an easy way to enable verbose and debug output, it may remain ‘secret’ and, eventually, it will be ignored.

In this series of tips, we will try to see different ways to check if commands (scripts or functions) that we’ve written, or have taken from someone else, make it easy for the user to control the verbose and debug output and explore the ways to fix it (even if we can’t modify source of the command).

The verbose messages, generated with the Write-Verbose cmdlet, are used when something doesn’t work as expected, or when a user wants to understand how a command works. The debug messages, generated with Write-Debug, on the other hand are used (as the name suggests) during debugging of a command.

When working with cmdlets enabling verbose or debug output is easy: these commands have common switch parameters that can be used to do that. We can enable this functionality for scripts and functions too. We just need [CmdletBinding()] attribute on param() block, or we have to use [Parameter()] attribute on one of the parameters specified. Either way, we will turn our command into ‘advanced’. The problem with this approach is, if we don’t enable the advanced function features, our ‘simple’ function or script will silently accept any parameters, even if these parameters don’t exist on our command.

function Test-FakeParameter {
    param (
    "First is: $First"
    "Second is: $Second"

Test-FakeParameter -Second test

First is:
Second is:

If user will try to use -Verbose switch on ‘simple’ command nothing will happen: there is neither error, nor verbose output:

function Test-SimpleWithVerbose {
    param ($First)
    Write-Verbose "You specified: $First"
    "First is: $First"
Test-SimpleWithVerbose -Verbose -First Test

First is: Test

This is confusing. To actually enable verbose output for such command, user would have to change $VerbosePreference variable in current scope:

$VerbosePreference = 'Continue'
Test-SimpleWithVerbose -First Test

VERBOSE: You specified: Test
First is: Test

Does it work? Yes. Is it best approach for this problem? I don’t think so. Instead, it would be a good idea to make sure that commands we produce are ‘advanced’ (and by extension: they will produce verbose output if user requests it with -Verbose switch). For example: to check if any of functions from given module is not ‘advanced’ we can use following command:

Get-Command -Module TestVerbose -CommandType Function |
    Where-Object { -not $_.CmdletBinding }

CommandType     Name                                               Source
-----------     ----                                               ------
Function        Test-NoVerbose                                  TestVerbose
Function        Test-Simple                                     TestVerbose
Function        Test-SimpleNested                               TestVerbose
Function        Test-SimpleParam                                TestVerbose
Function        Test-SimpleSubExpression                        TestVerbose

In the next tip, we will make our test more granular. If you look closely at the output from the last command, there is one function in TestVerbose module that probably doesn’t have to be advanced to work properly (I would argue though that there is no reason to keep it ‘simple’). After that we will try to ‘fix’ commands that we can’t fully control.

Share on: