powershellenvironment-variablesrestore

In Powershell how to store the current environment variables and restore them as they were


I am looking for a method to save all environment variables of the currently running process to a variable and restore them back from that saved state.

This is how far I got:

$OldEnvironment = Get-ChildItem env:
try
{
    # Manipulate a variable
    $env:PATH="C:\My\Utilities\Path;$env:PATH"
    # Remove some
    Remove-Item -Path Env:ALLUSERSPROFILE
    Remove-Item -Path Env:APPDATA
    Remove-Item -Path Env:LOCALAPPDATA
    # Set a new one which we presume did not exist before
    Set-Item -Path env:NEWSHINYVARIABLE "FOOBAR"
    # Output current environment
    Get-ChildItem env:|ft
}
finally
{
    # Restore ...
    $OldEnvironment|%{ Set-Item -Path "Env:$($_.Name)" "$($_.Value)" }
    # ... alas there's a glitch here:
    Get-ChildItem -Path env:NEWSHINYVARIABLE|ft
    # NEWSHINYVARIABLE _still_ exists
}

Now my issue is to make sure that variables set inside the try block don't leak out (like NEWSHINYVARIABLE does). Removed variables aren't an issue, because they will simply get created anew.

How can also remove variables currently contained in env: but not stored in $OldEnvironment with a concise one-liner? (Please note: I do not want to resort to modules and background jobs which - as I understand - contain their own private environment. I want to stay with the try .. finally method structurally.)


Solution

  • Use Compare-Object to compare the content of the Env: PowerShell drive before and after, and take appropriate actions to restore the values of preexisting or remove newly added environment variables:

    # Set some sample env. vars.
    $env:foo='original foo'
    $env:bar='original bar'
    
    # Get the current list of env. vars. and their values
    $envVarsBefore = Get-ChildItem Env:
    
    try {
      # Perform sample operations that add, update, and delete environment variables.
      $env:foo='changed foo'
      $env:bar=$null # Remove $env:bar
      $env:goaway='yes' # define a new var.
    } finally {
      # Restore the original environment, including removal of newly added variables.
      Compare-Object -PassThru $envVarsBefore (Get-ChildItem Env:) -Property { '[{0}, {1}]' -f $_.Key, $_.Value } | 
        ForEach-Object {
          # A newly added or updated variable: remove it.
          # (If it was updated, it'll be restored with its original value below.)
          if ($_.SideIndicator -eq '=>') {
            Remove-Item Env:$($_.Name)
          }
          else { # $_.SideIndicator -eq '<='
            # Restore a deleted variable or a modified's variable original value.
            Set-Item Env:$($_.Name) $($_.Value)
          }
        }
    }
    
    # Check if the original environment was restored:
    Get-Item Env:foo, Env:bar, Env:goaway
    

    The last command in the code above prints the original (restored) values of $env:foo and $env:bar and - as expected - reports an error for the no-longer-extant $env:goaway variable.

    Note: