Get List of Installed Windows Updates

This post falls into the category of note to self posts. A while back I researched the Internet for a way to get the list of installed updates on a computer.
This is useful for those of us still using Microsoft WSUS without SCCM or some other Reporting Tool, because WSUS reports only the number of computers having or not having a patch installed/applicable, but not which ones.
As of this date there are no Powershell cmdlets that let you get this information, no WMI query no nothing. You have to get it programatically, so I went along and created the following powershell code that creates a report.

$InputObject = Read-host -Prompt "Insert Computername to get list of installed updates"
$Report = @()
$filename = "$env:Temp\Report_$(get-date -Uformat "%Y%m%d-%H%M%S").csv"
If ($Computer -eq $null -and $InputFile -eq $null) {
	Write-Host -ForegroundColor Yellow "No Computer or ComputerList given, assuming value is localhost"
	$InputObject = $env:COMPUTERNAME
	}
$InputObject | % {
   $objSession = [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session",$_))
   $objSearcher= $objSession.CreateUpdateSearcher()
   $HistoryCount = $objSearcher.GetTotalHistoryCount()
   $colSucessHistorvy = $objSearcher.QueryHistory(0, $HistoryCount)
   Foreach($objEntry in $colSucvessHistory | where {$_.ResultCode -eq '2'}) {
       $pso = "" | select Computer,Title,Date
       $pso.Title = $objEntry.Title
       $pso.Date = $objEntry.Date
       $pso.computer = $_
       $Report += $pso
       }
   $objSession = $null
   }
$Report | where { $_.Title -notlike 'Definition Update*'} | Export-Csv $filename -NoTypeInformation -UseCulture
ii $filename

Once you run this report and have an csv viewer installed (excel for example( it will open up the file so you can review it. When exporting I did a filter to remove MS Forefront definition updates as it is pretty irrelevant most of the time, you use other tools to manage Forefront definitions.

Learning Points

Line 05 – This line creates and instance of the Windows Update API. What is neat about this function is the fact that can create an instance of the API and connect to a remote computer, notice the “$_” at the end of the line.

Line 09 – In this line after searching the entire history we filter out all but successful updates. Yes it would be nice to do that in the actual search, but I don’t know if it is possible. So I resorted to filtering out only successful result codes.

Below is a table with possible values. This can be useful if you want to generate a report based on the result code

Result Code Update Status
0 Not Started
1 In Progress
2 Successful
3 Incomplete
4 Failed
5 Aborted

That’s about it with getting the list of installed updates, the bit of code above can be easily integrated to run across a large number of computers. Thanks for reading and feedback.

Update1: Feb 2013 – i’ve modified the code to account for when you are trying to copy paste the data into a command line. Also I’ve discovered, that at least in my case, it doesn’t run on Windows 2012 machines.


Tagged , . Bookmark the permalink.

4 Responses to Get List of Installed Windows Updates

  1. Michael says:

    Hi, I know this was over a year ago but I just came across it. I’m trying to build a list of windows updates that I can search on instead of scrolling the list from “Installed Updates” When running this I came across an error that I can’t seem to troubleshoot. The error is as follows:

    An empty pipe element is not allowed.
    At C:\PowerShell\list-installedupdates.ps1:10 char:29
    + $pso = "" | <<<< select Computer,Title,Date
    + CategoryInfo : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : EmptyPipeElement

    Thoughts?

  2. Ionut Nica says:

    Hi,

    Thanks for stopping by, I took another look at the snippet and I found some strange formatting errors, missing characters and so on. I fixed them, just ran the script, it runs fine, returns updates. I’m not sure I understand your idea, about being able to search instead of listing them. If you want to add more criteria to the filter in the for loop, change this line
    Foreach($objEntry in $colSucessHistory | where {$_.ResultCode -eq ’2′ -and (< <>>)})

  3. Guest says:

    Hello. When I try to run it on Windows 8 I got next
    Exception calling “CreateInstance” with “1″ argument(s): “Retrieving the COM class factory for remote component with CLSID
    {4CB43D7F-7EEE-4906-8698-60DA1C38F2FE} from machine Windows8 failed due to the following error: 80070005 Windows8.”
    At C:\Users\User\Desktop\InstalledUpdates.ps1:6 char:4
    + $objSession = [activator]::CreateInstance([type]::GetTypeFromProgID(“Microsof …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : UnauthorizedAccessException

    You cannot call a method on a null-valued expression.
    At C:\Users\User\Desktop\InstalledUpdates.ps1:7 char:4
    + $objSearcher= $objSession.CreateUpdateSearcher()
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Users\User\Desktop\InstalledUpdates.ps1:8 char:4
    + $HistoryCount = $objSearcher.GetTotalHistoryCount()
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Users\User\Desktop\InstalledUpdates.ps1:9 char:4
    + $colSucessHistory = $objSearcher.QueryHistory(0, $HistoryCount)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    ii : An error occurred in sending the command to the application
    At C:\Users\User\Desktop\InstalledUpdates.ps1:20 char:1
    + ii $filename
    + ~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Invoke-Item], Win32Exception
    + FullyQualifiedErrorId : System.ComponentModel.Win32Exception,Microsoft.PowerShell.Commands.InvokeItemCommand

  4. Ionut Nica says:

    Hi,

    That happens because you are most likely trying to copy paste the code into the command line. When you do that, you are no passing any input to the $Inputobject variable, so your query doesn’t run on any objects. I’ve made a small change to the script above so it will work even if you attempt to run it with no inputobject, it will assume you want to run it on local host.

    That being said, Initially I suspected the problem was the fact that you were running the commands in Windows 8, but as it turned out it was just an input problem.
    However, I tested this code snippet on a windows 2012 test machine, and the code doesn’t work, at least not in this current form. I will take an indepth look to see if there is an easy fix, or we have to wait for someone who knows the Windows 8 APIs more intimately to help us out.