While working with PowerShell and writing various psm1 module files, I've noticed that for whatever reason, Advanced Functions in a Script Module (psm1 file) do not inherit the Preference variables (or any other variable) from the scope of the code that calls them (unless that happens to be the Global scope.) As far as the functions in the Script Module are concerned, variables exist either in the function, in the module, or in the Global scope, and that's it. If the caller sets, for example, $ErrorActionPreference to 'Stop' in the Script scope and then calls your function, your function will still be acting with the value of the global $ErrorActionPreference, which is most likely still set to the default value of 'Continue'.
You can see this behavior by creating a test PS1 and PSM1 file as follows:
# Test.psm1: function Test-ModuleFunction { [CmdletBinding()] param ( ) Write-Host "Module Function: ErrorActionPreference = $ErrorActionPreference" } # Test.ps1: Import-Module .\Test.psm1 $ErrorActionPreference = 'Stop' Test-ModuleFunction
I researched this, and at some point discovered that module functions can get at the variables in the caller's scope by using properties and methods of the $PSCmdlet object, for example:
function Test-ModuleFunction { [CmdletBinding()] param ( ) if (-not $PSBoundParameters.ContainsKey('ErrorAction')) { $ErrorActionPreference = $PSCmdlet.GetVariableValue('ErrorActionPreference') } Write-Host "Module Function: ErrorActionPreference = $ErrorActionPreference" }
However, it gets rather annoying to have to define code like that in every single function that needs to be exported from a script module (assuming you want the module's functions to behave according to preference variables set in the caller's scope.) I did some more digging earlier today, and discovered a way to put this code into a function, which can be called like this:
function Test-ModuleFunction { [CmdletBinding()] param ( ) Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState Write-Host "Module Function: ErrorActionPreference = $ErrorActionPreference" }
Passing the $PSCmdlet object to the Get-CallerPreference function gives it the ability to read the BoundParameters collection and access your caller's variables, and passing in $ExecutionContext.SessionState gives it the ability to set those variables in your function's scope (Test-ModuleFunction, in this case.) This works even if the Get-CallerPreference function is exported from a different module than Test-ModuleFunction.
I'm pretty happy with how easy it is to copy and paste this one line into the Begin block of my script module functions, and figured I'd share it. If you've run into this behavior and want to give the function a shot, it's on the Gallery athttp://gallery.technet.microsoft.com/Inherit-Preference-82343b9d