Search code examples
powershellconfirm

Should Whatif and ConfirmImpact have an else clause?


I want to include Whatif and Confirm to my functions but I encountered an issue with these parameters. My functions are structured like this:

function Get-Stuff {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
  param ( {...} )
  process {
    if ($PSCmdlet.ShouldProcess($Name, "Delete user")) {
      $result = Invoke-RestMethod @restBody            
    }
  }
  end {
    Write-Output -InputObject $result
    Remove-Variable -Name result
  }
}

I took on a habit to clean up my variables in the end-block with Remove-Variable. When I use now the -WhatIf or the -Confirm parameter (and denying it), I get an error that the $result variable is null.

ErrorRecord                 : Cannot find a variable with the name 'result'.

I understand that the RestMethod is skipped in this case but I would assume that the rest of the function would not be executed further.

My question is now, does one add an else-clause to end the continuing execution of the function or do I use these parameters incorrectly?


Solution

  • There's no good reason to remove your variables in the end block, since they go out of scope automatically anyway, given that they're local to your function.

    (The only thing that makes sense is to .Dispose() of variables containing objects that implement the System.IDisposable interface; if releasing memory as quickly as possible is paramount - at the expense of blocking execution temporarily - you can additionally call [GC]::Collect(); [GC]::WaitForPendingFinalizers())

    If you still want to call Remove-Variable, you have two options:

    • Simply ignore a non-existent variable by adding -ErrorAction Ignore to the Remove-Variable call.

      Remove-Variable -Name result -ErrorAction Ignore
      
    • Alternatively, protect the call - and the Write-Output object - with an explicit test for the existence of the variable:

      if (Get-Variable -Scope Local result -ErrorAction Ignore) {
        $result # short for: Write-Output -InputObject 
        Remove-Variable -Name result
      }
      

    Also note that it's typical for output objects to be emitted directly from the process block - emitting from the end block is only a necessity for commands that for conceptual reasons must collect all input first, such as Sort-Object.

    Emitting output objects from the process block - which is invoked for each input object - ensures the streaming output behavior - emitting objects one by one, as soon as they're available - that the pipeline was designed for.