In the previous post I talked about the template I have for many of my PowerShell functions. And I've talked before about adding support for ShouldProcess to functions which change the state of the system (  that allows  –confirm, -prompt, –whatif and –verbose switches). Since then I’ve learnt that functions can identify the level of impact they have – this ties into the $confirmPreference variable to turn confirmation prompts on without needing to specify them explicitly. Adding shouldProcess support turns my function template into this:
Function Stop-VM{
          [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact='High')]
   Param( [parameter(Mandatory = $true, ValueFromPipeline = $true)]$VM ,
         
 $Server = "." ) 
 
Process{ if ($VM –is  [String]) {$VM = GetVM –vm $vm –server $server}
          if ($VM –is  [array]) {$VM | foreach-object {Stop-VM –vm $_ –server $server}}
          if ($VMis  [System.Management.ManagementObject] `
                 
–and $pscmdlet.shouldProcess($vm.ElementName, "Power-Off VM without Saving")) {
             
$vm.RequestStateChange(3)
           }
}}

There is one nuisance when dealing with more than one VM: we will get the following message for every VM

Confirm

Are you sure you want to perform this action?
Performing operation "Power-Off VM without Saving" on Target "London-DC".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): n

even if we select “Yes to all” or “No to all”.
Each recursive call to Stop-VM has its own instance of $psCMDLET. And if confirmImpact is set to High, that will cause a script to stop and prompt. Jason Shirk, one of the active guys in our internal PowerShell alias pointed out first you can have a –force switch to prevent the prompt appearing and secondly you don’t need to use the functions’ OWN instances of $psCMDLET: why not pass one instance around? So my function template morphed into the following:

Function Stop-VM{
         
[CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact='High')] 
   Param( [parameter(Mandatory = $true, ValueFromPipeline = $true)]$VM ,
         
 $Server = "." ,
           $PSC, 
           [Switch]$force)
 Process{  if ($psc -eq $null) {$psc = $pscmdlet}
          
if (-not $PSBoundParameters.psc) {$PSBoundParameters.add("psc",$psc)} 
           if ($VM –is [String])  {$VM = GetVM –vm $vm –server $server}
           if ($VM –is [array])   {[Void]$PSBoundParameters.Remove("VM")
                                   $VM | ForEach-object {Stop-Vm -VM $_ @PSBoundParameters}}
         
 if ($VM -is [System.Management.ManagementObject] `
                 
–and ($force -or $psc.shouldProcess($vm.ElementName, "Power-Off VM without Saving"))){
                
 $vm.RequestStateChange(3) 
          }
}}

So now the first function called sets $PSC, which is passed to all other functions - in this case recursive calls to this function – which use it in place of their instance of $psCmdlet. And –force is there to trump everything.