Part 4 - Error Handling and Logs
Once your scripts go beyond test labs and start interacting with live environments—local systems, production servers, Azure resources. They will encounter errors. That’s not failure. That’s reality. What matters is how you handle it.
This post covers clean, predictable error handling and structured logging. We’ll move beyond -ErrorAction SilentlyContinue
and into something maintainable.
The Wrong Way: Swallowing Errors
This pattern is everywhere in beginner scripts:
Get-Service -Name 'InvalidName' -ErrorAction SilentlyContinue
It suppresses the error and gives you no feedback. That’s not handling an error—that’s ignoring one.
Try/Catch/Finally: The Real Deal
PowerShell supports structured exception handling with try
, catch
, and finally
.
try {
$service = Get-Service -Name 'InvalidName' -ErrorAction Stop
Write-Output "Service status: $($service.Status)"
}
catch {
Write-Output "Error: $_"
}
finally {
Write-Output "Checked service at $(Get-Date)"
}
Key points
- Always use
-ErrorAction Stop
insidetry
blocks. - The
catch
block uses$_
to reference the error. finally
runs whether thetry
succeeds or fails—great for cleanup or logging.
Detecting Silent Failures
Sometimes, you can’t rely on exceptions. Use $?
, $LASTEXITCODE
, or Test-Path
when needed.
Remove-Item -Path 'C:\Temp\file.txt' -Force
if (-not $?) {
Write-Output "Failed to delete file."
}
But whenever possible, prefer structured try/catch
.
Writing Logs
Logging should:
- Be timestamped
- Go to a file
- Be optional or configurable
Minimal pattern
function Write-Log {
param (
[Parameter(Mandatory)]
[string]$Message,
[string]$LogPath = "$PSScriptRoot\script.log"
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
"$timestamp`t$Message" | Add-Content -Path $LogPath
}
Usage
Write-Log -Message "Starting service check"
:green-circle: Always use
Write-Output
for runtime messages andWrite-Log
for persistent ones.
A Real Example: Error + Logging Together
function Restart-ServiceSafe {
param (
[Parameter(Mandatory)]
[string]$ServiceName
)
try {
$service = Get-Service -Name $ServiceName -ErrorAction Stop
if ($service.Status -eq 'Running') {
Restart-Service -Name $ServiceName -Force
Write-Output "$ServiceName restarted."
Write-Log -Message "$ServiceName restarted successfully."
}
else {
Write-Output "$ServiceName is not running. Skipped restart."
Write-Log -Message "$ServiceName was not running."
}
}
catch {
Write-Output "Error: $_"
Write-Log -Message "Failed to restart $ServiceName - $_"
}
}
Summary
You now know how to:
- Use
try/catch/finally
for real error handling - Avoid silent failures with
$?
,Test-Path
, etc. - Write consistent, timestamped log entries to a file
Error handling and logging aren’t optional in production scripts. They’re what make it production-ready.
Up Next
In Part 5, we’ll bring everything together—variables, functions, error handling, input, and logging—into one end-to-end PowerShell script solving a real-world task.
Until then: Handle your errors. Log your decisions. Script like you mean it.