Powershell script to remove duplicate, old modules

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

11 thoughts on “Powershell script to remove duplicate, old modules

    1. 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!

  1. 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!

    1. 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

  2. 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
    }

  3. 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

    1. 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

Leave a Reply