'PowerShell module function does not inherit WhatIf

TL;DR: Why do module functions not inherit -WhatIf implicitly when called from a script?

It was my understanding that cmdlets and functions will inherit switches such as -WhatIf from a calling script, however I have seen behaviour that demonstrates this is not always the case. I have confirmed that the examples in How do you support PowerShell's -WhatIf & -Confirm parameters in a Cmdlet that calls other Cmdlets? work fine for me, but the problem seems to happen when my script is calling a function that is defined in a module.

In my case I have a script with a function defined locally (i.e. in the PS1) file. This script imports a script module. When I run my main script with the -WhatIf switch, the local function inherits the -WhatIf state but module functions do NOT exhibit the "WhatIf" behaviour, potentially disastrously.

If I call Show-WhatIfOutput with the -WhatIf switch set explicitly, it works as expected.

If I move the Call-ShowWhatIf function from the script to the module, and use Call-ShowWhatIf -WhatIf it works fine. That is, Show-WhatIfOutput does have -WhatIf set implicitly, but this is not a solution I can use in my real-world case.

Even more simply, if I enable SupportsShouldProcess on the main script, the same pattern emerges: the local function will inherit the switch; however, the module functions will not.

Why does the module function not inherit -WhatIf when called from the script?


Test code

Test-WhatIf.psm1

function Show-WhatIfOutput {
    [CmdletBinding(SupportsShouldProcess)]
    param(
    )

    Write-Host $MyInvocation.Line.Trim()

    if($PSCmdlet.ShouldProcess("My host","Display WhatIf text")){
        Write-Warning "This is not WhatIf text!"
    }

    Write-Host ("-"*40)
}

Test-WhatIf.ps1

Import-Module C:\Test-WhatIf.psm1 -Force

function Call-ShowWhatIf {
    [CmdletBinding(SupportsShouldProcess)]
    param(
    )

    Write-Host "$($MyInvocation.Line.Trim()) > " -NoNewline
    Show-WhatIfOutput

    Write-Host "$($MyInvocation.Line.Trim()) > " -NoNewline
    Show-WhatIfOutput -WhatIf
}

Write-Host ("-"*40)

Show-WhatIfOutput
Show-WhatIfOutput -WhatIf
Call-ShowWhatIf
Call-ShowWhatIf -WhatIf

Save both of these files to (say) C:\ and run the PS1 script. The output I receive is:

----------------------------------------
Show-WhatIfOutput
WARNING: This is not WhatIf text!
----------------------------------------
Show-WhatIfOutput -WhatIf
What if: Performing the operation "Display WhatIf text" on target "My host".
----------------------------------------
Call-ShowWhatIf > Show-WhatIfOutput
WARNING: This is not WhatIf text!
----------------------------------------
Call-ShowWhatIf > Show-WhatIfOutput -WhatIf
What if: Performing the operation "Display WhatIf text" on target "My host".
----------------------------------------
Call-ShowWhatIf -WhatIf > Show-WhatIfOutput
WARNING: This is not WhatIf text!
----------------------------------------
Call-ShowWhatIf -WhatIf > Show-WhatIfOutput -WhatIf
What if: Performing the operation "Display WhatIf text" on target "My host".
----------------------------------------

You can see that in the 2nd "block" of output I call the module function directly and get a WhatIf statement.

You can see that in the 5th "block" of output I call the module function from within the local Call-ShowWhatIf function, as get the warning to say that WhatIf was not set.



Solution 1:[1]

Caller Preference Variables are not propagated from Call-ShowWhatIf to Show-WhatIfOutput. AFAIK this is a known issue with functions called from a module. In this case, it's the $WhatIfPreference that is not being propagated.

Adding this to your Show-WhatIfOutput should resolve the issue:

if (-not $PSBoundParameters.ContainsKey('WhatIf'))
{
    $WhatIfPreference= $PSCmdlet.GetVariableValue('WhatIfPreference')
}

It checks if the caller (Call-ShowWhatIf) has -WhatIf specified. It does this when -WhatIf is not specified in the function call (Show-WhatIfOutput)

This is a similar post which described the same issue with -Verbose not propagating.

Related links:
PowerShell.org - Script Modules and Variable Scopes
Scripting Guy - Weekend Scripter: Access PowerShell Preference Variables
TechNet - Import Preference variables from the caller of a Script Module function

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 G42