I have the following script:
$ErrorActionPreference = "Stop"
Install-Module 'DoesNotExist' -Force
Write-Host "This should not be printed!"
I'd assumed, that if an error occurs in Install-Module
, it terminates, because I set $ErrorActionPreference
to Stop
. Unfortunately it doesn't.
What even makes it weirder is, that if I set $ErrorActionPreference
in the global
scope to Stop
, it works.
$global:ErrorActionPreference = "Stop"
You're seeing an unfortunate design limitation, discussed in GitHub issue #4568:
Problem:
Commands implemented as PowerShell functions that originate in modules:
do not see any preference variables set in the caller's scope...
... except if the calling scope happens to be the global one - which is typically not the case.
Unfortunately, there are no good solutions to this problem, only cumbersome workarounds:
Workaround for callers:
A non-global caller, such as a script or a function (which run in a child scope by default) must:
Either: Explicitly set preference variables in the global scope (e.g.,
$global:ErrorActionPreference = 'Stop'
)finally
clause of atry
/catch
/finally
statement.Or: On a call-by-call basis, use the corresponding common parameters (e.g.
-ErrorAction Stop
)-ErrorAction Stop
is that it is not fully equivalent to$global:ErrorActionPreference = 'Stop'
in that only acts on non-terminating errors, and not also on statement-terminating ones, additionally necessitating atry
/catch
or atrap
statement. See this answer for background information.These workarounds are spelled out in detail in the middle section of this closely related answer.
Generally, the challenge is to know when a workaround is even needed, as a given command's name doesn't reveal whether it is PowerShell-implemented and whether it comes from a module.
You can use the following test for a given command; if it returns
$true
, the workaround is needed:Workaround for module authors:
Dave Wyatt has authored a helper module,
PreferenceVariables
, whoseGet-CallerPreference
function can be used inside module functions to obtain an outside caller's preference variables.In other words: You can use this to overcome the design limitation and make your PowerShell-implemented cmdlet (advanced function) behave like binary cmdlets with respect to the caller's preference variables.[1]
Get-CallerPreference
use is explained in this blog post.Note: You don't strictly need another module to implement this functionality, it - which doesn't require much code - could be integrated directly into a module.
[1] There are other, subtle differences, discussed in this answer.