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
| Value | Behavior |
|---|
SilentlyContinue | Suppresses all debug messages (default) |
Continue | Shows debug messages and keeps running |
Stop | Shows the message, then stops the script |
Inquire | Shows 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
| Command | What It Does |
|---|
s or StepInto | Execute the next line (enters functions) |
v or StepOver | Execute next line (skips into functions) |
o or StepOut | Run to end of current function |
c or Continue | Resume script until next breakpoint |
q or Quit | Stop the script completely |
l or List | Show surrounding lines of code |
$varName | Print 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 Type | Symptom | Debugging Approach |
|---|
| Wrong variable value | Unexpected output | Print variable with Write-Output |
| Loop not stopping | Script hangs or runs too long | Add counter check inside the loop |
| File not found | Terminating error | Use Test-Path before accessing file |
| Wrong data type | Math gives wrong result | Use .GetType() to inspect the type |
| Function not working | Returns nothing | Set 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.