You installed a PowerShell module on your machine and used it for a long time and suddenly… It doesn’t connect anymore, shows warnings that some commands are deprecated, and now what?! In this blog post, I will show you a way to update all your modules to the latest version and also remove unused older versions.
Requirements
The script should be able to update all modules to the latest version, but it should also update them to the latest prerelease version if specified. It should also show the current version and the new version when updated. And to keep things nice and tidy, it should also remove any previous versions.
Running the script
When running the script with no parameter, it will update all modules to the latest production version and remove older versions if any. With the -AllowPreRelease parameter, it will update all modules to the latest Prerelease version if available and to the latest Production version if not.
Note: The script requires running it as Administrator.
Warning: Updating all your modules to the PreRelease version could break things for you!
The output of the script when starting the Update-Modules function:

The output of the script when removing older versions:

Example output stating the updated modules at the end of the script:

It will show an error in red if the module can’t be updated, it will also report if modules can’t be updated because they are in use:

or

Note: If you have a lot of modules… This could take a while 🙂
The script
Below is the script, save it to a location (c:\scripts for example) and add it to your PowerShell profile by:
notepad $profile add ". c:\scripts\Update-modules.ps1" quit/save and start new PowerShell session
Special thanks go out to ScrambledBrain for updating the script through a pull request on GitHub twice 🙂
function Update-Modules { param ( [switch]$AllowPrerelease ) # Test admin privileges without using -Requires RunAsAdministrator, # which causes a nasty error message, if trying to load the function within a PS profile but without admin privileges if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) { Write-Warning ("Function {0} needs admin privileges. Break now." -f $MyInvocation.MyCommand) return } # Get all installed modules Write-Host ("Retrieving all installed modules ...") -ForegroundColor Green $CurrentModules = Get-InstalledModule | Select-Object Name, Version | Sort-Object Name if (-not $CurrentModules) { Write-Host ("No modules found.") -ForegroundColor Gray return } else { $ModulesCount = $CurrentModules.Count $DigitsLength = $ModulesCount.ToString().Length Write-Host ("{0} modules found." -f $ModulesCount) -ForegroundColor Gray } # Show status of AllowPrerelease Switch '' if ($AllowPrerelease) { Write-Host ("Updating installed modules to the latest PreRelease version ...") -ForegroundColor Green } else { Write-Host ("Updating installed modules to the latest Production version ...") -ForegroundColor Green } # Loop through the installed modules and update them if a newer version is available $i = 0 foreach ($Module in $CurrentModules) { $i++ $Counter = ("[{0,$DigitsLength}/{1,$DigitsLength}]" -f $i, $ModulesCount) $CounterLength = $Counter.Length Write-Host ('{0} Checking for updated version of module {1} ...' -f $Counter, $Module.Name) -ForegroundColor Green try { Update-Module -Name $Module.Name -AllowPrerelease:$AllowPrerelease -AcceptLicense -Scope:AllUsers -ErrorAction Stop } catch { Write-Host ("{0$CounterLength} Error updating module {1}!" -f ' ', $Module.Name) -ForegroundColor Red } # Retrieve newest version number and remove old(er) version(s) if any $AllVersions = Get-InstalledModule -Name $Module.Name -AllVersions | Sort-Object PublishedDate -Descending $MostRecentVersion = $AllVersions[0].Version if ($AllVersions.Count -gt 1 ) { Foreach ($Version in $AllVersions) { if ($Version.Version -ne $MostRecentVersion) { try { Write-Host ("{0,$CounterLength} Uninstalling previous version {1} of module {2} ..." -f ' ', $Version.Version, $Module.Name) -ForegroundColor Gray Uninstall-Module -Name $Module.Name -RequiredVersion $Version.Version -Force:$True -ErrorAction Stop } catch { Write-Warning ("{0,$CounterLength} Error uninstalling previous version {1} of module {2}!" -f ' ', $Version.Version, $Module.Name) } } } } } # Get the new module versions for comparing them to to previous one if updated $NewModules = Get-InstalledModule | Select-Object Name, Version | Sort-Object Name if ($NewModules) { '' Write-Host ("List of updated modules:") -ForegroundColor Green $NoUpdatesFound = $true foreach ($Module in $NewModules) { $CurrentVersion = $CurrentModules | Where-Object Name -EQ $Module.Name if ($CurrentVersion.Version -notlike $Module.Version) { $NoUpdatesFound = $false Write-Host ("- Updated module {0} from version {1} to {2}" -f $Module.Name, $CurrentVersion.Version, $Module.Version) -ForegroundColor Green } } if ($NoUpdatesFound) { Write-Host ("No modules were updated.") -ForegroundColor Gray } } }
Download the script(s) from GitHub here.
Your methodology is different than others that I have seen where it uses Get-InstalledModule. It is more elegant than others I have seen. However, it will not update modules that are native to PowerShell that have not previously been updated. For example, PSDesiredStateConfiguration.
The PSDesiredStateConfiguration brings up other issues between PowerShell 5x and 7x. In PowerShell 5, the Find-Module lists a version not compatible with this version of PowerShell. Attempting to install this version does not produce any errors and has an end result in no change.
It also doesn’t update modules when they are not installed using install-module. It’s not perfect, I could expand it to try and catch these exceptions too in the future