Print Detailed Windows Update Information

Mark Berry November 12, 2015

Earlier this year, I wrote about how to show and change Windows Update settings on a machine using PowerShell. But sometimes you need to know more about a Windows update than you can see in those scripts or in the Windows user interface, for example the UpdateID. This PowerShell script will print key information about one or more udpates. Run it from a Remote Management tool or directly on the machine you are investigating.

Provide as the first parameter a list of one or more numeric KB IDs that you want to view, separated by commas without spaces. For example:

.\WindowsUpdate.ShowDetails 2952664,2976978,3035583
.\WindowsUpdate.ShowDetails 3097877

Update 5 August 2016

The script now allows specifying updates by their 36-character UpdateID as well as by the KB number. Do not include the {curly brackets} when specifying an UpdateID. For example:

.\WindowsUpdate.ShowDetails a720f788-2503-42a9-88ad-68feffee6818

Sample output:

WindowsUpdateShowDetails

Save the script as .\WindowsUpdate.ShowDetails.ps1, for example:

<#
.Synopsis
  Print details about one or more updates identified by KB number.

  Copyright (c) 2016 by MCB Systems. All rights reserved.
  Free for personal or commercial use.  May not be sold.
  No warranties.  Use at your own risk.

.Notes 
    Name:       MCB.WindowsUpdate.ShowDetails.ps1
    Author:     Mark Berry, MCB Systems
    Created:    11/12/2015
    Last Edit:  08/05/2016

    Changes:
    
    11/20/2015  Add AutoSelection and AutoDownload fields.
    
    08/05/2016  Allow searching by 36-character UpdateID as well as by KB number.
                Do not include the {curly brackets} when specifying an UpdateID.
#>

param(
  # This parameter takes an array of KB Numbers or UpdateIDs.  Supply the numbers
  # separated by commas but no spaces, e.g. 2952664,2976978,3035583,F9164872-3E27-4410-B4FC-038814549BB1.
  [Parameter(Mandatory = $true,
                    Position = 0,
                    ValueFromPipelineByPropertyName = $true)]
  [String[]]$UpdateNumbers="",

  # The following $LogFile parameter is required by LogigNow Remote Management
  # and can be omitted when running the script directly.
  [Parameter(Mandatory = $false,
                    Position = 1,
                    ValueFromPipelineByPropertyName = $true)]
  [String]$LogFile=""
)

[Boolean]$ErrFound = $false
$ComputerName = $env:COMPUTERNAME

Foreach ($UpdateNumber in $UpdateNumbers) {
  if ($UpdateNumber.length -ne 36) {
  # If it's not a 36-character UpdateID, check if $UpdateNumber is numeric
    try {
      0 + $UpdateNumber | Out-Null 
    }
    catch{ 
      "Parameter error:  '$UpdateNumber' is not allowed. Specify one or more KBNumbers or 36-character UpdateIDs separated"
      "by commas but no spaces. Do not precede KBNumbers with 'KB' and do not enclose UpdateIDs in {curly brackets}."
      ""
      "Script execution failed"
      $ExitCode = 1001 # Cause script to report failure in GFI dashboard
      ""
      "Local Machine Time:  " + (Get-Date -Format G)
      "Exit Code: " + $ExitCode
      Exit $ExitCode
    } 
  }
}

"Computer Name: " + $ComputerName
""
"-------------------------------------------------------------------------------"
"Step 1:  Check whether update(s) are installed."
"         This section only shows updates for which you specifed a KBNumber."
"         Updates for which you provided an UpdateID cannot be listed here."
"-------------------------------------------------------------------------------"

#---------------------------------------------------------------------------------
# Check if update installed
# Adapted from http://techibee.com/powershell/powershell-uninstall-windows-hotfixesupdates/1084
#---------------------------------------------------------------------------------
# Note that the list returned by wmic includes the "KB" prefix but wusa wants the number only

$hotfixes = Get-WmiObject -ComputerName $ComputerName -Class Win32_QuickFixEngineering | select hotfixid            

Foreach ($UpdateNumber in $UpdateNumbers) {
  if ($UpdateNumber.length -eq 36) {
    Write-Host "Skipping $UpdateNumber specified as UpdateID."
  } else {
    $KBID = "KB"+$UpdateNumber
    if($hotfixes -match $KBID) {
      Write-host "Update $UpdateNumber is installed."
    } else {            
      Write-Host "Update $UpdateNumber is not installed."
    }            
  }
} # Foreach $UpdateNumber

#---------------------------------------------------------------------------------
# Print update status per Windows Update
# Adapted from http://superuser.com/a/922921/171670.
#---------------------------------------------------------------------------------
""
"-------------------------------------------------------------------------------"
"Step 2:  Print status of update(s)"
"-------------------------------------------------------------------------------"
"Searching online for updates applicable to this machine, whether installed or "
"not, including superseded updates.  This may take some time--please wait..."

try {
  #Get all updates in $SearchResult.
  $UpdateSession = New-Object -ComObject Microsoft.Update.Session
  $UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
  # If the update has been re-released, it may supersede a previous update.  
  $UpdateSearcher.IncludePotentiallySupersededUpdates = $true
  # Available search criteria:  https://msdn.microsoft.com/en-us/library/windows/desktop/aa386526%28v=vs.85%29.aspx
  $SearchResult = $UpdateSearcher.Search("IsInstalled=0 or IsInstalled=1") # include whether installed or not
  "Update list download complete."
  
  # For debugging:  select one update by searching for string in title
  # $Update = $SearchResult.updates | Where-Object{$_.Title -match "3097877"}
  
  # We use Foreach below so we can search through (potentially multiple) KBArticleIDs per update
  Foreach ($UpdateNumber in $UpdateNumbers) {
    [Boolean]$UpdateListed = $false # refers to the $UpdateNumber from the parameter list
    ""
    "Checking for update $UpdateNumber"
    "--------------------------------------------------------"
    Foreach ($Update in $SearchResult.updates) {
      [Boolean]$ShowUpdate = $false # refers to the $Update from the search result
    
      if ($Update.Identity.UpdateID -eq $UpdateNumber) {
        $ShowUpdate = $true # We want to show details on this specific update from the search result
      }
      
      Foreach ($KBArticleID in $Update.KBArticleIDs) {
        # Next line is for debugging
        # Write-Host "$KBArticleID, $($Update.IsHidden), $($Update.title)"
        if ($KBArticleID -eq $UpdateNumber) {
          $ShowUpdate = $true # We want to show details on this specific update from the search result
        } # if $KBArticleID -eq $UpdateNumber
      } # Foreach $KBArticleID
        
      if ($ShowUpdate) {
        $UpdateListed = $true # We listed details for at least one $Update matching $UpdateNumber
        Write-Host ""
        Write-Host "            KBArticleIDs: $($Update.KBArticleIDs)"
        Write-Host "                   Title: $($Update.Title)"
        Write-Host "                UpdateID: $($Update.Identity.UpdateID)"
        Write-Host "          RevisionNumber: $($Update.Identity.RevisionNumber)"
        Write-Host "LastDeploymentChangeTime: $($Update.LastDeploymentChangeTime.ToString())"
        Write-Host "            MsrcSeverity: $($Update.MsrcSeverity)"
        Write-Host "            IsDownloaded: $($Update.IsDownloaded)"
        Write-Host "                IsHidden: $($Update.IsHidden)"
        Write-Host "             IsInstalled: $($Update.IsInstalled)"
        Write-Host "             IsMandatory: $($Update.IsMandatory)"
        Write-Host "               IsPresent: $($Update.IsPresent)"
        Write-Host "              BrowseOnly: $($Update.BrowseOnly)"
        Write-Host "         IsUninstallable: $($Update.IsUninstallable)"
        Write-Host "         MinDownloadSize: $($Update.MinDownloadSize)"
        Write-Host "         MaxDownloadSize: $($Update.MaxDownloadSize)"
        Write-Host "          RebootRequired: $($Update.RebootRequired)"
        Write-Host "           AutoSelection: $($Update.AutoSelection)"
        Write-Host "            AutoDownload: $($Update.AutoDownload)"
        Write-Host "     SecurityBulletinIDs: $($Update.SecurityBulletinIDs)"
        Write-Host "     SupersededUpdateIDs:"
        Foreach ($SupUpdateID in $Update.SupersededUpdateIDs) {
          Write-Host "                          $SupUpdateID"
        } # Foreach $SupUpdateID
      } # If ($ShowUpdate)
    } # Foreach $Update
    if ($UpdateListed -eq $false) {
        Write-Host ""
        Write-Host "Update $UpdateNumber was not found searching Windows Update"
    }
    "----------------------------------------------------------------------------------"
  } # Foreach $UpdateNumber

  ""
  "For information on the AutoSelection and AutoDownload fields, see:"
  "http://www.mcbsys.com/blog/2015/11/important-microsoft-update-not-auto-installed/"

  ""
  "Script execution succeeded"
  $ExitCode = 0
}
catch {
  ""
  $error[0]
  ""
  "Hiding update(s) failed"
  $ExitCode = 1001 # Cause script to report failure in MaxFocus RM dashboard
}

""
"Local Machine Time:  " + (Get-Date -Format G)
"Exit Code: " + $ExitCode
Exit $ExitCode


1 Comment

  1. Important Microsoft Update Not Auto-Installed | MCB Systems   |  November 20, 2015 at 6:52 pm

    […] a handy-dandy PowerShell script, we can confirm that the update is not optional (not “BrowseOnly”), although it’s interesting […]

Leave a Reply





*