{"id":246,"date":"2014-12-05T00:01:00","date_gmt":"2014-12-05T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/12\/05\/use-the-powershell-debugger\/"},"modified":"2022-06-21T12:41:02","modified_gmt":"2022-06-21T19:41:02","slug":"use-the-powershell-debugger","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-the-powershell-debugger\/","title":{"rendered":"Use the PowerShell Debugger"},"content":{"rendered":"<p><b style=\"font-size:12px\">Summary<\/b><span style=\"font-size:12px\">: Boe Prox shows how to debug scripts in Windows PowerShell.<\/span><\/p>\n<p>Honorary Scripting Guy and Windows PowerShell MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. This is the final part in a series of five posts about troubleshooting Windows PowerShell scripts and functions. The series includes:<\/p>\n<ol>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/provide-support-by-using-verbose-and-debug-streams\/\" target=\"_blank\" rel=\"noopener\">Provide Support by Using Verbose and Debug Streams<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/troubleshoot-by-using-set-psdebug\/\" target=\"_blank\" rel=\"noopener\">Troubleshoot by Using Set-PSDebug<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/enforce-better-script-practices-by-using-set-strictmode\/\" target=\"_blank\" rel=\"noopener\">Enforce Better Script Practices by Using Set-StrictMode<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/scripting\/trace-your-commands-by-using-trace-command\/\" target=\"_blank\" rel=\"noopener\">Trace Your Commands by Using Trace-Command<\/a><\/li>\n<li><strong>Use the PowerShell Debugger<\/strong> (this post)<\/li>\n<\/ol>\n<p>Over the course of the week, I have been showing you various approaches to troubleshoot your code (or someone else\u2019s code). Today I wrap things up by talking about the Windows PowerShell debugger. I&#8217;ll look at the console and the ISE to help troubleshoot different parts of the code.<\/p>\n<p>Debugging in the ISE is nicer because you can use keyboard shortcuts to set breakpoints on various lines of code. When you hit that breakpoint, it shows up in the ISE, making it easier to tell where you are. The following table lists the keyboard shortcut.<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\" style=\"width:444px\">\n<tbody>\n<tr>\n<td width=\"234\">\n<p>\n          <strong>\u00a0Action<\/strong>\n        <\/p>\n<\/td>\n<td width=\"210\">\n<p>\n          <strong>\u00a0Keyboard Shortcut<\/strong>\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Run\/Continue\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0F5\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Step Into\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0F11\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Step Over\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0F10\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Step Out\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0SHIFT+F11\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Display Call Stack\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0CTRL+SHIFT+D\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0List Breakpoints\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0CTRL+SHIFT+L\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Toggle Breakpoint\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0F9\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Remove All Breakpoints\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0CTRL+SHIFT+F9\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"234\" valign=\"top\">\n<p>\n          \u00a0Stop Debugger\n        <\/p>\n<\/td>\n<td width=\"210\" valign=\"top\">\n<p>\n          \u00a0SHIFT+F5\n        <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-size:12px\">Let\u2019s image that I have a script that I want to test to make sure it behaves properly. I am going to run this in the ISE to set up a couple of breakpoints and see what everything is doing. Eventually, the first breakpoint will trip and then the console will enter the debugger.<\/span><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug1.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug1.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Now that we have hit the debugger, we can start working with some of the available debugging commands to further explore the code and step through various parts. These are the same commands that are available in the console.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug2.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug2.png\" alt=\"Image of commands\" title=\"Image of commands\" \/><\/a><\/p>\n<p>If we have other breakpoints set, we can type <strong>c **(continue), and the code will execute to the next breakpoint. We can also find all the available debugging commands by typing **?<\/strong> (questions mark) at the debug prompt.<\/p>\n<p>Knowing this, we can begin debugging the code and tracking the progress of each line, if needed, or simply move to the next breakpoint. The nice thing about the ISE is that the tracking for each line is shown as a highlighted row. In the console, we would be making liberal use of <strong>\u2018list\u2019<\/strong> to see where we actually are. I can also use <strong>Get-PSCallStack<\/strong> to see where we are in the current call stack.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug3.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug3.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>Another thing worth noting is that there are a few variables that hold a value in the debugger that is different from the value they would have in the script or function. Those variables are: \u00a0<\/p>\n<ul>\n<li>$_<\/li>\n<li>$Args<\/li>\n<li>$Input<\/li>\n<li>$MyInvocation \u00a0<\/li>\n<li>$PSBoundParameters<\/li>\n<\/ul>\n<p>If you want these variables to be available, you need to assign the values to a different variable.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug4.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug4.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>So I have had a little fun with the ISE, but now I am going to turn my attention to the console and show how we can set breakpoints on scripts or commands by using <strong>Set-PSBreakPoint<\/strong> (these also can be used in the ISE).<\/p>\n<h3>Breaking on a variable<\/h3>\n<p>We can set a breakpoint on a variable based on three actions:<\/p>\n<ol>\n<li>Read<\/li>\n<li>Read\/Write<\/li>\n<li>Write<\/li>\n<\/ol>\n<p>Let\u2019s say we want to see whenever an error occurs with a command. We can use the <strong>\u2013ErrorVariable<\/strong> parameter and then set a breakpoint against that variable to send us to the debugger to further investigate what is going on.<\/p>\n<p style=\"margin-left:30px\">\n  Set-PSBreakpoint -Variable WMIErrors -Mode Write\n<\/p>\n<p style=\"margin-left:30px\">\n  ## Script Named ComputerScan.ps1\n<\/p>\n<p style=\"margin-left:30px\">\n  Param($Computername)\n<\/p>\n<p style=\"margin-left:30px\">\n  $WMIParams = @{\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 Class = &#8216;Win32_OperatingSystem&#8217;\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 ErrorVariable = &#8216;WMIErrors&#8217;\n<\/p>\n<p style=\"margin-left:30px\">\n  }\n<\/p>\n<p style=\"margin-left:30px\">\n  ForEach ($Computer in $Computername) {\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $WMIParams.computername = $Computer\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 Write-Verbose &#8220;Checking $Computer&#8221; -Verbose\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 Get-WmiObject @WMIParams\n<\/p>\n<p style=\"margin-left:30px\">\n  }\n<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug5.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug5.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>As expected, when an error occurs with my WMI query, the error is written to the <strong>$WMIErrors<\/strong> variable, which throws me into the debugger, based on the breakpoint that I set. I cannot actually view the error yet because it hasn\u2019t been written to the variable. We need to move to the next line of the script to see what the error is.<\/p>\n<h3>Breaking on a command<\/h3>\n<p>We can break into the debugger whenever a command is used! I am going to modify my script a bit to remove the error handling and error variable for the sake of this demonstration.<\/p>\n<p style=\"margin-left:30px\">\n  Param($Computername)\n<\/p>\n<p style=\"margin-left:30px\">\n  $WMIParams = @{\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 Class = &#8216;Win32_OperatingSystem&#8217;\n<\/p>\n<p style=\"margin-left:30px\">\n  }\n<\/p>\n<p style=\"margin-left:30px\">\n  ForEach ($Computer in $Computername) {\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $WMIParams.computername = $Computer\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 Write-Verbose &#8220;Checking $Computer&#8221; -Verbose\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 Get-WmiObject @WMIParams\n<\/p>\n<p style=\"margin-left:30px\">\n  }\n<\/p>\n<p>This breakpoint will now target the <strong>Set-StrictMode<\/strong> cmdlet, and it will put me in the debugger whenever this command occurs:<\/p>\n<p style=\"margin-left:30px\">\n  Set-PSBreakpoint -Command Set-StrictMode<span style=\"font-size:12px\">\u00a0<\/span>\n<\/p>\n<p>Now we can run the script and wait for the error to occur.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug6.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug6.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>After breaking on <strong>Set-Strictmode<\/strong>, we can see where the handler is by using <strong>\u2018l\u2019<\/strong>. I could choose to continue stepping through the handler; but in this case, I am simply exploring some of the variables such as <strong>$PSBoundParameters<\/strong> and <strong>$args<\/strong>. Neither of them offers me anything because I am in the debugger. I can also look at the <strong>$DebugContext<\/strong> automatic variable that is created when entering the debugger to see if there is anything useful there.<\/p>\n<p style=\"margin-left:30px\">\n  <b>Note\u00a0<\/b> You can always tell if you are in the debugger by testing for <b>$PSDebugContext<\/b>. This is useful if you use a custom prompt that overwrites the <b>[DBG]:<\/b> that would normally appear.\n<\/p>\n<p>One last thing that I wanted to touch on is the use of the <strong>\u2013Action<\/strong> parameter with <strong>Set-PSBreakPoint<\/strong>. For example, by using this, you can perform other validation checks or set other variables, depending on what you want to accomplish.<\/p>\n<p>An important thing to realize is that if you use this parameter, you must use the <strong>Break<\/strong> keyword to enter the debugger. Otherwise, the code will continue like nothing happened. This command will not enter the debugger:<\/p>\n<p style=\"margin-left:30px\">\n  Set-PSBreakpoint -Command Set-StrictMode -Action {Write-Verbose &#8216;Preparing for the debugger!!&#8217; -Verbose}\n<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug7.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug7.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>This command will work:<span style=\"font-size:12px\">\u00a0<\/span><\/p>\n<p style=\"margin-left:30px\">\n  Set-PSBreakpoint -Command Set-StrictMode -Action {Write-Verbose &#8216;Preparing for the debugger!!&#8217; -Verbose;Break}\n<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug8.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/PSDebug8.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>That looks more like what I wanted! If you remember to add the <strong>Break<\/strong> keyword in your script block, you will enter the debugger and can start looking at what is happening in your code.<\/p>\n<p>This wraps up my week of script troubleshooting! I hope you have found this information to be useful. If you have any questions, feel free to let me know!<\/p>\n<p>We invite you to follow the Scripting Guy on\u00a0<a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\" rel=\"noopener\">Twitter<\/a>\u00a0and\u00a0<a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\" rel=\"noopener\">Facebook<\/a>. If you have any questions, send email to the Scripting Guy at\u00a0<a href=\"mailto:scripter@microsoft.com\" target=\"_blank\" rel=\"noopener\">scripter@microsoft.com<\/a>, or post your questions on the\u00a0<a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\" rel=\"noopener\">Official Scripting Guys Forum<\/a>. Until then, see ya!<\/p>\n<p><strong>Boe Prox<\/strong>, Windows PowerShell MVP and Honorary Scripting Guy<span style=\"font-size:12px\">\u00a0<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Boe Prox shows how to debug scripts in Windows PowerShell. Honorary Scripting Guy and Windows PowerShell MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. This is the final part in a series of five posts about troubleshooting Windows PowerShell scripts and functions. The series includes: Provide Support by [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[162,295,3,134,45],"class_list":["post-246","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-boe-prox","tag-debug","tag-scripting-guy","tag-troubleshooting","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Boe Prox shows how to debug scripts in Windows PowerShell. Honorary Scripting Guy and Windows PowerShell MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. This is the final part in a series of five posts about troubleshooting Windows PowerShell scripts and functions. The series includes: Provide Support by [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/246","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=246"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/246\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=246"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=246"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=246"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}