diff --git a/tools/windows/Reset-PWSHSystemPath.ps1 b/tools/windows/Reset-PWSHSystemPath.ps1 new file mode 100644 index 00000000000..eb3b182d419 --- /dev/null +++ b/tools/windows/Reset-PWSHSystemPath.ps1 @@ -0,0 +1,77 @@ +<# +.SYNOPSIS + Idempotently removes extra PowerShell Core paths from the machine, user and/or process environment scope with no reordering. + +.DESCRIPTION + Defaults to machine scope and leaving the last sorted path alone. + Does not touch path if there is nothing to clean. + Emits one simple log line about it's actions for each scope. + + Also accessible in the powershell-core Chocolatey package by using -params '"/CleanUpSystemPath"' + +.PARAMETER PathScope + Set of machine scopes to clean up. Valid options are one or more of: Machine, User, Process. + Default: machine + +.PARAMETER RemoveAllOccurences + By default the cleanup leaves the highest sorted PowerShell Core path alone. + This switch causes it to be cleaned up as well. + Default: false + +.EXAMPLE + .\Reset-PWSHSystemPath.ps1 + + Removes all PowerShell core paths but the very last one when sorted in ascending order from the Machine level path. + Good for running on systems that already has at least one valid PowerShell install. + +.EXAMPLE + .\Reset-PWSHSystemPath.ps1 -RemoveAllOccurences + + Removes ALL PowerShell core paths from the Machine level path. + Good for running right before upgrading PowerShell core. +.EXAMPLE + .\Reset-PWSHSystemPath.ps1 -PathScope Machine, User, Process + + Removes all paths but the very last one when sorted in ascending order. + Processes all path scopes including current process. +.EXAMPLE + .\Reset-PWSHSystemPath.ps1 -PathScope Machine, User, Process -RemoveAllOccurencs + + Removes all paths from all path scopes including current process. +#> +param ( + [ValidateSet("machine","user","process")] + [string[]]$PathScope="machine", + [switch]$RemoveAllOccurences +) + +#Do the cleanup for all specified path scopes +ForEach ($PathScopeItem in $PathScope) +{ + $AssembledNewPath = $NewPath = '' + #From the current path scope. retrieve the array of paths that match the pathspec of PowerShell Core (to use as a filter) + $pathstoremove = @([Environment]::GetEnvironmentVariable("PATH","$PathScopeItem").split(';') | Where { $_ -ilike "*\Program Files\Powershell\6*"}) + If (!$RemoveAllOccurences) + { + #If we are not removing all occurances of powershell core paths, then remove the highest sorted path from the filter + $pathstoremove = @($pathstoremove | sort-object | Select-Object -skiplast 1) + } + Write-Verbose "Reset-PWSHSystemPath: Found $($pathstoremove.count) paths to remove from $PathScopeItem path scope: $($Pathstoremove -join ', ' | out-string)" + If ($pathstoremove.count -gt 0) + { + foreach ($Path in [Environment]::GetEnvironmentVariable("PATH","$PathScopeItem").split(';')) + { + #rebuild the path in the same order, but eliminate the paths in the filter array and blanks + If ($Path) + { + If ($pathstoremove -inotcontains "$Path") + { + [string[]]$Newpath += "$Path" + } + } + } + $AssembledNewPath = ($newpath -join(';')).trimend(';') + $AssembledNewPath -split ';' + [Environment]::SetEnvironmentVariable("PATH",$AssembledNewPath,"$PathScopeItem") + } +}