10

According to answers like this one and my own experience, Powershell can take care of propagating -Verbose (and -Debug) automatically, which is very convenient. However this stops working when the functions which I want to propagate verbosity to are in a module. Code used for testing this:

Create a directory called Mod somewhere, suppose in c:, and add 2 files:

File c:\Mod\Functions.ps1:

function Show-VerbosityB { [cmdletbinding()]Param()
  Write-Output "Show-VerbosityB called"
  Write-Verbose "Show-VerbosityB is Verbose"
}

File c:\Mod\Mod.psd1:

@{
ModuleVersion = '1.0.0.0'
NestedModules = @('Functions.ps1')
FunctionsToExport = @('*-*')
}

Now crate the main script, say c:\Foo.ps1:

Import-Module c:\Mod

function Show-VerbosityA { [cmdletbinding()]Param()
  Write-Output "Show-VerbosityA called"
  Write-Verbose "Show-VerbosityA is Verbose"
}

function Show-Verbosity { [cmdletbinding()]Param()
  Write-Output "Show-Verbosity called"
  Write-Verbose "Show-Verbosity is Verbose"
  Write-Output "Testing propagation"
  Show-VerbosityA
  Show-VerbosityB
}

Show-Verbosity -Verbose

Results in

PS> . C:\Foo.ps1
Show-Verbosity called
VERBOSE: Show-Verbosity is Verbose
Testing propagation
Show-VerbosityA called
VERBOSE: Show-VerbosityA is Verbose
Show-VerbosityB called

Why is the Write-Verbose in the module's function skipped, why does propagation not behave like it does for Show-VerbosityA? (If I just dot-source Functions.ps1 instead of importing the module, the line VERBOSE: Show-VerbosityB is Verbose is printed). I could make propagation manual by e.g. calling Show-VerbosityB -Verbose:$PSBoundParameters['Verbose']. Or are there other, preferrably shorter, ways? It is quite messy if functions behave differently depending on whether they are part of a module or dot-sourced.

stijn
  • 34,664
  • 13
  • 111
  • 163

2 Answers2

12

The reason this is happening is because the $VerbosePreference is not propagated when the module is called. I modified your script to explicitly print the value at the same points you are outputting via Write-Verbose and Write-Output.

This powershell.org post proposes adding this to the module, which worked like a charm for me:

if (-not $PSBoundParameters.ContainsKey('Verbose'))
{
    $VerbosePreference = $PSCmdlet.GetVariableValue('VerbosePreference')
}

One of the comments mentions bug report with link (it doesn't exist or I don't have permissions to view)

The issue is discussed in a TechNet post, with a link to a Get-CallerPreferance function that addresses this issue.


Module:

function Show-VerbosityB { [cmdletbinding()]Param()

    <# uncomment to get verbose preference from caller, when verbose switch not explicitly used.
    if (-not $PSBoundParameters.ContainsKey('Verbose'))
    {
        $VerbosePreference = $PSCmdlet.GetVariableValue('VerbosePreference')
    }
    #>

    Write-Output "`nShow-VerbosityB called"
    Write-output "Global pref: $($global:VerbosePreference)"
    Write-output "Script pref: $($script:VerbosePreference)"
    Write-output "Effect pref: $VerbosePreference"
    Write-Verbose "Show-VerbosityB is Verbose"
}

Caller:

Import-Module C:\Mod

Write-output "On startup: $VerbosePreference"

function Show-VerbosityA { [cmdletbinding()]Param()
  Write-Output "`nShow-VerbosityA called"
  Write-output "Global pref: $($global:VerbosePreference)"
  Write-output "Script pref: $($script:VerbosePreference)"
  Write-output "Effect pref: $VerbosePreference"
  Write-Verbose "Show-VerbosityA is Verbose"
}

function Show-Verbosity { [cmdletbinding()]Param()
  Write-Output "`nShow-Verbosity called"
  Write-output "Global pref: $($global:VerbosePreference)"
  Write-output "Script pref: $($script:VerbosePreference)"
  Write-output "Effect pref: $VerbosePreference"
  Write-Verbose "Show-Verbosity is Verbose"
  Write-Output "`nTesting propagation"
  Show-VerbosityA
  Show-VerbosityB
}

Show-Verbosity -Verbose
G42
  • 9,791
  • 2
  • 19
  • 34
  • 2
    Thanks, this is the answer indeed. Unfortunately not exactly what I was hoping for :] Modifying every function to deal with this is crazy, so if I need all verbosity I'll probably just resort to something like `$global:VerbosePreference = [System.Management.Automation.ActionPreference]::Continue`.. – stijn Jul 04 '17 at 14:19
  • Global variables are ugly. Calling the linked `Get-CallerPreferance` function is a one-liner. For me it's worth it to let my module functions behave like built-in cmdlets and not having to worry at the caller site. – zett42 Apr 15 '20 at 17:24
  • Weird, this isn't working for me (Get-CallerPreference etc). The $PSCmdlet value is present but as soon as I call across to a function from another psm1 the functions in that other psm1 are seeing $PSCmdlet as null... – DaveUK Jan 05 '21 at 20:54
  • 1
    Never mind! My issue was that I wasn't specifying [cmdletbinding()]param on the functions within my other psm1. Adding the cmdletbinding() caused the $PSCmdlet variable to work. – DaveUK Jan 05 '21 at 20:57
  • More technically correct, things like `VerbosePrefreence`, `DebugPreference`, and `WhatIfPreference` may not be properly propagated between functions in different _script_ modules. [Microsoft is aware of this issue and has, for some time, been trying to fix this inconsistent behavior.](https://github.com/PowerShell/PowerShell-RFC/pull/221#issuecomment-592954839) – fourpastmidnight Nov 03 '22 at 14:55
1

If you want set from the caller script, try this:

(Get-Module 'ModuleName').SessionState.PSVariable.Set('Global:VerbosePreference', $VerbosePreference )

Rodrigo
  • 92
  • 6