When working with PowerShell to check processes across multiple servers, you'll often encounter two main issues:
- Connection errors when servers are unavailable
- Missing process errors when the specified process doesn't exist
The basic command looks like this:
get-process -ComputerName server1,server2,server3 -name explorer |
Select-Object processname,machinename
PowerShell has several ways to handle errors gracefully:
- -ErrorAction parameter
- -ErrorVariable parameter
- Try/Catch blocks
- $ErrorActionPreference variable
Here's an improved version that handles errors elegantly:
$servers = "server1","server2","server3"
$results = @()
foreach ($server in $servers) {
try {
$process = Get-Process -ComputerName $server -Name explorer -ErrorAction Stop
$results += $process | Select-Object ProcessName, MachineName
}
catch [System.InvalidOperationException] {
$results += [PSCustomObject]@{
ProcessName = "N/A (Connection Failed)"
MachineName = $server
}
}
catch {
$results += [PSCustomObject]@{
ProcessName = "N/A (Process Not Found)"
MachineName = $server
}
}
}
$results | Format-Table -AutoSize
For more sophisticated scenarios, consider these approaches:
$ErrorActionPreference = "SilentlyContinue"
$processes = $servers | ForEach-Object {
$process = Get-Process -ComputerName $_ -Name explorer
if (-not $process) {
[PSCustomObject]@{
ProcessName = "Not Running"
MachineName = $_
}
}
else {
$process | Select-Object ProcessName, MachineName
}
}
$ErrorActionPreference = "Continue"
Here's a function you can add to your profile or module:
function Get-RemoteProcess {
param(
[Parameter(Mandatory=$true)]
[string[]]$ComputerName,
[Parameter(Mandatory=$true)]
[string]$ProcessName
)
foreach ($computer in $ComputerName) {
try {
$process = Get-Process -ComputerName $computer -Name $ProcessName -ErrorAction Stop
$process | Select-Object ProcessName, MachineName
}
catch {
[PSCustomObject]@{
ProcessName = "ERROR: $($_.Exception.Message)"
MachineName = $computer
}
}
}
}
When querying processes across multiple servers using PowerShell's Get-Process
cmdlet, connection failures or missing processes can disrupt your entire output pipeline. The default behavior throws terminating errors that halt execution, which isn't ideal for monitoring scenarios.
PowerShell provides several mechanisms to control error handling:
# Common error handling parameters
-ErrorAction: SilentlyContinue | Stop | Continue | Inquire | Ignore
-ErrorVariable: Store errors in specified variable
Here's an improved version that handles connection issues gracefully while maintaining useful output:
$servers = "server1","server2","server3","server4"
$results = @()
foreach ($server in $servers) {
try {
$process = Get-Process -ComputerName $server -Name explorer -ErrorAction Stop |
Select-Object ProcessName, MachineName
$results += $process
}
catch [System.InvalidOperationException] {
$results += [PSCustomObject]@{
ProcessName = "N/A (Connection Failed)"
MachineName = $server
}
}
catch {
$results += [PSCustomObject]@{
ProcessName = "N/A (Process Missing)"
MachineName = $server
}
}
}
$results | Format-Table -AutoSize
For simpler scenarios, you can use the silent continue approach:
Get-Process -ComputerName $servers -Name explorer -ErrorAction SilentlyContinue |
Select-Object ProcessName, MachineName | Format-Table
This variation logs errors while continuing execution:
$errorLog = @()
$processes = Get-Process -ComputerName $servers -Name explorer -ErrorVariable +errorLog -ErrorAction SilentlyContinue
$processes | Select-Object ProcessName, MachineName | Format-Table
if ($errorLog) {
Write-Warning "Encountered $($errorLog.Count) errors:"
$errorLog | ForEach-Object { Write-Warning $_.Exception.Message }
}
When scanning many servers, consider these optimizations:
- Use parallel processing with
ForEach-Object -Parallel
(PowerShell 7+) - Implement timeout parameters to prevent hanging
- Cache results for frequently checked servers
Here's a comprehensive solution with all best practices:
param(
[string[]]$ComputerNames,
[string]$ProcessName = "explorer",
[int]$TimeoutSeconds = 10
)
$results = foreach ($computer in $ComputerNames) {
try {
$params = @{
ComputerName = $computer
Name = $ProcessName
ErrorAction = 'Stop'
}
if ($PSVersionTable.PSVersion.Major -ge 7) {
$params.ThrottleLimit = 5
}
$process = Get-Process @params |
Select-Object ProcessName, MachineName, Id, Responding
[PSCustomObject]@{
Status = "Running"
ProcessName = $process.ProcessName
MachineName = $computer
PID = $process.Id
Responsive = $process.Responding
}
}
catch [System.InvalidOperationException] {
[PSCustomObject]@{
Status = "Server Unreachable"
ProcessName = $ProcessName
MachineName = $computer
PID = $null
Responsive = $null
}
}
catch [Microsoft.PowerShell.Commands.ProcessCommandException] {
[PSCustomObject]@{
Status = "Process Not Found"
ProcessName = $ProcessName
MachineName = $computer
PID = $null
Responsive = $null
}
}
}
$results | Format-Table -AutoSize