Skip to content

Commit

Permalink
Fixes, Features and Docs (#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
potatoqualitee authored Sep 26, 2022
1 parent 55bee49 commit 88908ae
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/windows-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
Import-Module ./kbupdate.psd1 -ErrorAction Stop
Write-Output "Saving scanfile"
$scanfile = Save-KbScanFile -Path C:\temp -AllowClobber
Save-KbScanFile -Path C:\temp -AllowClobber
- name: Getting needed updates
shell: powershell
Expand Down
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ kbupdate finds, downloads, installs and uninstalls Windows patches. This toolset

kbupdate started as a command-line Windows Update Catalog but can now support a number of patching tasks. It can gather and list what's already installed on a system, it can check if any updates are needed on a system (either from Microsoft Update or from Microsoft's monthly catalog) and as previously mentioned, it can install and uninstall updates, on both local and remote systems.

kbupdate can even install patches on remote systems from centralized repositories and it works on older and new versions of PowerShell. Many commands work on Linux and mac OS too.
kbupdate can even install patches on remote systems on [offline networks](#offline-patching) from a centralized repository. Many commands work on Linux and mac OS too.

## Install

Expand All @@ -24,7 +24,7 @@ Save-Module kbupdate -Path C:\temp\copy_to_usb\

## Examples

kbupdate has about 10 commands that help simplify your patching process.
`kbupdate` has around 10 commands that help simplify your patching process. Check at the bottom of the readme for instructions on how to perform [offline patching](#offline-patching).

### Get-KbUpdate

Expand Down Expand Up @@ -169,6 +169,16 @@ Gets the latest patches from a batch of patches, based on Supersedes and Superse
Get-KbUpdate -Pattern 'sql 2017' | Where-Object Classification -eq Updates | Select-KbLatest
```

### Connect/Disconnect-KbWsusServer

Connects to a local WSUS server and sets the datasource to that server.

```powershell
Connect-KbWsusServer -ComputerName server01
Get-KbUpdate -Pattern powershell -Verbose
Disconnect-KbWsusServer
```

## Installation Methodolgy

kbupdate uses [Invoke-DscResource](https://devblogs.microsoft.com/powershell/invoking-powershell-dsc-resources-directly/) to install patches on remote machines. `Invoke-DscResource` was introduced in the WMF 5.0 Preview in February 2015 and is included in Windows Server 2016+, and Windows 10+.
Expand All @@ -177,6 +187,36 @@ If you need it on older systems (going back to Windows Server 2008 R2 and Window

If you can't update to WMF 5.0+, downloading patches using kbupdate then installing them using [PSWindowsUpdate](#PSWindowsUpdate) is probably your best bet.


## Offline patching

kbupdate makes it much easier to patch your offline servers, all without SCCM or WSUS.

```
# Download latest updates
Get-KbUpdate -Since (Get-Date).AddDays(-30) -Architecture x64 |
Out-GridView -Passthru |
Save-KbUpdate -Path C:\temp\burn_to_dvd
# Download Windows Update Client scan file
Save-KbScanFile -Path C:\temp\burn_to_dvd
### 💿💿💿 BURN TO DVD 💿💿💿 ###
### 🏃🏃🏃 TRANSFER TO OFFLINE NETWORK 🏃🏃🏃 ###
# Tell Install-KbUpdate to check for all needed updates
# and point the RepositoryPath to a network server. The
# servers will grab what they need.
$params = @{
ComputerName = "sql01", "sqlcs"
AllNeeded = $true
ScanFilePath = "\\san\share\updates\wsusscn2.cab"
RepositoryPath = "\\san\share\updates"
}
Install-KbUpdate @params
```

## Screenshots

![image](https://user-images.githubusercontent.com/8278033/60805564-c127af00-a180-11e9-843a-e7d159a50aa7.png)
Expand Down
3 changes: 2 additions & 1 deletion kbupdate.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
'Uninstall-KbUpdate',
'Select-KbLatest',
'Save-KbScanFile',
'Get-KbNeededUpdate'
'Get-KbNeededUpdate',
'Disconnect-KbWsusServer'
)

AliasesToExport = @('Get-KbInstalledUpdate')
Expand Down
10 changes: 7 additions & 3 deletions kbupdate.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ if (-not $script:compcollection) {
$script:languages = . "$ModuleRoot\library\languages.ps1"
$script:languagescsv = Import-Csv -Path "$ModuleRoot\library\languages.tsv" -Delimiter `t

$kblib = Split-Path -Path (Get-Module -Name kbupdate-library | Select-Object -Last 1).Path
$kblibmod = Get-Module -Name kbupdate-library | Select-Object -Last 1
if (-not $kblibmod) {
throw "Could not find kbupdate-library module"
}
$kblib = Split-Path -Path ($kblibmod).Path
$script:basedb = (Get-ChildItem -Path "$kblib\*.sqlite" -Recurse).FullName

# This will help jobs + instances where kbupdate is not in the psmodulepath
Expand Down Expand Up @@ -87,11 +91,11 @@ Register-PSFTeppArgumentCompleter -Command Get-KbUpdate, Save-KbUpdate -Paramete

# set some defaults
if ((Get-Command -Name Get-NetConnectionProfile -ErrorAction SilentlyContinue)) {
$internet = (Get-NetConnectionProfile).IPv4Connectivity -contains "Internet"
$script:internet = (Get-NetConnectionProfile).IPv4Connectivity -contains "Internet"
} else {
try {
$network = [Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}")
$internet = ([Activator]::CreateInstance($network)).GetNetworkConnections() | ForEach-Object {
$script:internet = ([Activator]::CreateInstance($network)).GetNetworkConnections() | ForEach-Object {
$_.GetNetwork().GetConnectivity()
} | Where-Object { ($_ -band 64) -eq 64 }
} catch {
Expand Down
4 changes: 2 additions & 2 deletions private/Get-Needed.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ function Get-Needed {
NetworkRequired = $wsuskb.InstallationBehavior.RequiresNetworkConnectivity
UninstallNotes = $wsuskb.UninstallNotes
UninstallSteps = $wsuskb.UninstallSteps
Supersedes = $null #TODO
SupersededBy = $null #TODO
Supersedes = $null # not needed because WUA already figures this out
SupersededBy = $null # not needed because WUA already figures this out
Link = $links
InputObject = $wsuskb
}
Expand Down
2 changes: 1 addition & 1 deletion private/Repair-Date.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function Repair-Date ($date) {
if ($date) {
$date = $date.Replace(" ", "").Replace(".","/")
$date = "$date".Replace(" ", "").Replace(".","/")
if ("$(Get-Culture)" -ne "en-US") {
try {
$datetime = [DateTime]::ParseExact("$date 12:00:00 AM", "M/d/yyyy h:mm:ss tt",[System.Globalization.DateTimeFormatInfo]::InvariantInfo, "None")
Expand Down
2 changes: 2 additions & 0 deletions public/Connect-KbWsusServer.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ function Connect-KbWsusServer {
$currenterror = (Get-Variable -Name Error -Scope 2 -ValueOnly) | Select-Object -First 1
throw $currenterror
} else {
Write-PSFMessage -Level Verbose -Message "Success! Setting source to Wsus for this session."
$null = Set-PSFConfig -FullName kbupdate.app.source -Value Wsus
$script:ConnectedWsus
}
} catch {
Expand Down
86 changes: 86 additions & 0 deletions public/Disconnect-KbWsusServer.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
function Disconnect-KbWsusServer {
<#
.SYNOPSIS
Disconnects from all connected WSUS Servers.
.DESCRIPTION
Disconnects from all connected WSUS Servers.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Author: Chrissy LeMaire (@cl), netnerds.net
Copyright: (c) licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Disconnect-KbWsusServer
Disconnects from all connected WSUS Servers.
#>
[cmdletbinding()]
param(
[switch]$EnableException
)
begin {
If ($IsLinux -or $IsMacOs) {
return
}

if (-not (Get-Command Disconnect-PSWSUSServer -ErrorAction Ignore)) {
try {
Import-Module -Name PoshWSUS -ErrorAction Stop
} catch {
Import-Module "$script:ModuleRoot\library\PoshWSUS"
}
}

# load the DLLs, does not load properly by default
$path = Split-Path -Path (Get-Module -Name PoshWSUS).Path
$arch = "$env:PROCESSOR_ARCHITECTURE".Replace("AMD", "")
$dir = "$path\Libraries\x$($arch)"
if (Test-Path -Path $dir) {
foreach ($file in Get-ChildItem -Path $dir) {
try {
Add-Type -Path $file.Fullname -ErrorAction Stop
} catch {
# nbd
}
}
}
}
process {
if (Test-PSFPowerShell -Edition Core) {
Stop-PSFFunction -Message "Core not supported :( WSUS DLLs would have to support it, so doesn't seem likely." -EnableException:$EnableException
return
}

if ($script:ConnectedWsus) {
try {
$null = Remove-Variable -Scope Script -Name ConnectedWsus
$null = Disconnect-PSWSUSServer -WarningAction SilentlyContinue -WarningVariable warning
if (-not $script:internet) {
Write-PSFMessage -Level Verbose -Message "Internet connection not detected. Setting source for Get-KbUpdate to Database."
$null = Set-PSFConfig -FullName kbupdate.app.source -Value Database
} else {
Write-PSFMessage -Level Verbose -Message "Internet connection detected. Setting source for Get-KbUpdate to Web and Database."
$null = Set-PSFConfig -FullName kbupdate.app.source -Value Web, Database
}

# Handle the way PoshWSUS deals with errors
if ($warning) {
$currenterror = (Get-Variable -Name Error -Scope 2 -ValueOnly) | Select-Object -First 1
throw $currenterror
}
Write-PSFMessage -Level Output -Message "Disconnected from all WSUS servers."
} catch {
Stop-PSFFunction -Message "Failure" -EnableException:$EnableException -ErrorRecord $_
}
} else {
Write-PSFMessage -Level Output -Message "Not connected to any WSUS servers."
}
}
}
30 changes: 27 additions & 3 deletions public/Get-KbUpdate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ function Get-KbUpdate {
}

function Get-KbItemFromWsusApi ($kb) {
Write-PSFMessage -Level Verbose -Message "Executing 'Get-PSWSUSUpdate -Update $kb'"
$results = Get-PSWSUSUpdate -Update $kb
foreach ($wsuskb in $results) {
# cacher
Expand Down Expand Up @@ -379,7 +380,6 @@ function Get-KbUpdate {
if ($title -match "ARM-based") {
$arch = "ARM32"
}

if ($link -match "x64" -or $link -match "AMD64" -and -not $arch) {
$arch = "x64"
}
Expand All @@ -397,6 +397,30 @@ function Get-KbUpdate {
$lastmod = Repair-Date $wsuskb.ArrivalDate
}

# reset values for the loop
$supersededby = $null
$supersedes = $null

if ($guid) {
if ($global:kbupdate) {
# cache has finished importing
try {
$supersededby = $global:kbupdate["superbyhash"][$guid]
$supersedes = $global:kbupdate["superhash"][$guid]
if (-not $link) {
$link = $global:kbupdate["linkhash"][$guid]
}
} catch {
#whatever
}
} else {
# I do wish my import didn't return empties but sometimes it does so check for length of 3
$supersededby = Invoke-SqliteQuery -DataSource $script:basedb -Query "select KB, Description from SupersededBy where UpdateId = '$($guid)' COLLATE NOCASE and LENGTH(kb) > 3"
$supersedes = Invoke-SqliteQuery -DataSource $script:basedb -Query "select KB, Description from Supersedes where UpdateId = '$($guid)' COLLATE NOCASE and LENGTH(kb) > 3"
$link = (Invoke-SqliteQuery -DataSource $script:basedb -Query "select DISTINCT Link from Link where UpdateId = '$($guid)' COLLATE NOCASE").Link
}
}

$null = $script:kbcollection.Add($hashkey, (
[pscustomobject]@{
Title = $wsuskb.Title
Expand All @@ -418,8 +442,8 @@ function Get-KbUpdate {
UninstallNotes = $null # $wsuskb.uninstallnotes
UninstallSteps = $null # $wsuskb.uninstallsteps
UpdateId = $guid
Supersedes = $null #TODO
SupersededBy = $null #TODO
Supersedes = $supersedes
SupersededBy = $supersededby
Link = $link
InputObject = $kb
}))
Expand Down
2 changes: 1 addition & 1 deletion public/Uninstall-KbUpdate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ function Uninstall-KbUpdate {

if ($jobs.Name) {
try {
$jobs | Start-JobProcess -Activity "Uninstalling updates" -Status "uninstalling updates" |
$jobs | Start-JobProcess -Activity "Uninstalling software" -Status "uninstalling software" |
Select-Object -Property * -ExcludeProperty PSComputerName, RunspaceId
} catch {
Stop-PSFFunction -Message "Failure" -ErrorRecord $PSItem -EnableException:$EnableException -Continue
Expand Down

0 comments on commit 88908ae

Please sign in to comment.