X Tutup
The Wayback Machine - https://web.archive.org/web/20220823172541/https://github.com/PowerShell/PowerShell/issues/12148
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShouldProcess does not emit Verbose Output even if $VerbosePreference is set #12148

Open
JustinGrote opened this issue Mar 18, 2020 · 3 comments
Labels
Issue-Question Up-for-Grabs WG-Engine

Comments

@JustinGrote
Copy link
Contributor

JustinGrote commented Mar 18, 2020

Steps to reproduce

function Test-Whatif {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        $String = 'Thing to do'
    )
    $target = 'Thing to Target'
    $VerbosePreference ='continue'
    if ($PSCmdlet.ShouldProcess($target,$string)){
        "Did $string to $target"
    }
}

Expected behavior

ShouldProcess would show Verbose output if $VerbosePreference is set either at the parent or in-function level

Actual behavior

Only works if -Verbose parameter is explicity specified on the cmdlet

image

Environment data

Name                           Value
----                           -----
PSVersion                      7.0.0
PSEdition                      Core
GitCommitId                    7.0.0
OS                             Microsoft Windows 10.0.19041
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
@JustinGrote JustinGrote added the Issue-Question label Mar 18, 2020
@iSazonov iSazonov added WG-Engine Up-for-Grabs labels Mar 19, 2020
@kilasuit
Copy link
Collaborator

kilasuit commented Mar 8, 2022

Sorry @JustinGrote but as this stand the actual behaviour is the expected behaviour and has been since as far as I can remember. It perhaps could be better documented in how to pass preferences between scopes in a way that respects the users set preferences

You can however control this by passing $VerbosePreference to -Verbose like so

$VerbosePreference = 'Continue'
Test-Whatif -Verbose:$VerbosePreference

You can confirm this by changing it back like so

$VerbosePreference = 'SilentlyContinue'
Test-Whatif -Verbose:$VerbosePreference

You can ofc get round explictly setting this by adding this in your $PSDefaultParameterValues like so (if you want to be less verbose in your cmdlet/function calls)

$PSDefaultParameterValues.Add('*:Verbose',$VerbosePreference)

and then changing the value as required in the top level scope just like I showed above but make sure to update the value in PSDefaultParameterValues like so

$PSDefaultParameterValues["*:Verbose"]=$VerbosePreference

The same logic as above also applies to other preference values and their respective parameters on advanced functions/cmdlets and also to Whatif/Confirm when they are enabled by the SupportsShouldProcess functionality.

However under no circumstances should a function/nested function override any preferences that a user has specified in the top scope of thier current session without being prompted to do so for them, but it can be done with Set-Variable and it's scope parameter like so

$ErrorActionPreference = 'Continue'
function Test-WhatifAndUpdateParentErrorPreference {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        $String = 'Thing to do'
    )
    $target = 'Thing to Target'
    $VerbosePreference ='continue'
    $ErrorActionPreference = 'Inquire'
    # Set variable in Parent scope 
    Set-Variable -Name ErrorActionPreference -Value $ErrorActionPreference -Scope 1   
if ($PSCmdlet.ShouldProcess($target,$string)){
        "Did $string to $target"
    }
    # Get variable in Parent scope
    Get-Variable -Name ErrorActionPreference -Scope 1 
}
Test-WhatifAndUpdateParentErrorPreference
$ErrorActionPreference

Note: if you do the above, please be a good citizen & ask to change it first, but if not you should remember to reset the value to what it was in the parent scope when you are done.

Also ShouldProcess only add's support for WhatIf/Confirm not verbose as currently documented

If this were to change it would IMO be a break to the User Experience that I can't see how much value this would bring when you can do the above (which I know is pure PowerShell but should be able to be replicated easily enough in other implementations)

@JustinGrote
Copy link
Contributor Author

JustinGrote commented Mar 8, 2022

@kilasuit you did not interpret my issue correctly. My issue is specifically on the verbose output of ShouldProcess when ShouldProcess is used.

Here's a better example:

function VerboseOverride {
  [CmdletBinding()]
  param()
  $verbosepreference = 'continue'
  Write-Verbose "Works even if silentlycontinue is set"
}

$verbosePreference = 'silentlycontinue'
VerboseOverride #Emits the verbose message

function VerboseOverrideShouldProcess {
  [CmdletBinding(SupportsShouldProcess)]
  param()
  $verbosepreference = 'continue'
  $null = $PSCmdlet.ShouldProcess('target','action')
}
$verbosePreference = 'silentlycontinue'
VerboseOverrideShouldProcess #Doesnt emit the verbose message unless -Verbose is explicitly specified

VerboseOverrideShouldProcess -Verbose #Emits the message

$verbosePreference = 'continue'
VerboseOverrideShouldProcess #Still doesn't emit the verbose message unless -Verbose is explicitly specified even though *CALLER* verbose preference says 'continue'

Do you see the issue here now? It basically ignores verbosepreference entirely whether it is in caller scope or not, and ONLY emits a verbose message if the -Verbose parameter is specified. This leads to inconsistent output.

@Jaykul
Copy link
Contributor

Jaykul commented Mar 9, 2022

Yeah, and for the record, these also do not work:

$verbose = $true
$PSBoundParameters['Verbose'] = $true
$MyInvocation.BoundParameters['Verbose'] = $True

The only thing that does work:

function VerboseOverrideShouldProcess {
  [CmdletBinding(SupportsShouldProcess)]
  param()
  
  # I'm just the messenger.
  $PSCmdlet.CommandRuntime.GetType().GetProperty("Verbose", [Reflection.BindingFlags]"NonPublic,Instance").SetValue($PSCmdlet.CommandRuntime, $True, $null)

  $null = $PSCmdlet.ShouldProcess('target','action')
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Question Up-for-Grabs WG-Engine
Projects
None yet
Development

No branches or pull requests

4 participants
X Tutup