Best practice in powershell Try/Catch/Finally scripting?

Viewed 309

I wonder if this is possible to remove all variables used in the current script session?

Try {
    Write-Host "Scripting start...."
}
Catch {
    Write-Warning -Message "[PROCESS] Something wrong happened"
    Write-Warning -Message $Error[0].Exception.Message
}
Finally {
    Remove-Variable *
    [System.GC]::Collect() 
}

If not, what can I do in the Finally block?

2 Answers

I believe you can use:

Clear-Variable * -Scope Global

OR

Remove-Variable * -Scope Global

Alternatively, would scoping the process resolve your issue? You can create a scriptblock and call it with the "call operator" (&). This would run the process in a scoped env separate from the global one. It would look something like this:

$scriptBlock = { 
    Try {
        Write-Host "Scripting start...."
    }
    Catch {
        Write-Warning -Message "[PROCESS] Something wrong happened"
        Write-Warning -Message $Error[0].Exception.Message
    }
    Finally {
        [System.GC]::Collect() 
    }
}

&$scriptBlock

You'd have no need to Remove-Variable, because after the execution, anything that occurred within the scope in terms of variables is already gone.

It is a best practice to name your custom variable with some entropy so that they are easily found and removed. Removing '*' removes everything regardless of when/how it was created, including all PS default variables.

Don't do this.

At each session run, collect that defaults and automatic ones in a variable, before you end your session compare that variable collection to what you created and only remove what you created.

So, in my case, I'd use, say ...

$panVariableName 

... then

Remove-Variable -Name 'pan*' -Force

Or if you do not want to do it this way. At session start, do this...

$AutomaticVariables = Get-Variable

... then you can compare any variables you create regardless of what you name them to get your removal collection. Here is a function I keep in my module profile for such use cases for cleanup relative to this approach.

So, in my module profile, this is at the top...

$AutomaticVariables = Get-Variable

Then when I am ready this function is called.

Function Clear-ResourceEnvironment
{
    [CmdletBinding(SupportsShouldProcess)]
    [Alias('cre')]
    
    Param
    (
        [switch]$AdminCredStore
    )
    
 
    [System.GC]::Collect()
    [GC]::Collect()
    [GC]::WaitForPendingFinalizers()
    
    Get-PSSession | 
    Remove-PSSession -ErrorAction SilentlyContinue
    
    If ($AdminCredStore)
    {Remove-Item -Path "$env:USERPROFILE\Documents\AdminCredSet.xml" -Force}
    Else 
    {
        Write-Warning -Message "`n`t`tYou decided not to delete the custom Admin credential store. 
        This store is only valid for this host and user $env:USERNAME"
    } 
    
    Write-Warning -Message "`n`t`tRemoving the displayed session specific variable ojects"
    
    Compare-Object -ReferenceObject (Get-Variable) -DifferenceObject $AutomaticVariables -Property Name -PassThru | 
    Where -Property Name -ne 'AutomaticVariables' | 
    Remove-Variable -Verbose -Force -Scope 'global' -ErrorAction SilentlyContinue
    Remove-Variable -Name AdminCredStore -Verbose -Force
}
Related