Skip to content

Instantly share code, notes, and snippets.

@PanosGreg
Last active October 15, 2021 15:26
Show Gist options
  • Save PanosGreg/b5cee510dbbd7bb6150a7ea1d6e28fbd to your computer and use it in GitHub Desktop.
Save PanosGreg/b5cee510dbbd7bb6150a7ea1d6e28fbd to your computer and use it in GitHub Desktop.
Install-PowerShell
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