Skip to content

Commit 700c1dc

Browse files
authored
Support for force scanning against Windows Update (#217)
1 parent dac4571 commit 700c1dc

6 files changed

+85
-42
lines changed

Diff for: README.md

+14-8
Original file line numberDiff line numberDiff line change
@@ -89,23 +89,27 @@ Install-KbUpdate -ComputerName server01 -HotfixId kb4486129
8989
When more than one computer is supplied, background jobs will be used to speed up the process. If you use the `-AllNeeded` switch, all needed patches will be installed.
9090

9191
```powershell
92-
# Install all needed updates and use the computer's current source as the Windows Update database checker
92+
# Install all needed updates and use the computer's Windows Update Agent configured source to scan against
9393
Install-KbUpdate -ComputerName localhost, sqlcs, sql01 -AllNeeded
9494
95-
# Install all needed updates and use the offline Windows Update database checker
95+
# Install all needed updates and use the Windows Update offline scan file to scan against
9696
$scanfile = Save-KbScanFile
9797
Install-KbUpdate -ComputerName localhost, sqlcs, sql01 -AllNeeded -ScanFilePath $scanfile
9898
```
9999

100100
### Get-KbNeededUpdate
101101

102-
Checks the target machines for needed updates. If Windows Update does not have access to WSUS or Microsoft's update catalog, a [local copy of the catalog](#Save-KbScanFile) can be provided.
102+
Checks the target machines for needed updates. If the Windows Update Agent (WUA) does not have access to WSUS or Windows Update (cloud service), a [local copy of the catalog](#Save-KbScanFile) can be provided.
103103

104+
The local catalog is the Windows Update offline scan file, `wsusscn2.cab` - [see Microsoft documentation for more information](https://learn.microsoft.com/en-us/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline).
104105

105106
```powershell
106-
# Get all the updates needed on the local machine using whatever upstream is set by Windows Update
107+
# Get all the updates needed on the local machine using whatever upstream is set by the Windows Update Agent (WUA)
107108
Get-KbNeededUpdate
108109
110+
# Same as above, but instead the upstream is Windows Update (cloud service) regardless if the device is configured to use a WSUS server
111+
Get-KbNeededUpdate -UseWindowsUpdate
112+
109113
# Get all the updates needed on server01 using the locally saved cab file
110114
$scanfile = Save-KbScanFile -Path \\server01\c$\temp
111115
Get-KbNeededUpdate -ComputerName server01 -ScanFilePath $scanfile
@@ -160,9 +164,11 @@ Get-KbInstalledSoftware -ComputerName server23 -ArgumentList "/S" | Uninstall-Kb
160164

161165
### Save-KbScanFile
162166

163-
Windows Update Agent (WUA) plus [the wsusscn2.cab catalog file](https://learn.microsoft.com/en-us/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline) can be used to scan computers for security updates without connecting to Windows Update or to a Windows Server Update Services (WSUS) server, which enables computers that are not connected to the Internet to be scanned for security updates.
167+
This downloads the [Windows Update offline scan file](https://learn.microsoft.com/en-us/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline), `wsusscn2.cab`.
168+
169+
The scan file `wsusscn2.cab` can be used to scan for missing updates without connecting to Windows Update or a Windows Server Update Services (WSUS). This is especially helpful for devices which are not connected to the Internet.
164170

165-
This file is used by `Get-KbNeededUpdate` when the `-ScanFilePath` is used and `Install-KbUpdate` when the `-AllNeeded` and `-ScanFilePath` parameters are used.
171+
This file is used by `Get-KbNeededUpdate` when the `-ScanFilePath` is used and `Install-KbUpdate` when the `-AllNeeded` and `-ScanFilePath` parameters are used.
166172

167173
```powershell
168174
# Saves the cab file to a temporary directory and returns the results of Get-ChildItem for the cab file
@@ -175,7 +181,7 @@ Save-KbScanFile -Path C:\temp -AllowClobber
175181
$scanfile = Save-KbScanFile -Path \\server01\c$\temp
176182
Get-KbNeededUpdate -ComputerName server01 -ScanFilePath $scanfile
177183
178-
# Install all needed updates and use the offline Windows Update database checker
184+
# Install all needed updates and use the Windows Update offline scan file
179185
$scanfile = Get-ChildItem -Path \\san\share\updates\wsusscn2.cab
180186
Install-KbUpdate -ComputerName localhost, sqlcs, sql01 -AllNeeded -ScanFilePath $scanfile
181187
```
@@ -226,7 +232,7 @@ Get-KbUpdate -Since (Get-Date).AddDays(-30) -Architecture x64 |
226232
Out-GridView -Passthru |
227233
Save-KbUpdate -Path C:\temp\burn_to_dvd
228234
229-
# Download Windows Update Client scan file
235+
# Download Windows Update offline scan file
230236
Save-KbScanFile -Path C:\temp\burn_to_dvd
231237
232238
### 💿💿💿 BURN TO DVD 💿💿💿 ###

Diff for: private/Get-Needed.ps1

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ function Get-Needed {
22
param (
33
$Computer,
44
$ScanFilePath,
5+
$UseWindowsUpdate,
56
$VerbosePreference
67
)
78

8-
if ($ScanFilePath) {
9+
if ($ScanFilePath -and -not $UseWindowsUpdate) {
910
try {
1011
If (-not (Test-Path $ScanFilePath -ErrorAction Stop)) {
1112
Write-Warning "Windows Update offline scan file, $ScanFilePath, cannot be found on $Computer"
@@ -24,7 +25,7 @@ function Get-Needed {
2425
Write-Verbose -Message "Processing $computer"
2526
$session = [type]::GetTypeFromProgID("Microsoft.Update.Session")
2627
$wua = [activator]::CreateInstance($session)
27-
if ($ScanFilePath) {
28+
if ($ScanFilePath -and -not $UseWindowsUpdate) {
2829
Write-Verbose -Message "Registering $ScanFilePath on $computer"
2930
try {
3031
$progid = [type]::GetTypeFromProgID("Microsoft.Update.ServiceManager")
@@ -45,6 +46,11 @@ function Get-Needed {
4546
} else {
4647
Write-Verbose -Message "Creating update searcher"
4748
$searcher = $wua.CreateUpdateSearcher()
49+
50+
if ($UseWindowsUpdate) {
51+
Write-Verbose -Message "Setting update searcher to use Windows Update cloud service"
52+
$searcher.ServerSelection = 2
53+
}
4854
}
4955
Write-Verbose -Message "Searching for needed updates"
5056
$wsuskbs = $searcher.Search("Type='Software' and IsHidden=0")

Diff for: private/Start-DscUpdate.ps1

+10-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function Start-DscUpdate {
2020
[Parameter(ValueFromPipeline)]
2121
[pscustomobject[]]$InputObject,
2222
[switch]$AllNeeded,
23+
[switch]$UseWindowsUpdate,
2324
[switch]$NoMultithreading,
2425
[switch]$EnableException,
2526
[bool]$IsLocalHost,
@@ -58,11 +59,17 @@ function Start-DscUpdate {
5859
}
5960

6061
if ($AllNeeded) {
62+
$GetKbNeededUpdate = @{
63+
ComputerName = $ComputerName
64+
}
6165
if ($ScanFilePath) {
62-
$InputObject = Get-KbNeededUpdate -ComputerName $ComputerName -ScanFilePath $ScanFilePath -Force
63-
} else {
64-
$InputObject = Get-KbNeededUpdate -ComputerName $ComputerName
66+
$GetKbNeededUpdate['ScanFilePath'] = $ScanFilePath
67+
$GetKbNeededUpdate['Force'] = $true
68+
69+
} elseif ($UseWindowsUpdate) {
70+
$GetKbNeededUpdate['UseWindowsUpdate'] = $true
6571
}
72+
$InputObject = Get-KbNeededUpdate @GetKbNeededUpdate
6673
}
6774

6875
if ($HotfixId -and -not $InputObject.Link) {

Diff for: public/Connect-KbWsusServer.ps1

-4
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ function Connect-KbWsusServer {
1515
.PARAMETER Port
1616
Port number to connect to. Default is Port "443" and "8530" if using HTTP. Accepted values are "80","443","8350" and "8351"
1717
18-
.PARAMETER Credential
19-
The optional alternative credential to be used when connecting to ComputerName.
20-
2118
.PARAMETER EnableException
2219
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
2320
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
@@ -51,7 +48,6 @@ function Connect-KbWsusServer {
5148
[Parameter(ValueFromPipeline)]
5249
[Alias("WsusServer")]
5350
[PSFComputer]$ComputerName,
54-
[pscredential]$Credential,
5551
[switch]$ForceInsecureConnection,
5652
[ValidateSet("80", "443", "8530", "8531" )]
5753
[int]$Port = 443,

Diff for: public/Get-KbNeededUpdate.ps1

+39-23
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
function Get-KbNeededUpdate {
22
<#
33
.SYNOPSIS
4-
Checks for needed updates.
4+
Scan for missing Windows updates.
55
66
.DESCRIPTION
7-
Checks for needed updates.
7+
This cmdlet scans for missing Windows updates. It can scan against:
8+
9+
- Windows Server Update Service (WSUS) server
10+
- Windows Update (cloud service)
11+
- Windows Update offline scan file (wsusscn2.cab - see https://learn.microsoft.com/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline)
12+
13+
The offline scan file can be downloaded using Save-KbScanFile from an internet-connected computer.
814
915
.PARAMETER ComputerName
1016
Used to connect to a remote host. Connects to localhost by default -- if scanning the local computer, the command must be run as administrator.
1117
1218
.PARAMETER Credential
1319
The optional alternative credential to be used when connecting to ComputerName
1420
21+
.PARAMETER UseWindowsUpdate
22+
This optional parameter will force the Windows Update Agent (WUA) to scan for needed updates against Windows Update (cloud service) instead of WSUS, regardless if the device is configured to use a WSUS server.
23+
1524
.PARAMETER ScanFilePath
16-
If Windows Update does not have access to WSUS or Microsoft's update catalog, a local copy of the catalog can be provided.
25+
If the Windows Update Agent (WUA) does not have access to WSUS or Windows Update a local copy of the catalog can be provided.
26+
27+
The local copy of the catalog is the Windows Update offline scan file (wsusscn2.cab - see https://learn.microsoft.com/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline).
1728
18-
This optional parameter will force the command to use a local update database instead of WSUS or Microsoft's online update catalog.
29+
This optional parameter will force the command to use a local update database instead of WSUS or Windows Update.
1930
2031
The scan file catalog/database can be downloaded using Save-KbScanFile from an internet-connected computer.
2132
@@ -52,13 +63,16 @@ function Get-KbNeededUpdate {
5263
5364
Saves all the updates needed on the local machine to C:\temp
5465
#>
55-
[CmdletBinding()]
66+
[CmdletBinding(DefaultParameterSetName = 'UseWUA')]
5667
param(
5768
[PSFComputer[]]$ComputerName = $env:COMPUTERNAME,
5869
[pscredential]$Credential,
59-
[parameter(ValueFromPipeline)]
70+
[parameter(ParameterSetName = 'UseWUA')]
71+
[switch]$UseWindowsUpdate,
72+
[parameter(ParameterSetName = 'UseScanFile', ValueFromPipeline)]
6073
[Alias("FullName")]
6174
[string]$ScanFilePath,
75+
[parameter(ParameterSetName = 'UseScanFile')]
6276
[switch]$Force,
6377
[switch]$EnableException
6478
)
@@ -79,32 +93,34 @@ function Get-KbNeededUpdate {
7993
try {
8094
Write-PSFMessage -Level Verbose -Message "Adding job for $computer"
8195
$arglist = [pscustomobject]@{
82-
ComputerName = $computer
83-
Credential = $Credential
84-
ScanFilePath = $ScanFilePath
85-
EnableException = $EnableException
86-
Force = $Force
87-
ScriptBlock = $remotescriptblock
88-
ModulePath = $script:dependencies
96+
ComputerName = $computer
97+
Credential = $Credential
98+
UseWindowsUpdate = $UseWindowsUpdate
99+
ScanFilePath = $ScanFilePath
100+
EnableException = $EnableException
101+
Force = $Force
102+
ScriptBlock = $remotescriptblock
103+
ModulePath = $script:dependencies
89104
}
90105

91106
$invokeblock = {
92107
foreach ($path in $args.ModulePath) {
93108
$null = Import-Module $path 4>$null
94109
}
95-
$sbjson = $args.ScriptBlock | ConvertFrom-Json
96-
$sb = [scriptblock]::Create($sbjson)
97-
$machine = $args.ComputerName
98-
$Credential = $args.Credential
99-
$ScanFilePath = $args.ScanFilePath
100-
$EnableException = $args.EnableException
101-
$Force = $args.Force
102-
$ScriptBlock = $sb
110+
$sbjson = $args.ScriptBlock | ConvertFrom-Json
111+
$sb = [scriptblock]::Create($sbjson)
112+
$machine = $args.ComputerName
113+
$Credential = $args.Credential
114+
$UseWindowsUpdate = $args.UseWindowsUpdate
115+
$ScanFilePath = $args.ScanFilePath
116+
$EnableException = $args.EnableException
117+
$Force = $args.Force
118+
$ScriptBlock = $sb
103119

104120
$computer = $machine.ComputerName
105121
$null = $completed++
106122

107-
if ($ScanFilePath -and $Force -and -not $machine.IsLocalhost) {
123+
if ($ScanFilePath -and $Force -and -not $machine.IsLocalhost -and -not $UseWindowsUpdate) {
108124
Write-PSFMessage -Level Verbose -Message "Initializing remote session to $computer and getting the path to the temp directory"
109125

110126
$scanfile = Get-ChildItem -Path $ScanFilePath
@@ -185,7 +201,7 @@ function Get-KbNeededUpdate {
185201
if ($machine.IsLocalHost -and -not (Test-ElevationRequirement -ComputerName $computer)) {
186202
continue
187203
}
188-
Invoke-PSFCommand -Computer $computer -Credential $Credential -ErrorAction Stop -ScriptBlock $scriptblock -ArgumentList $computer, $cabpath, $VerbosePreference
204+
Invoke-PSFCommand -Computer $computer -Credential $Credential -ErrorAction Stop -ScriptBlock $scriptblock -ArgumentList $computer, $cabpath, $UseWindowsUpdate, $VerbosePreference
189205
}
190206

191207
$jobs += Start-Job -Name $computer -ScriptBlock $invokeblock -ArgumentList $arglist -ErrorAction Stop

Diff for: public/Install-KbUpdate.ps1

+14-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,13 @@ function Install-KbUpdate {
3737
.PARAMETER AllNeeded
3838
Installs all needed updates
3939
40+
.PARAMETER UseWindowsUpdate
41+
This optional parameter will force the Windows Update Agent (WUA) to scan for needed updates against Windows Update (cloud service) instead of WSUS, regardless if the device is configured to use a WSUS server.
42+
4043
.PARAMETER ScanFilePath
41-
This optional parameter can be passed along with AllNeeded to use a local update database instead of WSUS or Microsoft's online update catalog.
44+
This optional parameter can be passed along with AllNeeded to use a local copy of the catalogue instead of WSUS or Windows Update (cloud service.)
45+
46+
The local copy of the catalog is the Windows Update offline scan file (wsusscn2.cab - see https://learn.microsoft.com/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline).
4247
4348
The local copy can be downloaded using Save-KbScanFile from an internet-connected computer.
4449
@@ -122,6 +127,7 @@ function Install-KbUpdate {
122127
[Parameter(ValueFromPipeline)]
123128
[pscustomobject[]]$InputObject,
124129
[switch]$AllNeeded,
130+
[switch]$UseWindowsUpdate,
125131
[parameter(ValueFromPipeline)]
126132
[Alias("FullName")]
127133
[string]$ScanFilePath,
@@ -133,14 +139,19 @@ function Install-KbUpdate {
133139
# $wublock = [scriptblock]::Create($((Get-Command Start-WindowsUpdate).Definition))
134140
$dscblock = [scriptblock]::Create($((Get-Command Start-DscUpdate).Definition))
135141
# cleanup
136-
$null = Get-Job -ChildJobState Completed | Where-Object Name -in $ComputerName.ComputerName | Remove-Job -Force
142+
$null = Get-Job -ChildJobState Completed | Where-Object Name -In $ComputerName.ComputerName | Remove-Job -Force
137143
}
138144
process {
139145
if (-not $PSBoundParameters.HotfixId -and -not $PSBoundParameters.FilePath -and -not $PSBoundParameters.InputObject -and -not $AllNeeded) {
140146
Stop-PSFFunction -EnableException:$EnableException -Message "You must specify either HotfixId or FilePath or AllNeeded or pipe in the results from Get-KbUpdate"
141147
return
142148
}
143149

150+
if ($ScanFilePath -and $UseWindowsUpdate) {
151+
Stop-PSFFunction -EnableException:$EnableException -Message "You can not use -ScanFilePath and -UseWindowsUpdate together"
152+
return
153+
}
154+
144155
if ($IsLinux -or $IsMacOs) {
145156
Stop-PSFFunction -Message "This command using remoting and only supports Windows at this time" -EnableException:$EnableException
146157
return
@@ -190,6 +201,7 @@ function Install-KbUpdate {
190201
EnableException = $EnableException
191202
IsLocalHost = $computer.IsLocalHost
192203
AllNeeded = $AllNeeded
204+
UseWindowsUpdate = $UseWindowsUpdate
193205
VerbosePreference = $VerbosePreference
194206
ScanFilePath = $ScanFilePath
195207
ModulePath = $script:dependencies

0 commit comments

Comments
 (0)