Upgrade to PWSH Without Being Destructive (even for DCs)

'Update-to-PS7.ps1'
<#
.SYNOPSIS
Script to download and “install” the portable PowerShell 7 (zip) to C:\powershell without being destructive.

.DESCRIPTION
  - Uses GitHub API to get the latest stable PowerShell 7 release
  - Installs to $targetRoot
  - Unzips the portable archive there
  - Adds the install directory to the current user’s PATH
  - Leaves pwsh.exe in place so you can launch PowerShell 7 by typing `pwsh`

.NOTES
 - Make sure c:\PowerShell exists or alter the path of $targetRoot
 - Make sure you close all Powershell.exe, powershell_ise.exe and PWSH.exe windows before running and ONLY open powershell.exe "Run As Administrator"
 - This script requires PowerShell 5.1+ for Invoke-RestMethod and Expand-Archive
 - Also should be run as a local 'BUILTIN\Administrators' user to enable writing to C:\ or D:\ and modify registry rights for PATH
#>

[CmdletBinding()]
param()

function Test-IsAdmin {
  return ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
         ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

if (-not (Test-IsAdmin)) {
  Throw "This script must be run as Administrator."
}

$targetRoot = 'C:\powershell'    #<-- change this for the PowerShell folder path you use

Write-Host "Installing PowerShell 7 to $targetRoot..."

# Check if PowerShell 7 is already installed
if (Test-Path (Join-Path $targetRoot 'pwsh.exe')) {
  Write-Host "PowerShell 7 is already installed at $targetRoot. Skipping installation."
  return
}

# Prepare target folder
if (-not (Test-Path $targetRoot)) {
  New-Item -Path $targetRoot -ItemType Directory | Out-Null
}

Write-Host "Querying GitHub for the latest PowerShell 7 release..."
$release = Invoke-RestMethod `
  -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' `
  -Headers @{ 'User-Agent' = 'PowerShell-Install-Script' }

# Find the portable-win-x64.zip asset (adjust for x86 if you need 32-bit)
$zipAsset = $release.assets `
  | Where-Object { $_.name -match 'powershell-.*-win-x64\.zip$' } `
  | Select-Object -First 1

if (-not $zipAsset) {
  Throw "Could not locate the portable ZIP asset for x64 in release $($release.tag_name)."
}

# Download ZIP to temp
$tempZip = Join-Path $env:TEMP $zipAsset.name
Write-Host "Downloading $($zipAsset.browser_download_url) to $tempZip ..."
Invoke-WebRequest `
  -Uri $zipAsset.browser_download_url `
  -OutFile $tempZip `
  -UseBasicParsing

# Extract
Write-Host "Extracting archive to $targetRoot ..."
Expand-Archive -Path $tempZip -DestinationPath $targetRoot -Force

# Clean up temp ZIP
Remove-Item $tempZip -Force

# Add target to current user’s PATH if not already present
$oldPath = [Environment]::GetEnvironmentVariable('Path','User')
if (-not ($oldPath.Split(';') -contains $targetRoot)) {
  Write-Host "Adding $targetRoot to your user PATH..."
  $newPath = $oldPath + ';' + $targetRoot
  [Environment]::SetEnvironmentVariable('Path',$newPath,'User')
  Write-Host "User PATH updated. You will need to open a new shell to see the change."
} else {
  Write-Host "$targetRoot is already in your user PATH."
}

Write-Host "`nDone! Open a new terminal window and run 'pwsh' to launch PowerShell 7."