Give advanced PowerShell functions and scripts an opt-in to limit verbose output to explicit Write-Verbose calls
See original GitHub issueFollow-up from #13636:
Note:
-
Substantially revised after the discussion below.
-
While the verbose stream is being discussed, the same applies analogously to the warning, debug, and information streams.
Summary of the new feature/enhancement
As a script/function author, I want to be able to create advanced functions / scripts that are cmdlet-like and deliberately limit verbose output to explicit Write-Verbose calls only (by default).
Because of PowerShell’s dynamic scoping, the stream-controlling $*Preference variables are visible to all child scopes of a given script/function and also apply to cmdlet calls inside them.
This also applies to the stream-controlling common parameters (e.g., -Verbose), which PowerShell automatically translates into function/script-local $*Preference variables.
While this can be helpful in troubleshooting a function or script, it can also lead to a flood of unwanted verbose output that drowns out the verbose output of interest. (A related problem are the verbose messages that can surface due to auto-loading of the module containing the command being invoked - see #13657.)
For instance, in the following (simulated) advanced function, I may want to show the user the explicit Write-Verbose call’s output only, not also the verbose messages produced by the Import-Module call, and the (simulated) call to another script or function.
# The script block represents an *advanced function*.
& {
[CmdletBinding()]
param()
# This also produces verbose output.
Import-Module PSReadLine
# Simulate a call to other PowerShell functions / scripts:
# This also produces verbose output.
& {
Write-Verbose 'in child scope'
}
# The only explicit Write-Verbose call
Write-Verbose Done.
} -Verbose
For carefully crafted, cmdlet-like advanced scripts/functions, it would be helpful to have an opt-in mechanism that makes PowerShell limit verbose output to explicit Write-Verbose calls only (by default), so that the verbose output isn’t “polluted” by verbose output from internal calls that should be considered implementation details.
While there are workarounds, they are cumbersome; here’s one, which demonstrates the desired behavior:
& {
[CmdletBinding()]
param()
# If verbose output is turned on, hide the setting from the descendant scopes,
# but make all Write-Verbose calls *in this scope only* emit verbose output.
if ($VerbosePreference -eq 'Continue') {
$VerbosePreference = 'SilentlyContinue' # Hide from cmdlet calls and descendant scopes.
# Apply only to Write-Verbose calls in the current scope.
$private:PSDefaultParameterValues = $PSDefaultParameterValues ? $PSDefaultParameterValues.Clone() : [System.Management.Automation.DefaultParameterDictionary]::new()
$private:PSDefaultParameterValues['Write-Verbose:Verbose'] = $true
}
# This is now quiet.
Import-Module PSReadLine
# Simulate a call to other PowerShell functions / scripts:
# This is now quiet too.
& {
Write-Verbose 'in child scope'
}
# Only this will print.
Write-Verbose Done.
} -Verbose
Proposed technical implementation details (optional)
Implement a new StreamPreferencePropagation property on the CmdletBinding attribute, which defaults to $true and can be set to $false (analogous to the PositionalBinding property):
& {
# Note the new property.
[CmdletBinding(StreamPreferencePropagation = $false)]
param()
# This is now quiet.
Import-Module PSReadLine
# Simulate a call to other PowerShell functions / scripts:
# This is now quiet too.
& {
Write-Verbose 'in child scope'
}
# Only this will print.
Write-Verbose Done.
} -Verbose
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:6 (4 by maintainers)

Top Related StackOverflow Question
I think this will annoy roughly as many people as it pleases. There are functions which have no write-verbose statements which are wrappers for another command which we want the detail from (IIRC I did this a lot to find what went into invoke-restmethod in other functions). I’ve also written functions where I expressly called other things with
-verbose:$falsebecause I did write to verbose in the function, and tens or hundreds of function calls with verbose made it hard to see the wood for the trees.I’m not sure which is the more common case, but my gut says its not 10 or 20 of one to one of the other.
@jazzdelightsme
It actually does stop a module boundaries, but that is in itself highly problematic: #4568
Yes, it’s an effective approach (which @jhoneill also mentioned), but my concern was that it is cumbersome and easy to forget.
But the point about intra-module helper code is a valid one.
I’m closing this, and let’s hope that fixing #13657 alone will go a long way toward mitigating the problem.