-
-
Save rmdes/8afb812b5d51920342c9a8b4ca8c4a22 to your computer and use it in GitHub Desktop.
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
# https://4bes.nl/2021/09/19/update-all-powershell-modules-on-a-system/ | |
<# | |
TIM C: Changes: | |
- Added scope parameter so scope could be controlled | |
- altered code to always check for old versions, as this script may not have done the install, but it can still remove old versions | |
- changed contains and othercomparison syntax to be case insensitive | |
- altered logic around when the module is not found in the gallery to make the verbose output clearer | |
- added version parses around the version compares so string comparisons do not screw up the comparison | |
- added admin check when using AllUsers | |
- added output object that can be queried / reported on with changes | |
- added switch to allow for pre-release versions | |
- added a progressbar | |
- added a check for admin, and relaunch into admin if not in admin | |
#> | |
function Test-Admin { | |
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent()) | |
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) | |
} | |
# launch this script in admin mode, if not already else it cannot update the all users modules | |
if (-not (Test-Admin)) { | |
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -file "{0}"' -f ($myinvocation.MyCommand.Definition)) | |
exit | |
} | |
function VersionParse { | |
param([string]$value) | |
# CYA: The version object CAN NOT parse versions with -prerelease strings, parse them into our own object | |
$version = $value -split "-" | |
$ret = "" | Select-Object Version, PreRelease | |
$ret.Version = [Version]::Parse(($version | Select-Object -First 1)) | |
$ret.PreRelease = ($version | Select-Object -Skip 1) -join "-" | |
return $ret | |
} | |
function Update-EveryModule { | |
<# | |
.SYNOPSIS | |
Updates all modules from the PowerShell gallery. | |
.DESCRIPTION | |
Updates all local modules that originated from the PowerShell gallery. | |
Removes all old versions of the modules. | |
.PARAMETER ExcludedModules | |
Array of modules to exclude from updating. | |
.PARAMETER SkipMajorVersion | |
Skip major version updates to account for breaking changes. | |
.PARAMETER KeepOldModuleVersions | |
Array of modules to keep the old versions of. | |
.PARAMETER ExcludedModulesforRemoval | |
Array of modules to exclude from removing old versions of. | |
The Az module is excluded by default. | |
.PARAMETER Scope | |
The scope to use when updating the module. | |
.PARAMETER AllowPrerelease | |
If enabled, then pre-release modules are allowed. | |
.EXAMPLE | |
Update-EveryModule -excludedModulesforRemoval 'Az' | |
.NOTES | |
Created by Barbara Forbes | |
@ba4bes | |
.LINK | |
https://4bes.nl | |
#> | |
[cmdletbinding(SupportsShouldProcess = $true)] | |
param ( | |
[parameter()] | |
[array]$ExcludedModules = @(), | |
[parameter()] | |
[switch]$SkipMajorVersion, | |
[parameter()] | |
[switch]$KeepOldModuleVersions, | |
[parameter()] | |
[array]$ExcludedModulesforRemoval = @("Az"), | |
[parameter()] | |
[validateset("CurrentUser", "AllUsers")] | |
[string]$Scope = "CurrentUser", | |
[parameter()] | |
[switch]$AllowPrerelease | |
) | |
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) | |
if ($Scope -ieq "AllUsers" -and -not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { | |
throw "When the scope is set to 'AllUsers' then this script must be run with administrative rights." | |
return | |
} | |
$ret = New-Object System.Collections.ArrayList | |
# Get all installed modules that have a newer version available | |
Write-Verbose "Checking all installed modules for available updates." | |
$CurrentModules = Get-InstalledModule | Where-Object { $ExcludedModules -inotcontains $_.Name -and $_.repository -ieq "PSGallery" } | |
$counter = 0 | |
$activity = "Scanning modules" | |
$total = $CurrentModules.Count | |
# Walk through the Installed modules and check if there is a newer version | |
$CurrentModules | ForEach-Object { | |
Write-Verbose "Checking $($_.Name)" | |
$skipped = $false | |
$counter++ | |
try { | |
$tmp = "" | Select-Object Name, Version, NewVersion, RemovedVersions, Error | |
$tmp.Name = $_.Name | |
$ret.Add($tmp) | Out-Null | |
Write-Progress -Activity $activity ` | |
-Status “Scanning module $($_.Name)” ` | |
-PercentComplete (([decimal][Math]::Min($total, $counter) / [decimal]$total) * 100.00) | |
$GalleryModule = Find-Module -Name $_.Name -Repository PSGallery -ErrorAction Stop -AllowPrerelease:$AllowPrerelease.IsPresent | |
} | |
Catch { | |
Write-Error "Module $($_.Name) not found in gallery $_" | |
$GalleryModule = $null | |
} | |
if ($GalleryModule) { | |
$galleryVersion = VersionParse -value $GalleryModule.Version | |
$moduleVersion = VersionParse -value $_.Version | |
$tmp.Version = $_.Version | |
if ($galleryVersion.Version -gt $moduleVersion.Version -or $galleryVersion.PreRelease -ine $moduleVersion.PreRelease) { | |
if ($SkipMajorVersion.IsPresent -and $galleryVersion.Version.Major -gt $moduleVersion.Version.Major) { | |
Write-Warning "Skipping major version update for module $($_.Name). Galleryversion: $($GalleryModule.Version), local version $($_.Version)" | |
$skipped = $true | |
} | |
else { | |
Write-Verbose "$($_.Name) will be updated. Galleryversion: $($GalleryModule.Version), local version $($_.Version)" | |
try { | |
if ($PSCmdlet.ShouldProcess( | |
("Module {0} will be updated to version {1}" -f $_.Name, $GalleryModule.Version), | |
$_.Name, | |
"Update-Module" | |
) | |
) { | |
Write-Progress -Activity $activity ` | |
-Status “Updating module $($_.Name) to version $($GalleryModule.Version)” ` | |
-PercentComplete (([decimal][Math]::Min($total, $counter) / [decimal]$total) * 100.00) | |
$tmp.NewVersion = $GalleryModule.Version | |
Update-Module $_.Name -ErrorAction Stop -Force -Scope $Scope -AcceptLicense | |
Write-Verbose "$($_.Name) has been updated" | |
} | |
} | |
Catch { | |
Write-Error "$($_.Name) failed: $_ " | |
$tmp.Error = "Update failed: $($_.Exception.Message)" | |
continue | |
} | |
} | |
} | |
else { | |
Write-Verbose "Module $($_.Name) up to date $($_.Version)" | |
} | |
# clean up any old modules if they are present, unless we are told to keep them and they were not skipped | |
if (-not $KeepOldModuleVersions.IsPresent -and -not $skipped) { | |
if ($ExcludedModulesforRemoval -icontains $_.Name) { | |
Write-Verbose "$($allversions.count) versions of this module found [ $($module.name) ]" | |
Write-Verbose "Please check this manually as removing the module can cause instabillity." | |
} | |
else { | |
try { | |
if ($PSCmdlet.ShouldProcess( | |
("Scan for old versions of module {0} to remove" -f $_.Name), | |
$_.Name, | |
"Uninstall-Module" | |
) | |
) { | |
$oldVersions = Get-InstalledModule -Name $_.Name -AllVersions | | |
Where-Object { $_.version -ine $GalleryModule.Version } | |
if ($oldVersions) { | |
Write-Verbose "Removing old versions of module $($_.Name)" | |
Write-Progress -Activity $activity ` | |
-Status “Removing old versions of module $($_.Name)” ` | |
-PercentComplete (([decimal][Math]::Min($total, $counter) / [decimal]$total) * 100.00) | |
$oldVersions | | |
Uninstall-Module -Force -ErrorAction Stop | |
$tmp.RemovedVersions = $oldVersions.Version | |
Write-Verbose "Old versions of $($_.Name) have been removed" | |
} | |
} | |
} | |
catch { | |
Write-Error "Uninstalling old versions of module $($_.Name) failed: $_" | |
$tmp.Error = "Uninstall failed: $($_.Exception.Message)" | |
} | |
} | |
} | |
} | |
else { | |
Write-Verbose "Module $($_.Name) not found in the gallery" | |
} | |
} | |
Write-Progress -Activity $activity -Completed | |
return $ret | |
} | |
Clear-Host | |
$modules = Update-EveryModule -Verbose -Scope AllUsers | |
$modules | Format-Table |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment