PDQ Package Library and the PSWindowsUpdate PowerShell Module

Purpose:

You are looking for a WSUS/Microsoft Update replacement integrated with PDQ Products to ensure that endpoints have all available patches from Microsoft.

Overview:

Although we currently offer Microsoft Cumulative Updates in our Package Library, we frequently have customers asking about a full WSUS/Microsoft Update replacement integrated into our products. While we typically avoid third-party tools, the PSWindowsUpdate module is well-known and widely used for checking and applying all available Microsoft updates to a machine and can be a viable alternative to WSUS/Microsoft Update. This is also an advantage for Connect customers, having the Microsoft Updates downloaded directly from Microsoft servers instead of a traditional on-premise server. 

If you are a military or government organization, you may want to review this PowerShell Module in greater detail before placing it into production to ensure that it meets your security standards:

Publisher: PowerShellGallery

Download: PSWindowsUpdate

VirusTotal: Report

Requirements:

1. Endpoints must have internet access. These packages will not work on air-gapped networks.

2. PowerShell scripts must be enabled and allowed to run on endpoints.

3. Endpoints must have access to Microsoft servers to download updates.

Getting Started:

There are currently 6 Packages in the Package Library available for you to use:

1. PSWindowsUpdate - Get All Applicable Updates from Microsoft (Audit Only)

2. PSWindowsUpdate - Install All Applicable Updates from Microsoft (No Drivers, No Feature Updates)

3. PSWindowsUpdate - Install All Applicable Critical and Security Updates from Microsoft

4. PSWindowsUpdate - Install All Applicable Drivers from Microsoft

5. PSWindowsUpdate - Install All Applicable Feature Updates from Microsoft

6. PSWindowsUpdate - Install Specific Microsoft KB

Each of these packages will do the following:

1. Check if the NuGet Package Provider is installed. If it is not, it will attempt to download and install the Nuget Provider.

2. Check if the PSWindowsUpdate module is installed. If it is not, it will use the NuGet installer to attempt to download and install the PSWindowsUpdate module.

3. It will then check for any existing WSUS/Windows Update registry settings and back them up before removing them. (This is to avoid returning a partial list of available updates restricted by policies)

4. It will then run the PSWindowsUpdate module and check for all available Microsoft patches.

5. Then, depending on which of the 6 packages you've selected to run, it will install those updates.

6. Reboots are disabled, but may be required in order to complete the installation of some patches and properly reflect that packages have been successfully installed

7. Finally, it will look and if the registry did have existing WSUS/Windows update policies, it will restore those settings to honor the organization GPO/configuration.

PACKAGE: Get All Applicable Updates from Microsoft (Audit Only)

This package is an Audit-Only package. You can run this and then view the Output log to see what applicable Microsoft Updates are available for the endpoint. You can then choose which other PSWindowsUpdate packages to use to install a subset or all of the updates on the machine.

IMPORTANT! Additional setup for PDQ Deploy and Inventory customers:

1. To extend this audit functionality for PDQ Inventory users, there are 3 additional files that have been bundled with this package. Once the package is downloaded, You can find them here:

$(Repository)\PSWindowsUpdate\InventoryScanner_GetAllApplicableUpdates.ps1
$(Repository)\PSWindowsUpdate\InventoryScanner_GetApplicableMicrosoftUpdates.xml
$(Repository)\PSWindowsUpdate\InventoryReport_GetApplicableMicrosoftUpdates.xml

(The default path for $(Repository) is typically: C:\Users\Public\Documents\Admin Arsenal\PDQ Deploy\Repository)

2. Open PDQ Inventory / Right-click on Scan Profiles and choose Import. Navigate to:

 $(Repository)\PSWindowsUpdate\InventoryScanner_GetApplicableMicrosoftUpdates.xml

and choose Open

3. Open PDQ Inventory / Right-click on Scan Profiles and choose Import. Navigate to:

 $(Repository)\PSWindowsUpdate\InventoryScanner_GetApplicableMicrosoftUpdates.xml

and choose Open. (If you require signed PowerShell scripts in your environment, you will need to edit the scanner you just imported and instead of the included script, you will need to change to File and browse to the signed PowerShell script $(Repository)\PSWindowsUpdate\InventoryScanner_GetAllApplicableUpdates.ps1.)

4. You can now right-click and Scan Computers or Scan Collection with the newly imported Get Applicable Microsoft Updates scanner

5. You can view the results of these scans by opening a computer's inventory, clicking on the PowerShell section, and then choosing PowerShell (Get Applicable Microsoft Updates) from the drop-down box.
Available updates will appear or you may notice a single entry indicating: No applicable updates.

6. Additionally, for a report of all computers scanned, if you can return to the Inventory home screen, Right-click on Reports and choose Import. Navigate to:

 $(Repository)\PSWindowsUpdate\InventoryReport_GetApplicableMicrosoftUpdates.xml

and choose Open and now run the Get Applicable Updates from Microsoft report. This will report on all machines that you have scanned with the Get Applicable Microsoft Updates scanner (Step 4) and help you identify which Microsoft updates are applicable to your environment.

7. You can then take action to install the updates you choose with one of the following PSWindowsUpdate Packages:

PACKAGE: Install All Applicable Updates from Microsoft (No Drivers, No Feature Updates)

This package will install ALL applicable updates from Microsoft, but will not install hardware drivers or feature updates.

PACKAGE: Install All Applicable Critical and Security Updates from Microsoft

This package will install Critical and Security Patches ONLY. This package will install cumulative updates as well as other critical updates and security updates.

PACKAGE: Install All Applicable Drivers from Microsoft

There are many hardware drivers now available from Microsoft to update, including but not limited to printers, video, mice, keyboards, network, and third-party firmware drivers. We strongly recommend additional testing, especially with network and firmware drivers on test machines or a test lab before deploying to remote machines and risking OS or network connectivity issues with installation failure.

PACKAGE: Install All Applicable Feature Updates from Microsoft

This package will specifically install Feature Updates. Please note that Feature Updates can take quite a bit of time to download and install, so you may need additional contact with end users to coordinate a block of time for a feature update (overnight deployment).

PACKAGE: Install Specific Microsoft KB

This package currently is only applicable for PDQ Deploy and gives you the ability to directly modify the KB Number you wish to deploy in the Parameters field in Step 1. Please pay close attention to preserving the exact syntax in the Parameters field:

Example 1 with one KB Number:

-KBArticleID 'KB8675309'

Example 2 with 2 or more KB Numbers to install:

-KBArticleID 'KB8675309, KB9274836, KB3371337'

Customizing PSWindowsUpdate for your own Environment:

While the default packages above will be a suitable WSUS/Microsoft Update alternative for the majority of our customers, If you are comfortable with PowerShell, You can download any of the PSWindowsUpdate packages (With the exception of the Specific Microsoft KB package) and convert the auto-download package to a standard package. Then you can browse to the PowerShell file in Step 1 and there are many different options to target and install updates specifically:

General Categories:'Critical Updates', 'Definition Updates', 'Drivers', 'Feature Packs', 'Security Updates', 'Service Packs', 'Tools', 'Update Rollups', 'Updates', 'Upgrades'
Category: CategoryIDApplication: 5C9376AB-8CE6-464A-B136-22113DD69801Connectors: 434DE588-ED14-48F5-8EED-A15E09A991F6Critical Updates: E6CF1350-C01B-414D-A61F-263D14D133B4Definition Updates: E0789628-CE08-4437-BE74-2495B842F43BDeveloper Kits: E140075D-8433-45C3-AD87-E72345B36078Feature Packs: B54E7D24-7ADD-428F-8B75-90A396FA584FGuidance: 9511D615-35B2-47BB-927F-F73D8E9260BBSecurity Updates: 0FA1201D-4330-4FA8-8AE9-B877473B6441ServicePacks: 68C5B0A3-D1A6-4553-AE49-01D3A7827828Tools: B4832BD8-E735-4761-8DAF-37F882276DABUpdateRollups: 28BC880E-0592-4CBF-8F95-C79B17911D5FUpdates: CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83

Microsoft Reference

EXAMPLE: Get all available patches from Microsoft

Get-WindowsUpdate -MicrosoftUpdate -Verbose

EXAMPLE: Exclude information:

Get-WindowsUpdate -MicrosoftUpdate -Verbose -NotCategory 'Drivers' -NotTitle 'OneDrive' -NotKBArticleID 'KB4489873'

EXAMPLE: Install updates by Category

Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -Category 'Critical Updates', 'Security Updates', 'Updates'

EXAMPLE: Install updates by CategoryID

Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -CategoryIDs 'E6CF1350-C01B-414D-A61F-263D14D133B4', 'CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83', '0FA1201D-4330-4FA8-8AE9-B877473B6441'

EXAMPLE: Install updates by KB number:

Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -KBArticleID 'KB2267602', 'KB4533002'

Full Script: Get/Install Available Microsoft Updates

With the exception of the "Install Specific Microsoft KB" script, all scripts are really the same with the only difference being one line of code:

Get-WindowsUpdate -MicrosoftUpdate -Verbose

You can change this with any of the lines above to alter what you want to install:

Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -NotCategory 'Drivers' -NotTitle 'Feature'

Here's the full script:

<###########################################################################
PSWindowsUpdates PowerShell Module Script adapted for PDQ Products
Created: 3/14/2023 C2
Updated: 3/26/2024 C2

General Categories:
Critical Updates', 'Definition Updates', 'Drivers', 'Feature Packs', 'Security Updates', 'Service Packs', 'Tools', 'Update Rollups', 'Updates', 'Upgrades', 'Microsoft'

Category				CategoryIDs
Application				5C9376AB-8CE6-464A-B136-22113DD69801
Connectors				434DE588-ED14-48F5-8EED-A15E09A991F6
Critical Updates		E6CF1350-C01B-414D-A61F-263D14D133B4
Definition Updates		E0789628-CE08-4437-BE74-2495B842F43B
Developer Kits			E140075D-8433-45C3-AD87-E72345B36078
Feature Packs			B54E7D24-7ADD-428F-8B75-90A396FA584F
Guidance				9511D615-35B2-47BB-927F-F73D8E9260BB
Security Updates		0FA1201D-4330-4FA8-8AE9-B877473B6441
ServicePacks			68C5B0A3-D1A6-4553-AE49-01D3A7827828
Tools					B4832BD8-E735-4761-8DAF-37F882276DAB
UpdateRollups			28BC880E-0592-4CBF-8F95-C79B17911D5F
Updates					CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83
Source: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ff357803(v=vs.85)

EXAMPLE: Get all available patches from Microsoft
Get-WindowsUpdate -MicrosoftUpdate -Verbose

EXAMPLE: Exclude information:
Get-WindowsUpdate -MicrosoftUpdate -Verbose -NotCategory 'Drivers' -NotTitle 'OneDrive' -NotKBArticleID 'KB4489873'

EXAMPLE: Install updates by Category
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -Category 'Critical Updates', 'Security Updates', 'Updates'

EXAMPLE: Install updates by CategoryID
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -CategoryIDs 'E6CF1350-C01B-414D-A61F-263D14D133B4', 'CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83', '0FA1201D-4330-4FA8-8AE9-B877473B6441'

EXAMPLE: Install updates by KB number:
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -KBArticleID 'KB2267602', 'KB4533002'
############################################################################>


########## Set Variables ####################################################
$StopWatch = [System.Diagnostics.Stopwatch]::StartNew()
$TimeStampBegin = Get-Date -Format "G"
$ProviderName = "NuGet"
$Module = "PSWindowsUpdate"
$WSUSRegPath = "HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate"
$WSUSRegPath2 = "HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate"
$WSUSBackupPath = Join-Path -Path $env:SystemDrive -ChildPath "PDQ\PSWindowsUpdates"
$WSUSBackupFile = Join-Path -Path $WSUSBackupPath -ChildPath "ConfigurationBackup.reg"
Write-Output "Script started: $TimeStampBegin"
#############################################################################


########## Check Nuget Status ###############################################
Write-Output ""
Write-Output "Attempting to set SecurityProtocol to Tls12 for Net.ServicePointManager..."
TRY {
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    Write-Output "SecurityProtocol set to Tls12."
}
CATCH {
    Write-Output "Unable to set SecurityProtocol to Tls12 for Net.ServicePointManager: $($_.Exception.Message)"
}
Write-Output ""
Write-Output "Checking if $ProviderName provider is installed..."
TRY {
    Install-PackageProvider -Name $ProviderName -Force -confirm:$False
    Write-Output "$ProviderName provider is installed."
}
CATCH {
    THROW "Unable to install $ProviderName provider: $($_.Exception.Message)"
}
#############################################################################


########## Check PSWindowsUpdate Module Status ##############################
Write-Output ""
Write-Output "Checking if $Module module is installed..."
TRY {
    Install-Module -Name $Module -Force -Confirm:$False
    Get-InstalledModule -Name $Module | Select-Object Name, Version, Repository, Description | Format-Table -Autosize
    Write-Output "$Module module is installed."
}
CATCH {
    THROW "Unable to install $Module module: $($_.Exception.Message)"
}
#############################################################################


########## Check for Existing Windows/WSUS configurations ###################
Write-Output ""
Write-Output "Checking for WindowsUpdate/WSUS registry settings..."
IF(Test-Path $WSUSRegPath){
    Write-Output "WindowsUpdate/WSUS registry settings exist."
    
    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Stopped") {
        Write-Output "The Windows Update service has been stopped."
        break
        } ELSE {
        Write-Output "Stopping the Windows Update service..."
        Stop-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Stopped")

    Write-Output ""
    Write-Output "Checking if WindowsUpdate/WSUS backup directory exists..."
    IF (-not (Test-Path $WSUSBackupPath)) {
        Write-Output "WindowsUpdate/WSUS backup directory does not exist."
        TRY {
            New-Item -Path $WSUSBackupPath -ItemType Directory -Force
            Write-Output "WindowsUpdate/WSUS backup directory created:"
            Write-Output "$WSUSBackupPath"
            }
        CATCH {
            THROW "Unable to create backup directory: $WSUSBackupPath $($_.Exception.Message)"
            }
    } ELSE {
        Write-Output "WindowsUpdate/WSUS backup directory exists:"
        Write-Output "$WSUSBackupPath"
    }

    Write-Output ""
    TRY {
        Write-Output "Exporting Current WindowsUpdate/WSUS registry settings..."
        Write-Output "From: $WSUSRegPath2"
        Write-Output "To: $WSUSBackupFile"
        $Process = Start-Process reg -ArgumentList "export $WSUSRegPath2 $WSUSBackupFile /y" -PassThru -Wait
        Write-Output "WindowsUpdate/WSUS registry settings file created."
    } CATCH {
        THROW "An error occurred during registry export to $WSUSBackupFile $($_.Exception.Message)"
    }
    
    Write-Output ""
    Write-Output "Removing current WindowsUpdate/WSUS registry settings..."
    Remove-Item $WSUSRegPath -Recurse
    Write-Output "WindowsUpdate/WSUS registry settings cleaned."

    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Running") {
        Write-Output "The Windows Update service has been started."
        break
        } ELSE {
        Write-Output "Starting the Windows Update service..."
        Start-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Running")
} ELSE {
    Write-Output "WindowsUpdate/WSUS registry settings do not exist."
}
#############################################################################


########## Importing the PSWindowsUpdate Module into the session ############
Write-Output ""
Write-Output "Importing $Module module into powershell session..."
TRY {
    Import-Module $Module
    Write-Output "$Module module has been imported into powershell session."
    }
CATCH {
    THROW "Unable to load $Module Module into powershell session $($_.Exception.Message)"
    }
#############################################################################


########## Checking for Available Microsoft Updates #########################
Write-Output ""
Write-Output "Retrieving available Microsoft Updates..."
TRY {
    $GWU = Get-WindowsUpdate -MicrosoftUpdate -Verbose
    }
CATCH {
    THROW "$Module failed to retrieve available Microsoft Updates $($_.Exception.Message)"
    }
#############################################################################


########## Return Results for Available Microsoft Updates ###################
Write-Output "-----------------------------------------------------------------------------"
IF ($null -ne $GWU) {
    $GWU | ForEach-Object {
        [PSCustomObject]@{
            "KB"                              = $_.KB
            "Title"                           = $_.Title
            "Size"                            = $_.Size
            "Type"                            = switch ($_.Type) {
                1 { "Software" }
                2 { "Driver" }
                Default { "Unknown" } 
            }
            "SupportUrl"                      = $_.SupportUrl
            "Description"                     = $_.Description
        }
        Write-Output "-----------------------------------------------------------------------------"
    }
}
ELSE {
    [PSCustomObject]@{
        "KB"                              = "No Applicable Updates"
        "Title"                           = $null
        "Size"                            = $null
        "Type"                            = $null
        "SupportUrl"                      = $null
        "Description"                     = $null
    }
    Write-Output "-----------------------------------------------------------------------------"
}
#############################################################################


########## Install Selected Microsoft Updates ###############################
Write-Output ""
Write-Output "Installing Selected Microsoft Updates..."
Write-Output "-----------------------------------------------------------------------------"
TRY {
    Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -NotCategory 'Drivers' -NotTitle 'Feature'
}
CATCH {
    THROW "$Module failed to Install Selected Microsoft Updates $($_.Exception.Message)"
}
Write-Output "-----------------------------------------------------------------------------"
#############################################################################


########## Restore Windows/WSUS configurations ##############################
Write-Output ""
Write-Output "Checking if Windows/WSUS registry settings file exists..."
IF(Test-Path $WSUSBackupFile -PathType Leaf){
    Write-Output "Windows/WSUS registry settings file exists."
    
    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Stopped") {
        Write-Output "The Windows Update service has been stopped."
        break
        } ELSE {
        Write-Output "Stopping the Windows Update service..."
        Stop-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Stopped")

    Write-Output ""
    TRY {
        Write-Output "Restoring Windows/WSUS settings to the registry..."
        Write-Output "From: $WSUSBackupFile"
        Write-Output "To: $WSUSRegPath2"
        $Process = Start-Process reg -ArgumentList "import $WSUSBackupFile" -PassThru -Wait
        Write-Output "WindowsUpdate/WSUS registry settings restored."
    } CATCH {
        THROW "An error occurred importing the WindowsUpdate/WSUS registry settings to $WSUSRegPath2 $($_.Exception.Message)"
    }

    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Running") {
        Write-Output "The Windows Update service has been started."
        break
        } ELSE {
        Write-Output "Starting the Windows Update service..."
        Start-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Running")
} ELSE {
    Write-Output "Windows/WSUS registry settings file does not exist."
}
#############################################################################


########## Wrapping Up ######################################################
Write-Output ""
$TimeStampEnd = Get-Date -Format "G"
Write-Output "Script finished: $TimeStampEnd"
$StopWatch.Stop()
$TotalTime = $StopWatch.Elapsed.TotalSeconds
$TotalTime = [Math]::Round($TotalTime, 2)
Write-Output "Total script run time: $TotalTime(sec)"
#############################################################################

Full Script: Install Specific Microsoft KB

This script includes an additional parameter block to change and read the KBNumber you wish to deploy:

[CmdletBinding()]param ([Parameter(Mandatory = $true)][String]$KBArticleID)Write-Host "KBArticleID is $KBArticleID"

The PowerShell script then looks for an additional parameter when running the script:

-KBArticleID 'KB8675309'

Here's the full script:

<###########################################################################
PSWindowsUpdates PowerShell Module Script adapted for PDQ Products
Created: 3/14/2023 C2
Updated: 3/26/2024 C2

General Categories:
Critical Updates', 'Definition Updates', 'Drivers', 'Feature Packs', 'Security Updates', 'Service Packs', 'Tools', 'Update Rollups', 'Updates', 'Upgrades', 'Microsoft'

Category				CategoryIDs
Application				5C9376AB-8CE6-464A-B136-22113DD69801
Connectors				434DE588-ED14-48F5-8EED-A15E09A991F6
Critical Updates		E6CF1350-C01B-414D-A61F-263D14D133B4
Definition Updates		E0789628-CE08-4437-BE74-2495B842F43B
Developer Kits			E140075D-8433-45C3-AD87-E72345B36078
Feature Packs			B54E7D24-7ADD-428F-8B75-90A396FA584F
Guidance				9511D615-35B2-47BB-927F-F73D8E9260BB
Security Updates		0FA1201D-4330-4FA8-8AE9-B877473B6441
ServicePacks			68C5B0A3-D1A6-4553-AE49-01D3A7827828
Tools					B4832BD8-E735-4761-8DAF-37F882276DAB
UpdateRollups			28BC880E-0592-4CBF-8F95-C79B17911D5F
Updates					CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83
Source: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ff357803(v=vs.85)

EXAMPLE: Get all available patches from Microsoft
Get-WindowsUpdate -MicrosoftUpdate -Verbose

EXAMPLE: Exclude information:
Get-WindowsUpdate -MicrosoftUpdate -Verbose -NotCategory 'Drivers' -NotTitle 'OneDrive' -NotKBArticleID 'KB4489873'

EXAMPLE: Install updates by Category
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -Category 'Critical Updates', 'Security Updates', 'Updates'

EXAMPLE: Install updates by CategoryID
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -CategoryIDs 'E6CF1350-C01B-414D-A61F-263D14D133B4', 'CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83', '0FA1201D-4330-4FA8-8AE9-B877473B6441'

EXAMPLE: Install updates by KB number:
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -KBArticleID 'KB2267602', 'KB4533002'
############################################################################>


########## Get User Input ###################################################
[CmdletBinding()]
param (
    [Parameter(Mandatory=$true)]
    [String]$KBArticleID
)
#############################################################################


########## Set Variables ####################################################
$StopWatch = [System.Diagnostics.Stopwatch]::StartNew()
$TimeStampBegin = Get-Date -Format "G"
$ProviderName = "NuGet"
$Module = "PSWindowsUpdate"
$WSUSRegPath = "HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate"
$WSUSRegPath2 = "HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate"
$WSUSBackupPath = Join-Path -Path $env:SystemDrive -ChildPath "PDQ\PSWindowsUpdates"
$WSUSBackupFile = Join-Path -Path $WSUSBackupPath -ChildPath "ConfigurationBackup.reg"
Write-Output "Script started: $TimeStampBegin"
Write-Output "$KBArticleID selected to install."
#############################################################################


########## Check Nuget Status ###############################################
Write-Output ""
Write-Output "Attempting to set SecurityProtocol to Tls12 for Net.ServicePointManager..."
TRY {
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    Write-Output "SecurityProtocol set to Tls12."
}
CATCH {
    Write-Output "Unable to set SecurityProtocol to Tls12 for Net.ServicePointManager: $($_.Exception.Message)"
}
Write-Output ""
Write-Output "Checking if $ProviderName provider is installed..."
TRY {
    Install-PackageProvider -Name $ProviderName -Force -confirm:$False
    Write-Output "$ProviderName provider is installed."
}
CATCH {
    THROW "Unable to install $ProviderName provider: $($_.Exception.Message)"
}
#############################################################################


########## Check PSWindowsUpdate Module Status ##############################
Write-Output ""
Write-Output "Checking if $Module module is installed..."
TRY {
    Install-Module -Name $Module -Force -Confirm:$False
    Get-InstalledModule -Name $Module | Select-Object Name, Version, Repository, Description | Format-Table -Autosize
    Write-Output "$Module module is installed."
}
CATCH {
    THROW "Unable to install $Module module: $($_.Exception.Message)"
}
#############################################################################


########## Check for Existing Windows/WSUS configurations ###################
Write-Output ""
Write-Output "Checking for WindowsUpdate/WSUS registry settings..."
IF(Test-Path $WSUSRegPath){
    Write-Output "WindowsUpdate/WSUS registry settings exist."
    
    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Stopped") {
        Write-Output "The Windows Update service has been stopped."
        break
        } ELSE {
        Write-Output "Stopping the Windows Update service..."
        Stop-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Stopped")

    Write-Output ""
    Write-Output "Checking if WindowsUpdate/WSUS backup directory exists..."
    IF (-not (Test-Path $WSUSBackupPath)) {
        Write-Output "WindowsUpdate/WSUS backup directory does not exist."
        TRY {
            New-Item -Path $WSUSBackupPath -ItemType Directory -Force
            Write-Output "WindowsUpdate/WSUS backup directory created:"
            Write-Output "$WSUSBackupPath"
            }
        CATCH {
            THROW "Unable to create backup directory: $WSUSBackupPath $($_.Exception.Message)"
            }
    } ELSE {
        Write-Output "WindowsUpdate/WSUS backup directory exists:"
        Write-Output "$WSUSBackupPath"
    }

    Write-Output ""
    TRY {
        Write-Output "Exporting Current WindowsUpdate/WSUS registry settings..."
        Write-Output "From: $WSUSRegPath2"
        Write-Output "To: $WSUSBackupFile"
        $Process = Start-Process reg -ArgumentList "export $WSUSRegPath2 $WSUSBackupFile /y" -PassThru -Wait
        Write-Output "WindowsUpdate/WSUS registry settings file created."
    } CATCH {
        THROW "An error occurred during registry export to $WSUSBackupFile $($_.Exception.Message)"
    }
    
    Write-Output ""
    Write-Output "Removing current WindowsUpdate/WSUS registry settings..."
    Remove-Item $WSUSRegPath -Recurse
    Write-Output "WindowsUpdate/WSUS registry settings cleaned."

    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Running") {
        Write-Output "The Windows Update service has been started."
        break
        } ELSE {
        Write-Output "Starting the Windows Update service..."
        Start-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Running")
} ELSE {
    Write-Output "WindowsUpdate/WSUS registry settings do not exist."
}
#############################################################################


########## Importing the PSWindowsUpdate Module into the session ############
Write-Output ""
Write-Output "Importing $Module module into powershell session..."
TRY {
    Import-Module $Module
    Write-Output "$Module module has been imported into powershell session."
    }
CATCH {
    THROW "Unable to load $Module Module into powershell session $($_.Exception.Message)"
    }
#############################################################################


########## Checking for Available Microsoft Updates #########################
Write-Output ""
Write-Output "Retrieving available Microsoft Updates..."
TRY {
    $GWU = Get-WindowsUpdate -MicrosoftUpdate -Verbose
    }
CATCH {
    THROW "$Module failed to retrieve available Microsoft Updates $($_.Exception.Message)"
    }
#############################################################################


########## Return Results for Available Microsoft Updates ###################
Write-Output "-----------------------------------------------------------------------------"
IF ($null -ne $GWU) {
    $GWU | ForEach-Object {
        [PSCustomObject]@{
            "KB"                              = $_.KB
            "Title"                           = $_.Title
            "Size"                            = $_.Size
            "Type"                            = switch ($_.Type) {
                1 { "Software" }
                2 { "Driver" }
                Default { "Unknown" } 
            }
            "SupportUrl"                      = $_.SupportUrl
            "Description"                     = $_.Description
        }
        Write-Output "-----------------------------------------------------------------------------"
    }
}
ELSE {
    [PSCustomObject]@{
        "KB"                              = "No Applicable Updates"
        "Title"                           = $null
        "Size"                            = $null
        "Type"                            = $null
        "SupportUrl"                      = $null
        "Description"                     = $null
    }
    Write-Output "-----------------------------------------------------------------------------"
}
#############################################################################


########## Install Selected Microsoft Updates ###############################
Write-Output ""
Write-Output "Installing Selected Microsoft Updates..."
Write-Output "-----------------------------------------------------------------------------"
TRY {
    Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -Verbose -IgnoreReboot -KBArticleID $KBArticleID
}
CATCH {
    THROW "$Module failed to Install Selected Microsoft Update(s) $($_.Exception.Message)"
}
Write-Output "-----------------------------------------------------------------------------"
#############################################################################


########## Restore Windows/WSUS configurations ##############################
Write-Output ""
Write-Output "Checking if Windows/WSUS registry settings file exists..."
IF(Test-Path $WSUSBackupFile -PathType Leaf){
    Write-Output "Windows/WSUS registry settings file exists."
    
    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Stopped") {
        Write-Output "The Windows Update service has been stopped."
        break
        } ELSE {
        Write-Output "Stopping the Windows Update service..."
        Stop-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Stopped")

    Write-Output ""
    TRY {
        Write-Output "Restoring Windows/WSUS settings to the registry..."
        Write-Output "From: $WSUSBackupFile"
        Write-Output "To: $WSUSRegPath2"
        $Process = Start-Process reg -ArgumentList "import $WSUSBackupFile" -PassThru -Wait
        Write-Output "WindowsUpdate/WSUS registry settings restored."
    } CATCH {
        THROW "An error occurred importing the WindowsUpdate/WSUS registry settings to $WSUSRegPath2 $($_.Exception.Message)"
    }

    Write-Output ""
    DO {
    $service = Get-Service -Name wuauserv
        IF ($service.Status -eq "Running") {
        Write-Output "The Windows Update service has been started."
        break
        } ELSE {
        Write-Output "Starting the Windows Update service..."
        Start-Service -Name wuauserv
        Start-Sleep -Seconds 5
        }
    } UNTIL ($service.Status -eq "Running")
} ELSE {
    Write-Output "Windows/WSUS registry settings file does not exist."
}
#############################################################################


########## Wrapping Up ######################################################
Write-Output ""
$TimeStampEnd = Get-Date -Format "G"
Write-Output "Script finished: $TimeStampEnd"
$StopWatch.Stop()
$TotalTime = $StopWatch.Elapsed.TotalSeconds
$TotalTime = [Math]::Round($TotalTime, 2)
Write-Output "Total script run time: $TotalTime(sec)"
#############################################################################
Was this article helpful?
Still have a question or want to share what you have learned? Visit our Community Discord to get help and collaborate with others.