One of the attractive features in PowerShell v5 is PowerShellGet. This module includes commands which makes it easy to discover and install PowerShell modules from the Internet, or even your network. The modules are stored in online repositories. Microsoft maintains one called PSGallery. Typically you will use PowerShell commands to find and install modules. As a quick side note, while Microsoft appears to do some degree of review using the PowerShell Script Analyzer, there is no guarantee that modules you find online will work in your environment. That's why the repository is untrusted by default. You can still download and install but you are accepting the potential risks. But that's not the point here. It is pretty easy to download new modules, which includes DSC resources. However, new versions can be published to the online repository. As far as I know there is no notification mechanism. So you might have to periodically check to see if there are new versions available. Which means I wrote a tool.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
You can easily check a module by comparing your local version against the repository.
Here you can see that I have installed the PoshWSUS module and it is still at version 2.2.1. But the online version is now 2.3.1.2. By the way, Find-Module will default to the Microsoft gallery so you don't have to specify the repository. I can use Update-Module to install the new version. But my goal is to be able to easily identify modules that can be updated. So I wrote a PowerShell function called Compare-Module.
#requires -version 5.0 | |
# https://gist.github.com/jdhitsolutions/7217ed9293f18e8d454e3f88ecb38b67 | |
Function Compare-Module { | |
<# | |
.Synopsis | |
Compare module versions. | |
.Description | |
Use this command to compare module versions between what is installed against an online repository like the PSGallery. Results will be automatically sorted by module name. | |
.Parameter Name | |
The name of a module to check. Wildcards are permitted. | |
.Notes | |
Version: 1.2 | |
Learn more about PowerShell: | |
http://jdhitsolutions.com/blog/essential-powershell-resources/ | |
.Example | |
PS C:\> Compare-Module | Where-objject {$_.UpdateNeeded} | |
Name : Azure | |
OnlineVersion : 1.5.1 | |
InstalledVersion : 1.0.4 | |
PublishedDate : 6/27/2016 6:50:11 PM | |
UpdateNeeded : True | |
Name : Azure.Storage | |
OnlineVersion : 1.1.4 | |
InstalledVersion : 1.0.4 | |
PublishedDate : 6/27/2016 6:48:07 PM | |
UpdateNeeded : True | |
Name : AzureRM | |
OnlineVersion : 1.5.1 | |
InstalledVersion : 1.2.0 | |
PublishedDate : 6/27/2016 7:08:50 PM | |
UpdateNeeded : True | |
... | |
.Example | |
PS C:\> Compare-Module | Where UpdateNeeded | Out-Gridview -title "Select modules to update" -outputMode multiple | Foreach { Update-Module $_.name } | |
Compare modules and send results to Out-Gridview. Use Out-Gridview as an object picker to decide what modules to update. | |
.Example | |
PS C:\> compare-module -name xWindows* | format-table | |
Name OnlineVersion InstalledVersion PublishedDate UpdateNeeded | |
---- ------------- ---------------- ------------- ------------ | |
xWindowsEventForwarding 1.0.0.0 1.0.0.0 6/17/2015 9:46:32 PM False | |
xWindowsRestore 1.0.0 1.0.0 12/18/2014 4:22:42 AM False | |
xWindowsUpdate 2.5.0.0 2.3.0.0 5/18/2016 11:02:47 PM True | |
Compare all modules that start with xWindows and display results in a table format. | |
.Example | |
PS C:\> get-dscresource cAD* | Select moduleName -Unique | compare-module | |
Name : cActiveDirectory | |
OnlineVersion : 1.1.1 | |
InstalledVersion : 1.0.1 | |
PublishedDate : 6/23/2015 9:24:55 PM | |
UpdateNeeded : True | |
Get all DSC Resources that start with cAD and select the corresponding module name. Since the module name will be listed for every resource, get a unique list and pipe that to Compare-Module. | |
.Link | |
Find-Module | |
.Link | |
Get-Module | |
.Link | |
Update-Module | |
.Inputs | |
[string] | |
.Outputs | |
[PSCustomObject] | |
#> | |
[cmdletbinding()] | |
[alias("cmo")] | |
Param | |
( | |
[Parameter( | |
Position = 0, | |
ValueFromPipelineByPropertyName | |
)] | |
[ValidateNotNullorEmpty()] | |
[Alias("modulename")] | |
[string]$Name, | |
[ValidateNotNullorEmpty()] | |
[string]$Gallery = "PSGallery" | |
) | |
Begin { | |
Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)" | |
$progParam = @{ | |
Activity = $MyInvocation.MyCommand | |
Status = "Getting installed modules" | |
CurrentOperation = "Get-Module -ListAvailable" | |
PercentComplete = 25 | |
} | |
Write-Progress @progParam | |
} #begin | |
Process { | |
$gmoParams = @{ | |
ListAvailable = $True | |
} | |
if ($Name) { | |
$gmoParams.Add("Name", $Name) | |
} | |
$installed = Get-Module @gmoParams | |
if ($installed) { | |
$progParam.Status = "Getting online modules" | |
$progParam.CurrentOperation = "Find-Module -repository $Gallery" | |
$progParam.PercentComplete = 50 | |
Write-Progress @progParam | |
$fmoParams = @{ | |
Repository = $Gallery | |
ErrorAction = "Stop" | |
} | |
if ($Name) { | |
$fmoParams.Add("Name", $Name) | |
} | |
Try { | |
$online = Find-Module @fmoParams | |
} | |
Catch { | |
Write-Warning "Failed to find online module(s). $($_.Exception.message)" | |
} | |
$progParam.status = "Comparing $($installed.count) installed modules to $($online.count) online modules." | |
$progParam.percentComplete = 80 | |
Write-Progress @progParam | |
$data = $online | Where-Object {$installed.name -contains $_.name} | | |
Select-Object -property Name, | |
@{Name = "OnlineVersion"; Expression = {$_.Version}}, | |
@{Name = "InstalledVersion"; Expression = { | |
#save the name from the incoming online object | |
$name = $_.Name | |
$installed.Where( {$_.name -eq $name}).Version -join ","} | |
}, | |
PublishedDate, | |
@{Name = "UpdateNeeded"; Expression = { | |
$name = $_.Name | |
#there could me multiple versions installed | |
$installedVersions = $installed.Where( {$_.name -eq $name}).Version | Sort-Object | |
foreach ($item in $installedVersions) { | |
If ([version]$_.Version -gt [version]$item) { | |
$result = $True | |
} | |
else { | |
$result = $False | |
} | |
} | |
$result | |
} | |
} | Sort-Object -Property Name | |
$progParam.PercentComplete = 100 | |
$progParam.Completed = $True | |
Write-Progress @progparam | |
#write the results to the pipeline | |
$data | |
} | |
else { | |
Write-Warning "No local module or modules found" | |
} | |
} #Progress | |
End { | |
Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)" | |
} #end | |
} #close function | |
The function gets all of your currently installed modules, gets a list of all modules in the gallery and then compares the version information. The function creates a custom object representing every installed module.
You can specify a module name with wildcards:
Or I can search all modules.
I could even use this function as a means of selectively updating:
Compare-Module | Out-Gridview -title "Select modules to update" -outputMode multiple | Foreach { Update-Module $_.name -force }
Or you might want to use Save-Module instead. The choice is yours.
I hope you'll give this spin and let me know what you think. As always, please post any problems or requests in the comments section on the Gist page.
Enjoy!
Brilliant, thanks! I wrote something like this for myself earlier this year but yours is far superior. Appreciated.
Jeffery:
Maybe your tips can be helpful. Somehow, somewhere I must have come across “The Lonely Administrator” and associated you with PowerShell. I have recently been exploring PS to find quality, pre-written PS scripts to incorporated into a managed services platform called ATERA.
You know the extent to which the evolution of remote monitoring and access have encroached into the today’s personal computing environment. This morning MVP story strikes at another time mark with respect to MSP functionality and PS incorporated functionalities.
Personally, I don’t see myself becoming a PS ‘guru’. I am already using about 36 or the 48 hours available in a day’s time and am hard pressed to add more expertise. For me, the answer would be a reliable, reputable resource for PS Scripts that can easily be plugged into my ATERA MSP platform to accomplish needed and desired tasks. Would you have any suggestions as to how to go about meeting such a challenge?
The direction Microsoft is taking is supporting NuGet-style repositories such as the PowerShell Gallery. I think the best direction would be to package your scripts and tools as PowerShell modules which you can publish to your own private repository. Clients would need to register and trust the new repository. Then they could use commands like Find-Module and Install-Module. Or you could give them wrapper scripts to query only your repository.
I have not setup my own repository so I can’t give you any guidance on that. There is a project on GitHub (https://github.com/PowerShell/PSPrivateGallery) on setting up private repositories in your own domain.
Good luck.