Reputation: 1721
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
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