PowerShell Debugging

Debugging is the process of finding and fixing errors in a script. Even experienced PowerShell developers encounter bugs. PowerShell provides a set of built-in tools — from simple output statements to a full interactive debugger — that help locate exactly where a script goes wrong and why.

Levels of Debugging in PowerShell

Level 1: Write-Output / Write-Host
     ↓   (quick checks, print variable values)

Level 2: Write-Debug / $DebugPreference
     ↓   (structured debug messages)

Level 3: Set-PSBreakpoint
     ↓   (stop execution at a specific line)

Level 4: PowerShell ISE / VS Code Debugger
         (visual breakpoints, step-through, watch window)

Method 1 — Print Debugging with Write-Output

The simplest debugging approach prints variable values at key points in the script to see what is happening.
$users = @("Alice", "Bob", "Charlie")
$count = 0

foreach ($user in $users) {
    Write-Output "Processing user: $user"    # Debug checkpoint
    $count++
    Write-Output "Count so far: $count"      # Watch the counter
}

Write-Output "Total users processed: $count"

Output

Processing user: Alice
Count so far: 1
Processing user: Bob
Count so far: 2
Processing user: Charlie
Count so far: 3
Total users processed: 3

Method 2 — Write-Debug with DebugPreference

Write-Debug adds structured debug messages that only display when debug mode is active. Normal users never see them; you switch them on only when investigating a problem.
function Get-FileSize {
    param($filePath)

    Write-Debug "Checking file: $filePath"

    if (Test-Path $filePath) {
        $size = (Get-Item $filePath).Length
        Write-Debug "File size in bytes: $size"
        return $size
    } else {
        Write-Debug "File not found!"
        return -1
    }
}

# Turn on debug messages
$DebugPreference = "Continue"

Get-FileSize "C:\Temp\report.txt"

# Turn them off when done
$DebugPreference = "SilentlyContinue"

DebugPreference Values

ValueBehavior
SilentlyContinueSuppresses all debug messages (default)
ContinueShows debug messages and keeps running
StopShows the message, then stops the script
InquireShows message and asks whether to continue

Method 3 — Verbose Output with Write-Verbose

Write-Verbose adds detailed step-by-step information without cluttering normal output. It activates with the -Verbose switch.
function Copy-ReportFiles {
    [CmdletBinding()]
    param($source, $destination)

    Write-Verbose "Starting copy from $source to $destination"

    $files = Get-ChildItem $source -Filter "*.txt"
    Write-Verbose "Found $($files.Count) files to copy"

    foreach ($file in $files) {
        Copy-Item $file.FullName -Destination $destination
        Write-Verbose "Copied: $($file.Name)"
    }

    Write-Output "Copy complete."
}

# Run with verbose output
Copy-ReportFiles -source "C:\Reports" -destination "C:\Backup" -Verbose

Method 4 — Set-PSBreakpoint

A breakpoint pauses script execution at a specific line, variable change, or command. This lets you inspect the state of all variables at that exact moment.

Break at a Specific Line

# Set a breakpoint at line 15 of a script
Set-PSBreakpoint -Script "C:\Scripts\deploy.ps1" -Line 15

Break When a Variable Changes

# Pause whenever $errorCount changes
Set-PSBreakpoint -Variable "errorCount" -Mode Write

Break When a Command Runs

# Pause every time Remove-Item is called
Set-PSBreakpoint -Command "Remove-Item"

Managing Breakpoints

# List all active breakpoints
Get-PSBreakpoint

# Remove a specific breakpoint (use ID from Get-PSBreakpoint)
Remove-PSBreakpoint -Id 0

# Remove all breakpoints
Get-PSBreakpoint | Remove-PSBreakpoint

Method 5 — The Built-in PowerShell Debugger

When a breakpoint triggers, PowerShell enters debug mode. A [DBG]: prompt appears and you can type commands to inspect the script.
[DBG]: PS C:\Scripts>>

Debugger Commands

CommandWhat It Does
s or StepIntoExecute the next line (enters functions)
v or StepOverExecute next line (skips into functions)
o or StepOutRun to end of current function
c or ContinueResume script until next breakpoint
q or QuitStop the script completely
l or ListShow surrounding lines of code
$varNamePrint a variable's current value

Method 6 — Try/Catch for Controlled Error Debugging

try {
    $result = 100 / 0    # This causes a divide-by-zero error
    Write-Output "Result: $result"
}
catch {
    Write-Output "Error caught: $($_.Exception.Message)"
    Write-Output "Error type: $($_.Exception.GetType().Name)"
    Write-Output "Line: $($_.InvocationInfo.ScriptLineNumber)"
}

Output

Error caught: Attempted to divide by zero.
Error type: RuntimeException
Line: 2

Common Bugs and How to Find Them

Bug TypeSymptomDebugging Approach
Wrong variable valueUnexpected outputPrint variable with Write-Output
Loop not stoppingScript hangs or runs too longAdd counter check inside the loop
File not foundTerminating errorUse Test-Path before accessing file
Wrong data typeMath gives wrong resultUse .GetType() to inspect the type
Function not workingReturns nothingSet breakpoint at function entry

Inspect Variable Types During Debugging

$age = "25"      # Stored as string, not integer

# Check what type it actually is
$age.GetType()   # Output: System.String

# Fix — cast to integer
$age = [int]"25"
$age.GetType()   # Output: System.Int32

Write-Output ($age + 5)   # Output: 30 (correct math, not string concat)

Using VS Code for Visual Debugging

VS Code with the PowerShell extension offers a full graphical debugger:
Steps to debug a PowerShell script in VS Code:
1. Open the .ps1 file in VS Code
2. Click the left margin next to a line number — a red dot appears (breakpoint)
3. Press F5 to run the script in debug mode
4. Script pauses at the red dot
5. Inspect variables in the VARIABLES panel on the left
6. Use F10 (Step Over) or F11 (Step Into) to move line by line
7. Press F5 again to continue to the next breakpoint

Summary

PowerShell debugging ranges from simple Write-Output checks to full interactive breakpoint sessions. Use Write-Debug for messages you can switch on and off, Set-PSBreakpoint to pause at any line or variable change, and the built-in debugger commands to inspect state during execution. VS Code provides a visual debugging experience for larger scripts. Combining these tools makes it straightforward to find and fix any bug.

Leave a Comment

Your email address will not be published. Required fields are marked *