Skip to content

Instantly share code, notes, and snippets.

@tcartwright
Forked from Ba4bes/Update-EveryModule.ps1
Last active March 15, 2023 14:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tcartwright/615053acde5ea64d1664ef6f9faa6524 to your computer and use it in GitHub Desktop.
Save tcartwright/615053acde5ea64d1664ef6f9faa6524 to your computer and use it in GitHub Desktop.
# 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