
close
close
Whenever I teach about PowerShell scripting I always stress the value of using verbose messages in your functions and scripts. Assuming you are using cmdletbinding, and why wouldn’t you, you can insert Write-Verbose statements throughout your script. These statements won’t do anything unless your command is run with the common -Verbose parameter. However, these statements can be very useful for tracing and troubleshooting. I use these Verbose statements to help me track what my command is doing so that if it fails I have a better idea of where it failed and what it was doing. I don’t enjoy debugging and dealing with the interactive debugger and having Write-Verbose statements makes my life easier. Here are some ideas on how you might want to start using Write-Verbose.
First, if you want to try some of these things right from the command prompt, turn on your verbose pipeline.
$VerbosePreference = "continue"
When you are done testing, set it back to the default.
advertisment
$VerbosePreference = "silentlycontinue"
Now you can run Write-Verbose commands to see what they look like. Remember that anything you want to write with Write-Verbose must be a string or at least be able to be treated as a string.
Write-Verbose "I am doing something"
You may need to use subexpressions as well. One suggestion is to include a timestamp in your verbose message.
Write-Verbose "[$(Get-Date)] Starting to do something long running"
Write-Verbose "$((get-date).TimeOfDay.ToString()) Initializing array"
Function Get-Foo { [cmdletbinding()] Param( [Parameter(Position = 0, Mandatory, ValueFromPipeline)] [string[]]$Computername ) Begin { Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)" Write-Verbose "[BEGIN ] Initializing foo array" [email protected]() } #begin Process { foreach ($computer in $computername) { Write-Verbose "[PROCESS] Getting foo from $($computer.toUpper())" #code happens } } #process End { Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)" } #end }
The Begin and End blocks include code to display the command name. This is very handy if you have a function that is calling another function. You may also be wondering about the spacing of END and BEGIN. I did that so that the verbose output would align nicely and be easier to read.
advertisment
Function TryMe { [cmdletbinding()] Param( [string]$Computername ) Begin { Write-Verbose "$((get-date).TimeOfDay.ToString()) [BEGIN ] Starting: $($MyInvocation.Mycommand)" Write-Verbose "$((get-date).TimeOfDay.ToString()) [BEGIN ] Initializing array" $a = @() } #begin Process { Write-Verbose "$((get-date).TimeOfDay.ToString()) [PROCESS] Processing $Computername" # code goes here } #process End { Write-Verbose "$((get-date).TimeOfDay.ToString()) [END ] Ending: $($MyInvocation.Mycommand)" } #end } #function
Function TryMe2 { [cmdletbinding()] Param( [Parameter(Position = 0, Mandatory,ValueFromPipeline)] [string]$Computername ) Begin { $start = Get-Date Write-Verbose "00:00:00.0000000 [BEGIN ] Starting: $($MyInvocation.Mycommand)" Write-Verbose "$((New-TimeSpan -Start $start).ToString()) [BEGIN ] Initializing array" $a = @() } #begin Process { Write-Verbose "$((New-TimeSpan -Start $start).ToString()) [PROCESS] Processing $Computername" # code goes here Start-Sleep -Milliseconds 1234 } #process End { Write-Verbose "$((New-TimeSpan -Start $start).ToString()) [END ] Ending: $($MyInvocation.Mycommand)" } #end } #function
Function TestThis { [cmdletbinding()] Param() Write-Verbose "Execution Metadata:" Write-Verbose "User = $($env:userdomain)\$($env:USERNAME)" $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $IsAdmin = [System.Security.Principal.WindowsPrincipal]::new($id).IsInRole('administrators') Write-Verbose "Is Admin = $IsAdmin" Write-Verbose "Computername = $env:COMPUTERNAME" Write-Verbose "OS = $((Get-CimInstance Win32_Operatingsystem).Caption)" Write-Verbose "Host = $($host.Name)" Write-Verbose "PSVersion = $($PSVersionTable.PSVersion)" Write-Verbose "Runtime = $(Get-Date)" }
That might be a bit much to include in every script. What could be easier is an external function I could load in my profile.
Function Get-ExecutionMetadata { [cmdletbinding()] Param() $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $IsAdmin = [System.Security.Principal.WindowsPrincipal]::new($id).IsInRole('administrators') $os = (Get-CimInstance Win32_Operatingsystem).Caption $meta = [pscustomobject]@{ User = "$($env:userdomain)\$($env:USERNAME)" IsAdmin = $IsAdmin Computername = $env:COMPUTERNAME OS = $os Host = $($host.Name) PSVersion = $($PSVersionTable.PSVersion) Runtime = $(Get-Date) } $meta }
I could then call this code in my function.
advertisment
Function Get-Foo { [cmdletbinding()] Param( [Parameter(Position = 0, Mandatory, ValueFromPipeline)] [string[]]$Computername ) Begin { Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)" #don't pass -Verbose to the Get-ExecutionMetadata function $metadata = Get-ExecutionMetadata -Verbose:$false | Out-String Write-Verbose "[BEGIN ] Execution Metadata:" Write-Verbose $metadata Write-Verbose "[BEGIN ] Initializing foo array" [email protected]() } #begin Process { foreach ($computer in $computername) { Write-Verbose "[PROCESS] Getting foo from $($computer.toUpper())" #code happens [pscustomobject]@{Computername = $computer;Time = (Get-Date).TimeOfDay} } } #process End { Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)" } #end }
I hope you’ll let me know if this was helpful and how you start using it.
More from Jeff Hicks
advertisment
Petri Newsletters
Whether it’s Security or Cloud Computing, we have the know-how for you. Sign up for our newsletters here.
advertisment
More in PowerShell
Microsoft’s New PowerShell Crescendo Tool Facilitates Native Command-Line Wraps
Mar 21, 2022 | Rabia Noureen
Most popular on petri
Log in to save content to your profile.
Article saved!
Access saved content from your profile page. View Saved
Join The Conversation
Create a free account today to participate in forum conversations, comment on posts and more.
Copyright ©2019 BWW Media Group