Last active
October 15, 2021 15:26
-
-
Save PanosGreg/b5cee510dbbd7bb6150a7ea1d6e28fbd to your computer and use it in GitHub Desktop.
Install-PowerShell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Install-PowerShell { | |
<# | |
.SYNOPSIS | |
Installs latest PowerShell 7 via msi, from GitHub. | |
You need to: | |
a) run this from PS 5 (not PS 7) on windows (not linux) | |
b) have internet connectivity (and able to access github.com) | |
c) and have rights to install apps (usually local admin privileges) | |
.EXAMPLE | |
Install-PowerShell -Verbose | |
.NOTES | |
Author: Panos Grigoriadis | |
Date: 15/10/2021 | |
#> | |
[OutputType([System.Void])] | |
[OutputType([System.Boolean], ParameterSetName='OutBool')] | |
[OutputType([Microsoft.PackageManagement.Packaging.SoftwareIdentity], ParameterSetName='OutObject')] | |
[CmdletBinding(DefaultParameterSetName='__AllParameterSets')] | |
param ( | |
[int]$Timeout = 5, # in minutes | |
[Parameter(ParameterSetName='OutBool')] | |
[switch]$Quiet, # [bool] output | |
[Parameter(ParameterSetName='OutObject')] | |
[switch]$PassThru # [psobject] output | |
) | |
$PSDefaultParameterValues = @{'Remove-Job:Verbose' = $false} | |
$IsWinPS = $PSVersionTable.PSVersion.Major -eq 5 | |
if (-not $IsWinPS) { | |
return 'Please run this from Windows PowerShell 5.x, not from PowerShell 6+' | |
# that's because if you run this on PS7, you can't update its own process | |
} | |
# create a PS job for the installation | |
$timer = [Diagnostics.StopWatch]::StartNew() | |
$block = { | |
$PSDefaultParameterValues = @{ | |
'Invoke-RestMethod:Verbose' = $false | |
'Start-Process:Verbose' = $false | |
} | |
$ErrorActionPreference = 'Stop' | |
try { | |
$repo = 'PowerShell/PowerShell' | |
$url = "https://api.github.com/repos/$repo/releases/latest" | |
$ass = (Invoke-RestMethod $url).assets_url | |
$all = (Invoke-RestMethod $ass).browser_download_url | |
$msi = $all | where {$_ -like '*win-x64.msi'} | |
$cmd = "/package $msi /quiet REGISTER_MANIFEST=1" # ENABLE_PSREMOTING=1 | |
Start-Process msiexec.exe -Wait -ArgumentList $cmd | |
} | |
catch {throw $_} | |
} | |
Write-Verbose 'Starting installation' | |
$job = Start-Job -ScriptBlock $block | |
# wait for the install to finish | |
$every = (10,30,60,120,240) ; $i = 0 # <-- exponential back-off on the wait feedback | |
$StartTime = Get-Date ; $TimeoutExpired = $false ; $IsDone = $false | |
while (-not $IsDone -and -not $TimeoutExpired) { | |
$Elapsed = (Get-Date) - $StartTime | |
$TimeoutExpired = $Elapsed.TotalMinutes -gt $Timeout | |
if ($TimeoutExpired) {Write-Warning 'Timeout expired';break} | |
$sec = $Elapsed.TotalSeconds ; $dur = $Elapsed.ToString('mm\:ss') | |
$chk = $sec % $every[$i] ; $wait = 5 # in seconds | |
if ($chk -lt $sec -and $chk -le $wait -and $sec -ge $wait) { | |
$i++ | |
Write-Verbose "Wait for install to finish [$dur]" | |
} | |
Start-Sleep -Seconds $wait | |
$IsDone = $job.State -eq 'Completed' -or $job.State -eq 'Failed' | |
$job = Get-Job -Id $job.Id | |
} | |
# prepare some variables for the output | |
if ($job.State -eq 'Failed') {$Err = $job.ChildJobs[0].JobStateInfo.Reason.ErrorRecord} | |
if ($job.State -eq 'Completed') {$Pkg = Get-Package -ProviderName msi | where Name -like 'PowerShell*'} | |
# Note: Get-Package only works on PS5, not PS6 or PS7, cause it can't access the msi provider (as of yet) | |
# Note: To get all job states [Enum]::GetNames([System.Management.Automation.JobState]) | |
$job | Remove-Job -Force -ErrorAction Ignore | |
$timer.Stop() | |
$total = $timer.Elapsed.ToString('mm\:ss') | |
$Complet = $job.State -eq 'Completed' | |
$Failed = $job.State -eq 'Failed' | |
$NotDone = $job.State -NotMatch 'Completed|Failed' | |
$HasPkg = $Pkg -as [bool] | |
$OutObj = $PassThru.IsPresent | |
# finally show the output | |
if ($Complet -and $HasPkg) {Write-Verbose "Installed successfully! [$total]"} | |
if ($Complet -and $HasPkg -and $Quiet) {Write-Output $true} | |
if ($Complet -and $HasPkg -and $OutObj) {Write-Output $Pkg} | |
if ($Complet -and !$HasPkg -and !$Quiet) {Write-Warning 'PowerShell was NOT found on the system!'} | |
if ($Complet -and !$HasPkg -and $Quiet) {Write-Output $false} | |
if ($Failed -and !$Quiet) {Write-Error -ErrorRecord $Err} | |
if ($Failed -and $Quiet) {Write-Output $false} | |
if ($NotDone -and !$Quiet) {Write-Warning 'Could NOT install PowerShell'} | |
if ($NotDone -and $Quiet) {Write-Output $false} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment