jimbobmcgee
jimbobmcgee

Reputation: 1721

Suppressing called function/cmdlet's WhatIf output, when that function has no WhatIf parameter

TL;DR - How do I specify -WhatIf:$false when a function/cmdlet does not have a -WhatIf parameter but still reacts to a -WhatIf in the parent call?

Consider the following slightly-contrived example:

Import-Module ActiveDirectory

Function Test-MyFunction {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
    Param(
        [Parameter(Mandatory=$true)]
        [String] $Identity
    )

    $private:q = ($Identity -replace "'","''")
    if (Get-ADServiceAccount -Filter "sAMAccountName -eq '${private:q}'") {
        if (-not (Test-ADServiceAccount -Identity $Identity `
                                        -WarningAction 'SilentlyContinue' `
                                        -ErrorAction 'SilentlyContinue'))
        {
            if ($PSCmdlet.ShouldProcess($Identity, 'Install')) {
                Install-ADServiceAccount -Identity $Identity
            }
            else {
                Write-Verbose "Skipped processing '$Identity'"
            }
        }
        else {
            Write-Verbose "Already registered '$Identity'"
        }
    }
    else {
        Write-Warning "'$Identity' does not exist"
    }
}

...and it's possible outputs:

PS> Test-MyFunction -Verbose -Identity 'nonexistent$'
WARNING: 'nonexistent$' does not exist

PS> Test-MyFunction -Verbose -Identity 'registered$'
VERBOSE: Already registered 'registered$'

PS> Test-MyFunction -Verbose -Identity 'notregistered$'
Confirm
Are you sure you want to perform this action?
Performing the operation "Install" on target "notregistered$"
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): n
VERBOSE: Skipped processing 'notregistered$'

If I add -WhatIf to those calls, you instead get the following:

PS> Test-MyFunction -WhatIf -Verbose -Identity 'nonexistent$'
WARNING: 'nonexistent$' does not exist

PS> Test-MyFunction -WhatIf -Verbose -Identity 'registered$'
What if: Performing the operation "Test" on target "CN=registered,CN=Managed Service Accounts,DC=contoso,DC=local"
What if: Performing the operation "Install" on target "registered$"
VERBOSE: Skipped processing 'registered$'

PS> Test-MyFunction -WhatIf -Verbose -Identity 'notregistered$'
What if: Performing the operation "Test" on target "CN=notregistered,CN=Managed Service Accounts,DC=contoso,DC=local"
What if: Performing the operation "Install" on target "notregistered$"
VERBOSE: Skipped processing 'notregistered$'

By specifying -WhatIf on my function call, it has passed that preference on the functions called by my function. This is by design and, in most cases, what you would want.

For completeness, Test-ADServiceAccount is supposed to return $true or $false depending on whether a particular Managed Service has been installed to the local computer (at least, it does if you suppress its warnings/errors).

However, in this instance, due to the way Test-ADServiceAccount seems to have been written, if -WhatIf is supplied on my function call, it never actually performs the test, so always returns $false. This means that my if/else logic is broken -- my function should not indicate that Install-ADServiceAccount would be called for registered$ if -WhatIf was removed.

In normal circumstances, I would simply add -WhatIf:$false on to the end of the called function, but Test-ADServiceAccount does not expose its own -WhatIf parameter:

PS> Test-ADServiceAccount -WhatIf:$false -Identity 'registered$'
Test-ADServiceAccount : A parameter cannot be found that matches parameter name 'WhatIf'.

I appreciate that there are probably better ways to write the function, and that there is no harm really done here because Install-ADServiceAccount wasn't run anyway, but this function was intended as a MCVE, rather than something to be explicitly improved upon. I also appreciate that Test-ADServiceAccount may not need -WhatIf semantics and probably shouldn't itself do $PSCmdlet.ShouldProcess (which I assume it is doing), but ActiveDirectory isn't my module and I don't see me changing its behaviour any time soon.

In the general case, how could I override the -WhatIf behaviour for a called cmdlet/function I don't control, from within my own function, when my function is called with its -WhatIf parameter and the called function does not expose its own -WhatIf parameter.

Upvotes: 3

Views: 393

Answers (1)

Moerwald
Moerwald

Reputation: 11294

You could set the $WhatIfPreference to $false before calling Test-AdServiceAccount. Afterwards you can restore the preference

    function Function1 {
        [CmdletBinding(SupportsShouldProcess = $true)]
        param (
        )

        $origWhatIfPreference = $WhatIfPreference.IsPresent

        $WhatIfPreference = $false
        Function2
        $WhatIfPreference = $origWhatIfPreference

        if ($PSCmdlet.ShouldProcess("target", "command")) {
            Write-Host "test"
        }
    }

    function Function2 {
        [CmdletBinding(SupportsShouldProcess = $true)]
        param (
        )
        if ($PSCmdlet.ShouldProcess("f2: target", "f2: command")) {
            Write-Host "f2: test"
        }
    }

Found $WhatIfPreference.IsPresent in this stackoverflow answer.

It seems that Test-AdServiceAccount detects the WhatIf switch in a dynamic way, e.g. via $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference')

Update:

Based on @Jessens comment, you can also wrap a specific function in a script block and invoke it invoke-command { $WhatIfPreference = $false; Function2;}, here you won't ne to reconstruct the old $WhatIfPreference state.

Hope that helps.

Upvotes: 2

Related Questions