Public/Save-LatestUpdate.ps1
Function Save-LatestUpdate { <# .SYNOPSIS Downloads the updates passed from other LatestUpdate Get functions. .DESCRIPTION Downloads the updates passed from other LatestUpdate Get functions to a local folder. Use functions such as Get-LatestCumulativeUpdate and Get-LatestMonthlyRollup to retrieve the list of updates to then pass to Save-LatestUpdate to download each update locally. Do one or more of the following with the downloaded updates: - Import the update into an MDT share to speed deployment of Windows (new PCs, reference images etc.) - Apply the update to an offline WIM using DISM - Deploy the update with ConfigMgr (if not using WSUS) .PARAMETER Updates Specifies the list of updates from other LatestUpdate functions such as Get-LatestCumulativeUpdate and Get-LatestMonthlyRollup. .PARAMETER Path Specifies a path as the destination for the downloaded updates. All updates passed in -Updates will be downloaded to this path. .PARAMETER ForceWebRequest Forces the use of Invoke-WebRequest instead of Start-BitsTransfer when running under Windows PowerShell. .PARAMETER Priority Specifies the priority of a BITS transfer job. Defaults to Foreground. Foreground will enforced when proxy credentials are passed to Save-LatestUpdate .PARAMETER Proxy Specifies a proxy server address to use when initiating a download. .PARAMETER ProxyCredential Specifies a [System.Management.Automation.PSCredential] object to use when the proxy server requires authentication. .PARAMETER Force Specifies that existing downloaded updates will be re-downloaded and overwritten. .EXAMPLE PS C:\> $Updates = Get-LatestCumulativeUpdate PS C:\> Save-LatestUpdate -Updates $Updates -Path C:\Temp\Updates This commands reads the the Windows 10 update history feed and returns an object that lists the most recent Windows 10 Cumulative Updates. Save-LatestUpdate will download each returned update to C:\Temp\Updates. .EXAMPLE PS C:\> Get-LatestServicingStackUpdate | Save-LatestUpdate This commands reads the the Windows 10 update history feed and returns an object that lists the most recent Windows 10 Servicing Stack Updates. The output is then passed to Save-LatestUpdate and each update is downloaded to the current directory. #> [OutputType([System.Management.Automation.PSObject])] [CmdletBinding(SupportsShouldProcess = $True, HelpUri = "https://docs.stealthpuppy.com/docs/latestupdate/usage/download")] Param( [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline)] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSObject] $Updates, [Parameter(Mandatory = $False, Position = 1, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [ValidateScript( { If (Test-Path -Path $_ -PathType 'Container') { Return $true } Else { Throw "Cannot find path $_" } })] [System.String] $Path = $PWD, [Parameter(Mandatory = $False)] [System.Management.Automation.SwitchParameter] $ForceWebRequest, [Parameter(Mandatory = $False)] [ValidateSet('Foreground', 'High', 'Normal', 'Low')] [System.String] $Priority = "Foreground", [Parameter(Mandatory = $False)] [System.String] $Proxy, [Parameter(Mandatory = $False)] [System.Management.Automation.PSCredential] $ProxyCredential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $False)] [System.Management.Automation.SwitchParameter] $Force ) # Step through each update in $Updates ForEach ($update in $Updates) { # Manage updates with multiple URLs ForEach ($url in $update.URL) { # Create the target file path where the update will be saved $updateDownloadTarget = Join-Path -Path $Path -ChildPath (Split-Path -Path $url -Leaf) # If the update is not already downloaded, download it. If ((Test-Path -Path $updateDownloadTarget) -and (-not $Force.IsPresent)) { Write-Verbose -Message "File exists: $updateDownloadTarget. Skipping download." } Else { If ($ForceWebRequest -or (Test-PSCore)) { If ($pscmdlet.ShouldProcess($url, "WebDownload")) { #Running on PowerShell Core or ForceWebRequest try { $iwrParams = @{ Uri = $url OutFile = $updateDownloadTarget UseBasicParsing = $True ErrorAction = $script:resourceStrings.Preferences.ErrorAction } If ($PSBoundParameters.ContainsKey('Proxy')) { $iwrParams.Proxy = $Proxy } If ($PSBoundParameters.ContainsKey('ProxyCredential')) { $iwrParams.ProxyCredentials = $ProxyCredential } Invoke-WebRequest @iwrParams } catch [System.Net.Http.HttpRequestException] { Write-Warning -Message "$($MyInvocation.MyCommand): HttpRequestException: Check URL is valid: $url." Write-Warning -Message ([string]::Format("Error : {0}", $_.Exception.Message)) } catch [System.Net.WebException] { Write-Warning -Message "$($MyInvocation.MyCommand): WebException." Write-Warning -Message ([string]::Format("Error : {0}", $_.Exception.Message)) } catch [System.Exception] { Write-Warning -Message "$($MyInvocation.MyCommand): Failed to download: $url." Throw $_.Exception.Message } } } Else { If ($pscmdlet.ShouldProcess($(Split-Path -Path $url -Leaf), "BitsDownload")) { #Running on Windows PowerShell try { $sbtParams = @{ Source = $url Destination = $updateDownloadTarget Priority = $Priority DisplayName = $update.Note Description = "Downloading $url" ErrorAction = $script:resourceStrings.Preferences.ErrorAction } If ($PSBoundParameters.ContainsKey('Proxy')) { # Set priority to Foreground because the proxy will remove the Range protocol header $sbtParams.Priority = "Foreground" $sbtParams.ProxyUsage = "Override" $sbtParams.ProxyList = $Proxy } If ($PSBoundParameters.ContainsKey('ProxyCredential')) { $sbtParams.ProxyCredential = $ProxyCredentials } Start-BitsTransfer @sbtParams } catch [System.Exception] { Write-Warning -Message "$($MyInvocation.MyCommand): Exception: check URL is valid: $url." Throw $_.Exception.Message } } } If (Test-Path -Path $updateDownloadTarget) { $outputObject = [PSCustomObject] @{ KB = $update.KB Note = $update.Note Path = (Resolve-Path -Path $updateDownloadTarget) } Write-Output -InputObject $outputObject } Else { Write-Warning -Message "$($MyInvocation.MyCommand): failed to download [$updateDownloadTarget]." } } } } } |