Skip to content

Instantly share code, notes, and snippets.

@bklockwood
Last active November 6, 2017 19:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bklockwood/da0ca606e6c0f2bfc34f to your computer and use it in GitHub Desktop.
Save bklockwood/da0ca606e6c0f2bfc34f to your computer and use it in GitHub Desktop.
Simplified PSWU
#requires -version 2.0
function Get-UpdateList {
<#
.Synopsis
Gets list of updates from Windows Update.
.PARAMETER Criteria
The search criteria, see http://goo.gl/7nZSPs
Left at default, it will return all updates that have not yet
been installed, whether software or driver. Including Hidden
updates.
.NOTES
Returns an ISearchResult object (http://goo.gl/pvnUSM) named $ISearchResult
ISearchresult type - System.__ComObject#{d40cff62-e08c-4498-941a-01e25f0fd33c}
$ISearchResult.Updates contains an IUpdateCollection - http://goo.gl/8C2dbb
WU error codes: http://goo.gl/cSWDY8
.EXAMPLE
Get-UpdateList | ft -AutoSize
ResultCode RootCategories Updates Warnings
---------- -------------- ------- --------
2 System.__ComObject System.__ComObject System.__ComObject
.EXAMPLE
(Get-UpdateList).Updates.Count
40
Shows that there are 40 updates available.
.EXAMPLE
(Get-UpdateList).Updates | select maxdownloadsize, title | ft -AutoSize
MaxDownloadSize Title
--------------- -----
10123467 Update for Windows Server 2012 R2 (KB2884846)
948931 Security Update for Windows Server 2012 R2 (KB2876331)
517819 Security Update for Windows Server 2012 R2 (KB2892074)
376647 Update for Windows Server 2012 R2 (KB2917993)
#>
[CmdletBinding()]
Param
([Parameter(Mandatory=$false, ValueFromPipeline=$false, Position=0)] $Criteria = "IsInstalled=0")
try {
$Searcher = New-Object -ComObject Microsoft.Update.Searcher
$ISearchResult = $Searcher.Search($Criteria)
$ISearchResult
} catch {
Write-Error "ERROR in Get-UpdateList"
return $_
}
}
Function Show-UpdateList {
<#
.Synopsis
Print a nice table of Updates not installed with some attribute info.
.Description
Columns:
THDRE
|||||- "E" if EULA accepted, "-" if not
||||-- "R" if reboot required, "-" if not (frequently wrong!)
|||--- "D" if the update has been downloaded, "-" if not
||---- "H" if the update is hiden, "-" if not
|----- "S" if software, "D" if driver
.TODO
They don't sort properly;
#>
[Cmdletbinding()]
Param([Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]$ISearchResult)
if ($ISearchResult.pstypenames -notcontains 'System.__ComObject#{d40cff62-e08c-4498-941a-01e25f0fd33c}') {
Write-Error "$ISearchResult is not an ISearchResult object (http://goo.gl/pvnUSM)"
break
}
Write-Output "$($ISearchResult.Updates.Count) updates available:"
$ISearchResult.Updates |
Select @{n='KB';e={$_.KbArticleIds}},
#Update type 1 is software, type 2 is driver. http://goo.gl/VvV7tt
@{n='T';e={if ($_.Type -eq 1) {"S"} ElseIf ($_.Type -eq 2) {"D"}}},
@{n='H';e={if ($_.isHidden) {"H"} Else {"-"}}},
@{n='D';e={if ($_.isDownloaded) {"D"} Else {"-"}}},
@{n='R';e={if ($_.Rebootrequired) {"R"} Else {"-"}}},
@{n='E';e={if ($_.EulaAccepted) {"E"} Else {"-"}}},
@{n='MB';e={'{0:N0}' -f ($_.MaxDownloadSize/1MB)}},
@{n='Severity';e={$_.MsrcSeverity}},
@{n='Published';e={$_.LastDeploymentChangeTime.ToShortDateString()}},
#@{n='UID';e={$_.Identity.UpdateID}},
#truncate title to 40 chars
@{n='Title';e={ if ($($_.Title.Length) -lt 40) {$_.Title} else {$($_.Title.Substring(0,37)) + "..."}}} |
Sort -Property $_.LastDeploymentChangeTime | ft -AutoSize |out-string
}
function Install-Update {
<#
.SYNOPSIS
Downloads and installs updates
.NOTES
Uses IUpdateDownloader http://goo.gl/hPK49j
and IUpdateInstaller http://goo.gl/jeDijU
WU error codes: http://goo.gl/cSWDY8
#>
[CmdletBinding()]
Param (
[parameter(Mandatory=$true, ValueFromPipeline=$true)]$ISearchResult,
[parameter(Mandatory=$false, ValueFromPipeline=$true)][switch]$OneByOne
)
if ($ISearchResult.pstypenames -notcontains 'System.__ComObject#{d40cff62-e08c-4498-941a-01e25f0fd33c}') {
Write-Error "$ISearchResult is not an ISearchResult object (http://goo.gl/pvnUSM)"
break
}
$DesiredUpdates = New-Object -ComObject Microsoft.Update.UpdateColl
$counter = 0
foreach ($u in $ISearchResult.Updates) {
$u.AcceptEula()
if (!$($u.IsHidden) -and !$($u.Rebootrequired)) {
$counter++
$DesiredUpdates.Add($u) |out-null
}
#Used for debugging. One update at a time.
if ($OneByOne) {
if ($counter -gt 1) {break}
}
}
If ($DesiredUpdates.Count -lt 1) {
Write-Verbose "No updates to install!"
} else {
Write-Verbose "Downloading and installing $($DesiredUpdates.Count) updates"
$Downloader = New-Object -ComObject Microsoft.Update.Downloader
$Downloader.Updates = $DesiredUpdates
$DownloadResult = $Downloader.Download()
#Resultcode 2-success, 3-success with errors.
#Using -contains instead of -in for PS v2 compat
if (2,3 -notcontains $DownloadResult.ResultCode) {
Write-Error "Downloader error HResult $($DownloadResult.HResult), resultcode $($DownloadResult.ResultCode)"
} else {
if ($DownloadResult.ResultCode -eq 3) {Write-Verbose "Downloaded with errors; beginning install."}
if ($DownloadResult.ResultCode -eq 2) {Write-Verbose "Downloaded successfully; beginning install."}
$Installer = New-Object -ComObject Microsoft.Update.Installer
$Installer.Updates = $DesiredUpdates
$InstallResult = $Installer.Install()
switch ($InstallResult.ResultCode) {
2 {Write-Verbose "Installed updates successfully."}
3 {Write-Verbose "Installed updates swith errors."}
default {Write-Error "Installer error $($InstallResult.HResult),resultcode $($InstallResult.ResultCode)"}
}
}
}
}
Get-UpdateList -Criteria "IsInstalled=0 AND RebootRequired=0 AND BrowseOnly=0 AND Type='Software'" | Install-Update
@bklockwood
Copy link
Author

this version of PSWU only downloads and installs those updates which claim they won't need a reboot.

see https://goo.gl/7nZSPs for better understanding of the criteria on last line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment