If you’ve installed modules in powershell such as the AzureRM collection, and have updated to newer versions of said modules, it’s possible that you might have both the old and new versions installed. This can slow down some commands in powershell (get-command for example)
I wrote a small script to remove old versions of Powershell modules using what I found at https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/14934876-update-module-needs-flag-to-remove-previous-versio) as a starter.
Note that when I say “Remove old versions” I mean it leaves the most current one you have and removes the older ones – if your system is 3 versions behind, it will not go fetch new commands first (though it does remind you of the command to do that)
write-host "this will remove all old versions of installed modules" write-host "be sure to run this as an admin" -foregroundcolor yellow write-host "(You can update all your Azure RM modules with update-module Azurerm -force)" $mods = get-installedmodule foreach ($Mod in $mods) { write-host "Checking $($mod.name)" $latest = get-installedmodule $mod.name $specificmods = get-installedmodule $mod.name -allversions write-host "$($specificmods.count) versions of this module found [ $($mod.name) ]" foreach ($sm in $specificmods) { if ($sm.version -ne $latest.version) { write-host "uninstalling $($sm.name) - $($sm.version) [latest is $($latest.version)]" $sm | uninstall-module -force write-host "done uninstalling $($sm.name) - $($sm.version)" write-host " --------" } } write-host "------------------------" } write-host "done"
Hope this helps!
Update 3-2018:
The script above was originally missing a -force parameter, this has been fixed.
While troubleshooting the script above, I wanted a fast, non-destructive way to see what modules I had installed that had multiple versions.
I created the script below for that purpose, you may find it useful:
write-host "this will report all modules with duplicate (older and newer) versions installed" write-host "be sure to run this as an admin" -foregroundcolor yellow write-host "(You can update all your Azure RMmodules with update-module Azurerm -force)" $mods = get-installedmodule foreach ($Mod in $mods) { write-host "Checking $($mod.name)" $latest = get-installedmodule $mod.name $specificmods = get-installedmodule $mod.name -allversions write-host "$($specificmods.count) versions of this module found [ $($mod.name) ]" foreach ($sm in $specificmods) { if ($sm.version -eq $latest.version) { $color = "green"} else { $color = "magenta"} write-host " $($sm.name) - $($sm.version) [highest installed is $($latest.version)]" -foregroundcolor $color } write-host "------------------------" } write-host "done"
- Jack
love it thanks
This doesn’t take care of the module dependencies…
Ayan, you’re right, the original version of the script was missing the -force parameter after uninstall-module.
I’ve updated the script, thank you for pointing this out!
Your code inspired me to write something for our needs.. hopefully it’s helpfull.
function UnInstall-Modules {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]
$RetentionMonths = 3
)
if ($PSCmdlet.MyInvocation.BoundParameters[“Debug”].IsPresent) {
$DebugPreference = “Continue”
}
$CMDLetName = $MyInvocation.MyCommand.Name
# Get a list of the current modules installed.
Write-Debug “Getting list of current modules installed …”
$Modules = Get-InstalledModule
$Counter = 0 # Used to track count of un-installations
foreach ($Module in $Modules) {
Write-Debug $($Module.Name) # List out all the modules installed.
}
foreach ($Module in $Modules) {
Write-Host “`n”
$ModuleVersions = Get-InstalledModule -Name $($Module.Name) -AllVersions # Get all versions of the module
$ModuleVersionsArray = New-Object System.Collections.ArrayList
foreach ($ModuleVersion in $ModuleVersions) {
$ModuleVersionsArray.Add($ModuleVersion.Version) > $Null
}
Write-Debug “Reviewing module: $($Module.name) – Versions installed: $($ModuleVersionsArray.Count)”
$VersionsToKeepArray = New-Object System.Collections.ArrayList
$MajorVersions = @($ModuleVersionsArray.Major | Get-Unique) # Get unique majors
$MinorVersions = @($ModuleVersionsArray.Minor | Get-Unique) # Get unique minors
foreach ($MajorVersion in $MajorVersions) {
foreach ($MinorVersion in $MinorVersions) {
$ReturnedVersion = (Get-InstalledModule -Name $($Module.Name) -MaximumVersion “${MajorVersion}.${MinorVersion}.99999” -ErrorAction SilentlyContinue)
$VersionsToKeepArray.add($ReturnedVersion) > $Null # Versions to keep
$ModuleVersionsArray.Remove($ReturnedVersion.Version) # Remove versions we’re keeping.
}
}
# Groom the builds
if ($ModuleVersionsArray) {
foreach ($Version in $ModuleVersionsArray) {
Write-Debug “Removing Module: $($Module.Name) – Version: ${Version} ”
try {
Uninstall-Module -Name $($Module.Name) -RequiredVersion “${Version}” -ErrorAction Stop
$Counter++
}
catch {
Write-Warning “Problem”
}
}
}
else {
Write-Debug “No builds to remove”
}
# Evaluate removing previous builds older than retention period.
$VersionsToRemoveArray = New-Object System.Collections.ArrayList # Create an array a versions to remove
$Oldest = ($VersionsToKeepArray.version | Measure-Object -Minimum).Minimum # Get oldest version
$Newest = ($VersionsToKeepArray.version | Measure-Object -Maximum).Maximum # Get newest version
$ReturnedVersion = (Get-InstalledModule -Name $($Module.Name) -RequiredVersion $Oldest) # Find the oldest of the keepers
if ($Oldest -ne $Newest) {
# Skip adding it the current is both newest and oldest.
$VersionsToRemoveArray.add($ReturnedVersion) > $Null # Versions to remove
}
if ($VersionsToRemoveArray) {
foreach ($Module in $VersionsToRemoveArray) {
if ($Module.version -eq $Oldest -and $Module.InstalledDate -lt (get-date).AddMonths( – ${RetentionMonths})) {
try {
Uninstall-Module -Name $($Module.Name) -RequiredVersion “${Version}” -ErrorAction Stop
$Counter++
}
catch {
Write-Warning “Problem”
}
}
else {
Write-Debug “Module: $($Module.Name) – Version: $($Module.version) is not yet older than retention of ${RetentionMonths} months, skipping removal. ”
}
}
}
} # For each module end
if ($Counter -gt 0) {
Write-Debug “Removed ${Counter} module versions”
}
} # Function end
You can add it to a module as a cmdlet or run it on it’s own.
Uninstall-Modules -Debug
Cheers!
Thanks for taking the time to post this!
Other readers note: I’ve not tested the above code but it looks helpful so I’m approving the reply ‘as is’ in case it helps others!
– Jack
Very handy!
This is wonderful. Thanks for sharing.
Worked great!
Thanks a lot Jack!
Based on your solution a made you a little something
#Will test all installed modules against the PSGallery and see if a never version is available
$mods = Get-InstalledModule
foreach ($mod in $mods) {
$latestAvailable = Find-Module $mod.Name
if ($mod.version -eq $latestAvailable.version) {
$color = “green”
$text = “Latest available version match current installed”
}
else {
$color = “magenta”
$text = “Latest available version is higher than the current installed”
}
Write-Host “$($mod.Name) – $($mod.version) [$text $($latestAvailable.version)]” -foregroundcolor $color
}
Looks fantastic! LOVE THIS SCRIPT!
Hi,
thanks – most useful.
why not starting the module with two updates at the beginning:
Just a general update-module and update-help first ?
Then you would automatically get the newest versions first ?
Just as an idea …
mfg Klaus
Great idea Klaus! In this case, my assumption is that the reader has installed a specific version and may not want a newer one, but may want to clear out old ones to ensure they are only using the intended version.
your ideas would be easily incorporated at the top of the script and would likely help many readers – thanks for posting this!
– Jack