#Requires –Version 3 #Get WindowsUpdate List for offline patch function Get-WindowsUpdateFileList { param( [Parameter(Mandatory=$True)] [string] $Filter ) $objSession = New-Object -ComObject "Microsoft.Update.Session" $objSearcher = $objSession.CreateUpdateSearcher() $results = $objSearcher.Search($Filter) $downloadList =@() foreach($update in $results.Updates) { $title = $update.Title $date = $update.LastDeploymentChangeTime $isInstalled = $update.IsInstalled $rebootRequired = $update.RebootRequired foreach($bundledUpdate in $update.BundledUpdates) { foreach($content in $bundledUpdate.DownloadContents) { #if($url.Contains("-express_") -or $url.Contains("-delta_") -or $url.EndsWith(".psf")) if($content.IsDeltaCompressedContent) { #Skip express package(Delta Compressed Package) and .psf file continue } #Append to download list $url = $content.DownloadURL $fileName = [IO.Path]::GetFileName($url) $downloadList += [pscustomobject]@{Title=$title;LastDeploymentChangeTime=$date ;DownloadURL=$url;IsInstalled=$isInstalled;RebootRequired=$rebootRequired;FileName=$fileName} } } } return $downloadList } function Start-DownloadWindowsUpdateFile { [Cmdletbinding()] param( [string] $TargetComputer, $Credential, [string] $DownloadFolder, [string] $WindowsUpdateFilter ) if(!(Test-Path $DownloadFolder)) { Write-Error "Destination folder not found!" } if($TargetComputer -eq $env:ComputerName) { Write-Verbose ("Search WindowsUpdate for local computer...") $results = Get-WindowsUpdateFileList $WindowsUpdateFilter } else { Write-Verbose ("Search WindowsUpdate for remote computer({0})..." -f $TargetComputer) $results = Invoke-Command -Computer $TargetComputer -Credential $Credential -ScriptBlock ${function:Get-WindowsUpdateFileList} -ArgumentList $WindowsUpdateFilter } Write-Verbose ("WindowsUpdate check completed: {0} files found" -f $results.Count) #$results | select Title,FileName, LastDeploymentChangeTime | Format-List #Start Download Files Write-Verbose ("Download files started...") foreach($update in $results) { $fileName = [IO.Path]::GetFileName($update.DownloadURL) $filePath = Join-Path $DownloadFolder $fileName if(Test-Path $filePath) { Write-Verbose "`tSkip already exists patch: $fileName" continue } Write-Verbose "`tStart download patch: $fileName" $params = @{ Source = $update.DownloadURL DisplayName = $update.Title Description = $update.DownloadURL Destination = $DownloadFolder } Start-BitsTransfer @params } Write-Verbose ("Download files completed...") }