biepbiep
biepbiep

Reputation: 217

ErrorActionPreference behaving differently as ErrorAction parameter

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

Answers (1)

mklement0
mklement0

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#).

    • To verify, inspect the output from
      (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).

      • Therefore, such preference variables aren't honored. See GitHub issue #4568 for a discussion of this problematic behavior.
      • By contrast, common parameters such as -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

Related Questions