PowerShell function to find information about module updates

I decided to update the one liner from my blog article last week and take it a step further by turning it into a reusable tool that displays information about module updates that are available regardless of where they were installed from.

  1#Requires -Version 3.0 -Modules PowerShellGet
  2function Find-MrModuleUpdate {
  3
  4<#
  5.SYNOPSIS
  6    Finds updates for installed modules from an online gallery that matches the specified criteria.
  7
  8.DESCRIPTION
  9    Find-MrModuleUpdate is a PowerShell advanced function that finds updates from an online gallery for locally installed modules
 10    regardless of whether or not they were originally installed from an online gallery or from the same online gallery where the
 11    update is found.
 12
 13.PARAMETER Name
 14    Specifies the names of one or more modules to search for.
 15
 16.PARAMETER Scope
 17    Specifies the search scope of the installed modules. The acceptable values for this parameter are: AllUsers and CurrentUser.
 18
 19.EXAMPLE
 20     Find-MrModuleUpdate
 21
 22.EXAMPLE
 23     Find-MrModuleUpdate -Name PSScriptAnalyzer, PSVersion
 24
 25.EXAMPLE
 26     Find-MrModuleUpdate -Scope CurrentUser
 27
 28.EXAMPLE
 29     Find-MrModuleUpdate -Name PSScriptAnalyzer, PSVersion -Scope CurrentUser
 30
 31.INPUTS
 32    None
 33
 34.OUTPUTS
 35    Mr.ModuleUpdate
 36
 37.NOTES
 38    Author:  Mike F Robbins
 39    Website: http://mikefrobbins.com
 40    Twitter: @mikefrobbins
 41#>
 42
 43    [CmdletBinding()]
 44    [OutputType('Mr.ModuleUpdate')]
 45    param (
 46        [ValidateNotNullOrEmpty()]
 47        [string[]]$Name,
 48
 49        [ValidateSet('AllUsers', 'CurrentUser')]
 50        [string]$Scope
 51    )
 52
 53    $AllUsersPath = "$env:ProgramFiles\WindowsPowerShell\Modules\*"
 54    $CurrentUserPath = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\*"
 55
 56    switch ($Scope) {
 57        'AllUsers' {$Path = $AllUsersPath; break}
 58        'CurrentUser' {$Path = $CurrentUserPath; break}
 59        Default {$Path = $AllUsersPath, $CurrentUserPath}
 60    }
 61
 62    $Params = @{
 63        ListAvailable = $true
 64    }
 65
 66    if ($PSBoundParameters.Name) {
 67        $Params.Name = $Name
 68    }
 69
 70    $Modules = Get-Module @Params
 71
 72    foreach ($p in $Path) {
 73
 74        $ScopedModules = $Modules |
 75        Where-Object ModuleBase -like $p |
 76        Sort-Object -Property Name, Version -Descending |
 77        Get-Unique
 78
 79        foreach ($Module in $ScopedModules) {
 80
 81            Remove-Variable -Name InstallInfo -ErrorAction SilentlyContinue
 82            $Repo = Find-Module -Name $Module.Name -ErrorAction SilentlyContinue
 83
 84            if ($Repo) {
 85                $Diff = Compare-Object -ReferenceObject $Module -DifferenceObject $Repo -Property Name, Version |
 86                        Where-Object SideIndicator -eq '=>'
 87
 88                if ($Diff) {
 89                    $PSGetModuleInfoPath = "$($Module.ModuleBase)\PSGetModuleInfo.xml"
 90
 91                    if (Test-Path -Path $PSGetModuleInfoPath) {
 92                        $InstallInfo = Import-Clixml -Path $PSGetModuleInfoPath
 93                    }
 94
 95                    switch ($Module.ModuleBase) {
 96                        {$_ -like $AllUsersPath} {$Location = 'AllUsers'; break}
 97                        {$_ -like $CurrentUserPath} {$Location = 'CurrentUser'; break}
 98                        Default {Throw 'An unexpected error has occured.'}
 99                    }
100
101                    [pscustomobject]@{
102                        Name = $Module.Name
103                        InstalledVersion = $Module.Version
104                        InstalledLocation = $Location
105                        Repository = $Repo.Repository
106                        RepositoryVersion = $Diff.Version
107                        InstalledFromRepository = $InstallInfo.Repository
108                        PSTypeName = 'Mr.ModuleUpdate'
109                    }
110
111                }
112
113            }
114
115        }
116
117    }
118
119}
1Find-MrModuleUpdate

find-mrmoduleupdate1a.jpg

As shown in the previous results, the function lists modules that have updates available regardless of where they were originally installed from. It also lists the installed version and location along with the repository where the updated version resides and the updated version number. It also lists where the module was originally installed from by reading the contents of the hidden XML file in the module directory if it was installed using PowerShellGet.

You can narrow your search down to specific modules:

1Find-MrModuleUpdate -Name PSScriptAnalyzer, PSVersion

find-mrmoduleupdate2a.jpg

To a specific scope:

1Find-MrModuleUpdate -Scope CurrentUser

find-mrmoduleupdate3a.jpg

Use a combination of specific modules and scope to narrow the search down even further:

1Find-MrModuleUpdate -Name PSScriptAnalyzer, PSVersion -Scope AllUsers

find-mrmoduleupdate4a.jpg

The function also uses custom formatting to allow for table output with more than four columns by default without having to pipe to Format-Table. The relevant portion of the PS1XML file used for the custom formatting is shown in the code example below:

 1<View>
 2    <Name>Mr.ModuleUpdate</Name>
 3    <ViewSelectedBy>
 4        <TypeName>Mr.ModuleUpdate</TypeName>
 5    </ViewSelectedBy>
 6    <TableControl>
 7        <TableHeaders>
 8                <TableColumnHeader>
 9                <Width>30</Width>
10            </TableColumnHeader>
11            <TableColumnHeader>
12                <Width>16</Width>
13            </TableColumnHeader>
14            <TableColumnHeader>
15                <Width>17</Width>
16            </TableColumnHeader>
17            <TableColumnHeader>
18                <Width>10</Width>
19            </TableColumnHeader>
20            <TableColumnHeader>
21                <Width>17</Width>
22            </TableColumnHeader>
23            <TableColumnHeader>
24                <Width>23</Width>
25            </TableColumnHeader>
26        </TableHeaders>
27        <TableRowEntries>
28            <TableRowEntry>
29                <TableColumnItems>
30                    <TableColumnItem>
31                        <PropertyName>Name</PropertyName>
32                    </TableColumnItem>
33                    <TableColumnItem>
34                        <PropertyName>InstalledVersion</PropertyName>
35                    </TableColumnItem>
36                    <TableColumnItem>
37                        <PropertyName>InstalledLocation</PropertyName>
38                    </TableColumnItem>
39                    <TableColumnItem>
40                        <PropertyName>Repository</PropertyName>
41                    </TableColumnItem>
42                    <TableColumnItem>
43                        <PropertyName>RepositoryVersion</PropertyName>
44                    </TableColumnItem>
45                    <TableColumnItem>
46                        <PropertyName>InstalledFromRepository</PropertyName>
47                    </TableColumnItem>
48                </TableColumnItems>
49            </TableRowEntry>
50            </TableRowEntries>
51    </TableControl>
52</View>

The Find-MrModuleUpdate function shown in this blog article is part of my MrToolkit module which can be downloaded from my PowerShell repository on GitHub.

µ