Search code examples
powershellinvoke

Possible to use -WhatIf and the invocation operator (&)?


Is it possible to use the -WhatIf argument when executing external commands? I want to be able to run a script with -WhatIf and have it print out a full list of all the external commands and arguments it's going to run without actually running them.

I've tried doing stuff like the following:

Function Invoke-Checked
{
    param([ScriptBlock]$s)

    if ($PSCmdlet.ShouldProcess($s.ToString(), "Execute"))
    {
        Invoke-Command $s
    }
}

But that won't expand any variables that are present in the scriptblock - doing something like:

$s = { & dir $test }
Invoke-Checked $s

just prints

Performing the operation "Execute" on target " & dir $test ".

not particularly helpful.

Is there any way to do what I want?


Solution

  • First of all - you need to make sure that your 'wrapper' function supports WhatIf. Another thing: you can expand the scriptBlock, but I'm not really convinced that is smart thing to do: e.g. if $test = 'Some path with spaces', it would stop working after expansion.

    That being said: here are two options that work for me: using GetNewClosure() method on scriptBlock, and expanding whole thing:

    function Invoke-ExpandedChecked {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Medium'
    )]
        param([ScriptBlock]$ScriptBlock)
    
        $expanded = $ExecutionContext.InvokeCommand.ExpandString($ScriptBlock)
        $script = [scriptblock]::Create($expanded)
        if ($PSCmdlet.ShouldProcess($script.ToString(), "Execute"))
        {
            & $script
        }
    }
    
    function Invoke-Checked {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Medium'
    )]
        param([ScriptBlock]$ScriptBlock)
    
        $newClosure = $ScriptBlock.GetNewClosure()
        if ($PSCmdlet.ShouldProcess($newClosure.ToString(), "Execute"))
        {
            & $newClosure
        }
    }
    
    $test = '.\DSCDemo.ps_'
    $s = { cmd /c dir $test} 
    
    Invoke-Checked $s -WhatIf
    Invoke-Checked $s
    Invoke-ExpandedChecked $s -WhatIf
    Invoke-ExpandedChecked $s
    

    And an example of results for path with spaces:

    $test = 'C:\Program Files'
    Invoke-Checked $s
    Invoke-ExpandedChecked $s
    

    Works fine for one with new enclosure. With expanded:

    cmd : File Not Found
    At line:1 char:2
    +  cmd /c dir C:\Program Files