Getting Installed Packages InSync Across Multiple Machines with the PowerShell version 5 Preview OneGet Module
I presented a session for the Mississippi PowerShell User Group a couple of days ago and I thought I would share a couple of things I showed during that presentation that I hadn't previously blogged about.
I'm starting where I left off during my last blog article about the OneGet module in the PowerShell version 5 preview.
I've stored the names of the packages that are installed on the local computer (PC03) in a variable named Software and created PSSessions to three remote computers (PC04, PC05, and PC06). Those PSSessions have been stored in a variable named Sessions as shown in the following example:
1($software = Get-Package | Select-Object -ExpandProperty Name)
2($Session = New-PSSession -ComputerName pc04, pc05, pc06)
In the previous blog article, the same packages that are installed on PC03 were installed on PC04, PC05, and PC06 using PowerShell remoting and cmdlets from the OneGet module.
Scenario: There has been a recent problem where other admins have made unauthorized changes, adding and removing various packages on PC04, PC05, and PC06.
The Get-Package
cmdlet which is part of the new OneGet module can be used to determine what
packages are missing and have been added to those machines based off of what's currently installed
on the template machine (PC03), all without leaving your desk:
1Invoke-Command -Session $Session {
2 $InstalledPackages = Get-Package | Select-Object -ExpandProperty Name
3 foreach ($package in $Using:software) {
4 if ($installedPackages -notcontains $package) {
5 Write-Output "$Env:COMPUTERNAME is missing the $package package"
6 }
7 }
8 foreach ($package in $InstalledPackages) {
9 if ($Using:software -notcontains $package) {
10 Write-Output "Unauthorized package named $package detected on $Env:COMPUTERNAME"
11 }
12 }
13}
There is indeed a problem which needs to be corrected to get those machines back in sync with what packages should be installed on them. A slight modification to the previous script has been made to not only report the inconsistencies, but to also correct them on the fly:
1Invoke-Command -Session $Session {
2 $InstalledPackages = Get-Package | Select-Object -ExpandProperty Name
3 foreach ($package in $Using:software) {
4 if ($installedPackages -notcontains $package) {
5 Write-Output "$Env:COMPUTERNAME is missing the $package package"
6 Install-Package -Name $package -Force
7 }
8 }
9 foreach ($package in $InstalledPackages) {
10 if ($Using:software -notcontains $package) {
11 Write-Output "Unauthorized package named $package detected on $Env:COMPUTERNAME"
12 Uninstall-Package -Name $package -Force
13 }
14 }
15}
PC04, PC05, and PC06 are now back to having the same packages installed on them as PC03:
1Invoke-Command -Session $Session {Get-Package} | Format-Table -GroupBy PSComputerName
I've gone back and made some more changes to PC04, PC05, and PC06 to see if I could solve this problem without iterating through both sides with a foreach loop which seems inefficient. This time, multiple issues exist on some of the machines:
Notice in the previous example that using the Format-Table
cmdlet with the GroupBy
parameter
didn't properly group them by PSComputerName because we have two groups for PC05, although it
appeared to work two examples ago. This is actually a bonus tip. To determine why this occurred,
take a look at the help for the GroupBy
parameter:
1help Format-Table -Parameter GroupBy
Based on the information in the help for the GroupBy
parameter, the results needed to be sorted
first (via the Sort-Object
cmdlet). We just got lucky in the example where it worked that the
results were in the correct order. Moral of the story: When in doubt, read the help.
Now back to getting those packages on PC04, PC05, and PC06 in sync with the ones installed on PC03.
Instead of iterating through all of the items installed on the source and comparing them to the destination and then iterating through them again to compare the destination to the source to figure out what's missing and what's installed that shouldn't be, how about getting the differences and iterating through only the differences one time?
1Invoke-Command -Session $Session {
2 $Differences = Compare-Object -ReferenceObject $Using:software -DifferenceObject (Get-Package | Select-Object -ExpandProperty Name)
3 foreach ($Difference in $Differences) {
4 if ($Difference.SideIndicator -eq '<=') {
5 Write-Output "$Env:COMPUTERNAME is missing the $($Difference.InputObject) package"
6 Install-Package -Name $Difference.InputObject -Force
7 }
8 elseif ($Difference.SideIndicator -eq '=>'){
9 Write-Output "Unauthorized package named $($Difference.InputObject) detected on $Env:COMPUTERNAME"
10 Uninstall-Package -Name $Difference.InputObject -Force
11 }
12 }
13}
That was definitely a more efficient way of accomplishing this task. All of the machines are back in Sync once again as far as the installed packages go:
1Invoke-Command -Session $Session {Get-Package} |
2Sort-Object -Property PSComputerName |
3Format-Table -GroupBy PSComputerName
Notice that in the previous example, the results were sorted via the Sort-Object
cmdlet prior to
sending them to the Format-Table
cmdlet with the GroupBy
parameter to prevent the issue of
having multiple groups per computer.
µ