UnattendResources/WindowsUpdates/WindowsUpdates/WindowsUpdates.psm1
# Copyright 2016 Cloudbase Solutions Srl # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. $ErrorActionPreference = "Stop" $UPDATE_SESSION_COM_CLASS = "Microsoft.Update.Session" $UPDATE_SYSTEM_INFO_COM_CLASS = "Microsoft.Update.SystemInfo" $UPDATE_COLL_COM_CLASS = "Microsoft.Update.UpdateColl" $SERVER_SELECTION_WINDOWS_UPDATE = 2 $UPDATE_DOWNLOAD_STATUS_CODES = @{ 0 = "NotStarted" 1 = "InProgress" 2 = "Downloaded" 3 = "DownloadedWithErrors" 4 = "Failed" 5 = "Aborted" } $UPDATE_INSTALL_STATUS_CODES = @{ 0 = "NotStarted" 1 = "InProgress" 2 = "Installed" 3 = "InstalledWithErrors" 4 = "Failed" 5 = "Aborted" } function Write-UpdateInformation { Param( [Parameter(Mandatory=$true)] $Updates ) foreach ($update in $Updates) { Write-Host ("Update title: " + $update.Title) Write-Host ($update.Categories | Select-Object Name) Write-Host ("Update size: " + ([int]($update.MaxDownloadSize/1MB) + 1) + "MB") Write-Host "" } } function Get-UpdateSearcher { $updateSession = New-Object -ComObject $UPDATE_SESSION_COM_CLASS return $updateSession.CreateUpdateSearcher() } function Get-UpdateDownloader { $updateSession = New-Object -ComObject $UPDATE_SESSION_COM_CLASS return $updateSession.CreateUpdateDownloader() } function Get-LocalUpdates { Param( [Parameter(Mandatory=$true)] $UpdateSearcher, [Parameter(Mandatory=$true)] [string]$SearchCriteria ) try { $updatesResult = $updateSearcher.Search($searchCriteria) } catch [Exception]{ Write-Host "Failed to search for updates" throw } return $updatesResult } function Add-WindowsUpdateToCollection { Param( [Parameter(Mandatory=$true)] $Collection, [Parameter(Mandatory=$true)] $Update ) $Collection.Add($Update) | Out-Null } function Get-WindowsUpdate { <# .SYNOPSIS Get-WindowsUpdate is a command that will return the applicable updates to the Windows operating system. #> [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [array]$ExcludeKBId=@() ) PROCESS { $updateSearcher = Get-UpdateSearcher # Set the update source server to Windows Update $updateSearcher.ServerSelection = $SERVER_SELECTION_WINDOWS_UPDATE # Set search criteria $searchCriteria = "( IsInstalled = 0 and IsHidden = 0)" $updateResult = Get-LocalUpdates -UpdateSearcher $updateSearcher ` -SearchCriteria $searchCriteria if (!$updateResult -or !$updateResult.Updates) { return } $updates = $updateResult.Updates $filteredUpdates = New-Object -ComObject $UPDATE_COLL_COM_CLASS for ($i=0; $i -lt $updates.Count; $i++) { $update = $updates.Item($i) $updateKBId = ($update.KBArticleIDs -join ", KB") if ($ExcludeKBId -contains ("KB" + $updateKBId)) { Write-Verbose ("Exclude update KB{0}" ` -f @($updateKBId)) } else { Add-WindowsUpdateToCollection $filteredUpdates $update } } return $filteredUpdates } } function Get-RegKeyRebootRequired { $basePath = "HKLM:\\SOFTWARE\Microsoft\Windows\CurrentVersion\" $cbsRebootRequired = Get-Item -Path "${basePath}Component Based Servicing\RebootPending" ` -ErrorAction SilentlyContinue $auRebootRequired = Get-Item -Path "${basePath}\WindowsUpdate\Auto Update\RebootRequired" ` -ErrorAction SilentlyContinue return ($cbsRebootRequired -and $auRebootRequired) } function Get-UpdateRebootRequired { $systemInfo = New-Object -ComObject $UPDATE_SYSTEM_INFO_COM_CLASS return $systemInfo.RebootRequired } function Get-RebootRequired { <# .SYNOPSIS Get-RebootRequired is a command that will return the reboot required status of a Windows machine. This status check is necessary in order to know whether to perform a machine restart in order to continue to install the Windows updates. #> return ((Get-UpdateRebootRequired) -or (Get-RegKeyRebootRequired)) } function Install-WindowsUpdate { <# .SYNOPSIS Install-WindowsUpdate is a command that will install the updates given as a parameter on the Windows operating system. .Parameter Updates The required value can be obtained by running Get-WindowsUpdate #> [CmdletBinding()] Param( [Parameter( Position=0, Mandatory=$true, ValueFromPipeline=$true)] $Updates ) PROCESS { foreach ($update in $Updates) { $updateSize = ([int]($update.MaxDownloadSize/1MB) + 1) Write-Host ("Installing Update: {0} ({1}MB)" -f @($update.Title, $updateSize)) if ($update.EulaAccepted -eq 0) { Write-Host ("AcceptEula for Update: " + $update.Title) $update.AcceptEula() } $updateSession = New-Object -ComObject $UPDATE_SESSION_COM_CLASS $updateColl = New-Object -ComObject $UPDATE_COLL_COM_CLASS $updateColl.add($update) | Out-Null # DOWNLOAD UPDATE $updateDownloader = $updateSession.CreateUpdateDownloader() $updateDownloader.Updates = $updateColl $maxRetries = 5 $retries = 0 while ($retries -lt $maxRetries) { $downloadResult = $updateDownloader.Download() if ($downloadResult.ResultCode -ne 2) { $retries++ Write-Host "Failed to download update. Reason: " + ` $UPDATE_DOWNLOAD_STATUS_CODES[$downloadResult.ResultCode] } else { Write-Host "Update has been downloaded." break } } if ($retries -eq $maxRetries) { write-host "$retries" throw "Failed to download update." } # INSTALL UPDATE $updateInstaller = $updateSession.CreateUpdateInstaller() $updateInstaller.Updates = $updateColl $maxRetries = 5 $retries = 0 while ($retries -lt $maxRetries) { $installResult = $updateInstaller.Install() if ($installResult.ResultCode -ne 2) { $retries++ Write-Host "Failed to install update. Reason: " + ` $UPDATE_INSTALL_STATUS_CODES[$installResult.ResultCode] } else { Write-Host "Update has been installed." break } } if ($retries -eq $maxRetries) { write-host "$retries" throw "Failed to install update." } $updateColl.clear() } } } Export-ModuleMember -Function * -Alias * |