Skip to content

Instantly share code, notes, and snippets.

@Smalls1652
Last active December 28, 2021 00:35
Show Gist options
  • Save Smalls1652/855e6a0894d31dc1fd09271a924640e0 to your computer and use it in GitHub Desktop.
Save Smalls1652/855e6a0894d31dc1fd09271a924640e0 to your computer and use it in GitHub Desktop.
<#PSScriptInfo
.VERSION 2021.12.0
.GUID 9332350c-f337-4184-a850-a5c9367aac55
.AUTHOR Tim Small
.COMPANYNAME Smalls.Online
.COPYRIGHT 2021
.TAGS
.LICENSEURI
.PROJECTURI
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
.PRIVATEDATA
#>
<#
.SYNOPSIS
WSUS Update Catalog Downloader
.DESCRIPTION
Download the latest copy of the WSUS update catalog.
.PARAMETER OutDir
The directory to save the 'wsusscn2.cab' file to.
.PARAMETER ForceDownload
Force the download of the 'wsussc2.cab' file.
.EXAMPLE
PS C:\> Invoke-DownloadWsusCab.ps1 -OutDir ".\wsus\"
Downloads 'wsusscn2.cab' to a specified output directory.
.EXAMPLE
PS C:\> Invoke-DownloadWsusCab.ps1
Downloads 'wsusscn2.cab' to the current location.
.EXAMPLE
PS C:\> Invoke-DownloadWsusCab.ps1 -OutDir ".\wsus\" -ForceDownload
Downloads 'wsusscn2.cab' even if the current locally downloaded copy is up-to-date.
#>
#Requires -Version 7.0.0
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
param(
[Parameter(Position = 0)]
[ValidateNotNullOrEmpty()]
[string]$OutDir = ".",
[Parameter(Position = 1)]
[switch]$ForceDownload
)
$resolvedOutPath = (Resolve-Path -Path $OutDir -ErrorAction "Stop").Path
$outPathItem = Get-Item -Path $resolvedOutPath
if ([System.IO.FileAttributes]::Directory -notin $outPathItem.Attributes) {
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
[System.IO.IOException]::new("The supplied output path is not a directory."),
"OutputPathNotDirectory",
[System.Management.Automation.ErrorCategory]::InvalidArgument,
$outPathItem
)
)
}
$cabFileOutPath = Join-Path -Path $resolvedOutPath -ChildPath "wsusscn2.cab"
$cabFileLastUpdatedFilePath = Join-Path -Path $resolvedOutPath -ChildPath "wsussc2.cab.last-modified"
$cabShortLinkUri = "https://go.microsoft.com/fwlink/p/?linkid=74689"
# Create the HttpClient object.
$httpClient = [System.Net.Http.HttpClient]::new()
# Get the current cab file location from the quick link
$shortLinkRedirectReqMsg = [System.Net.Http.HttpRequestMessage]::new(
[System.Net.Http.HttpMethod]::Head,
$cabShortLinkUri
)
$shortLinkRedirectRsp = $httpClient.SendAsync($shortLinkRedirectReqMsg).GetAwaiter().GetResult()
$cabDownloadUri = $shortLinkRedirectRsp.Headers.Location
Write-Verbose "Current CAB file is located at: $($cabDownloadUri)"
$shortLinkRedirectRsp.Dispose()
$shortLinkRedirectReqMsg.Dispose()
# Get info about the latest 'wsusscn2.cab' file
Write-Verbose "Getting latest CAB file info."
$currentCabHeadReqMsg = [System.Net.Http.HttpRequestMessage]::new(
[System.Net.Http.HttpMethod]::Head,
$cabDownloadUri
)
$currentCabHeadRsp = $httpClient.SendAsync($currentCabHeadReqMsg).GetAwaiter().GetResult()
$currentCabModifiedDateTime = $currentCabHeadRsp.Content.Headers.LastModified
$currentCabHeadRsp.Dispose()
$currentCabHeadReqMsg.Dispose()
#Dispose the HttpClient. It's not needed after this point.
$httpClient.Dispose()
# Process if the CAB file needs to be updated.
$shouldDownload = $false
# If the script was set to 'Force', then download.
if ($ForceDownload -eq $true) {
$shouldDownload = $true
}
# If the CAB file doesn't already exist locally, then download.
if (((Test-Path -Path $cabFileOutPath) -eq $false) -and ($shouldDownload -eq $false)) {
$shouldDownload = $true
}
# If the last updated text file exists and 'shouldDownload' is set to false, then check to see
# if the locally downloaded copy is older than the currently available copy.
if (((Test-Path -Path $cabFileLastUpdatedFilePath) -eq $true) -and ($shouldDownload -eq $false)) {
$cabFileLastUpdatedDateTime = [System.DateTimeOffset]::FromUnixTimeMilliseconds((Get-Content -Path $cabFileLastUpdatedFilePath -Raw))
# If the currently available copy's last modified time is not equal to the locally downloaded last updated time, then download.
if (($currentCabModifiedDateTime -eq $cabFileLastUpdatedDateTime) -eq $false) {
$shouldDownload = $true
}
else {
Write-Warning "The locally downloaded 'wsusscn2.cab' file is already the latest available copy."
Write-Warning "If you want to disregard this check, add the '-ForceDownload' parameter."
}
}
else {
$shouldDownload = $true
}
# If 'shouldDownload' is set to true, then start the download process.
if ($shouldDownload -eq $true) {
Write-Verbose "Latest CAB file will be downloaded."
Write-Verbose "Output path will be here: $($cabFileOutPath)"
# Check to see if the CAB file already exists locally and prompt to delete if it does.
if ((Test-Path -Path $cabFileOutPath) -eq $true) {
if ($PSCmdlet.ShouldProcess($cabFileOutPath, "Delete existing file")) {
Remove-Item -Path $cabFileOutPath -Force -Recurse -Confirm:$false
}
else {
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
[System.Exception]::new("User cancelled operation."),
"UserCancelledOperation",
[System.Management.Automation.ErrorCategory]::OperationStopped,
$cabFileOutPath
)
)
}
}
Write-Warning "Downloading 'wsusscn2.cab'. This may take a bit."
$ProgressPreference = "SilentlyContinue"
Invoke-WebRequest -TimeoutSec (New-TimeSpan -Minutes 5).Seconds -Method "Get" -Uri $cabDownloadUri -OutFile $cabFileOutPath -Verbose:$false
$ProgressPreference = "Continue"
# Write the last modified time to the 'wsusscn2.cab.last-modified' file.
# It's saved as milliseconds in Unix time.
$currentCabModifiedDateTime.ToUnixTimeMilliseconds() | Out-File -FilePath $cabFileLastUpdatedFilePath -Force
# Write the downloaded file item to the output.
$downloadedOutFile = Get-Item -Path $cabFileOutPath
Write-Output -InputObject $downloadedOutFile
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment