Reputation: 217
I have the following code in PowerShell 5.1
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
...
try {
$scopes = Get-DhcpServerv4Scope -ComputerName $dhcphost -CimSession $dhcphostx
...
} catch {
Write-Warning "Failed to query local DHCP server! Server 2003 system?"
}
Some of the DHCP servers which the script will query are Server 2003 and on those servers, the code will fail. If the error is the following, the try...cath will get triggered and my warning will get displayed as expected
+ CategoryInfo : ResourceUnavailable: (PS_DhcpServerv4Scope:String) [Get-DhcpServerv4Scope], CimJobExcept ion
+ FullyQualifiedErrorId : CimJob_BrokenCimSession,Get-DhcpServerv4Scope
+ PSComputerName : S1773C01
But on the following error, the try...catch will not trigger
+ CategoryInfo : ObjectNotFound: (PS_DhcpServerv4Scope:root/Microsoft/...cpServerv4Scope) [Get-DhcpServerv4Scope], CimException
+ FullyQualifiedErrorId : HRESULT 0x80338000,Get-DhcpServerv4Scope
+ PSComputerName : S1761C01
The try...catch will ONLY trigger on both errors when setting the ErrorAction parameter
$scopes = Get-DhcpServerv4Scope -ComputerName $dhcphost -CimSession $dhcphostx -ErrorAction Stop
So ErrorActionPreference does not always throw and gets overridden by ErrorAction parameter in this case. Why is this happening and is there a way to have the global ErrorActionPreference ALWAYS throw an error?
Upvotes: 2
Views: 1232
Reputation: 439347
The Get-DhcpServerv4Scope
cmdlet (all cmdlets that come with the DhcpServer
module) is implemented as an (advanced) function[1] (PowerShell code) rather than via a binary (as a class from a compiled .NET assembly, originally typically written in C#).
(Get-Command Get-DhcpServerv4Scope).CommandType
Regrettably, these two implementation methods result in different behavior, and you're seeing one aspect of it:
A (cmdlet-like advanced) function defined in a module does not see the caller's variables, which includes preference variables such as $ErrorActionPreference
(except when calling directly from the global scope).
-ErrorAction
are processed correctly.Additional behavioral differences exist, independently of whether a given advanced function is defined in a module or not - see this answer.
Workaround:
Complement $ErrorActionPreference = 'Stop'
with a local copy of $PSDefaultParameterValues
that presets use of -ErrorAction Stop
when calling cmdlets (from the caller's scope and its descendant scopes):
# Still necessary so as to also terminate on *expression*
# errors, such as exceptions thrown by .NET calls.
$ErrorActionPreference = 'Stop'
# Create a local copy of $PSDefaultParameterValues; without it,
# changes would take effect session-globally.
# To start with a clean slate, call .Clear() afterwards.
$PSDefaultParameterValues = $PSDefaultParameterValues.Clone()
# Preset -ErrorAction Stop for all cmdlet calls, which is
# also effective for cmdlets implemented as advanced functions
# in modules.
$PSDefaultParameterValues['*:ErrorAction'] = 'Stop'
Note: Conceivably, you could use $global:ErrorActionPreference = 'Stop'
to set the global instance of the preference variable, which functions in modules do see. However, this requires saving the original global value and restoring it in a finally
script block of a try / catch / finally
statement that encloses your entire code, so that future code executed in the same session isn't affected.
[1] More specifically, the module's functions are implemented as CIM wrappers via CDXML.
Upvotes: 3