Powershell – Get the actual installed dates of hotfixes

The Get-Hotfix cmdlet has a bug in it that does not always return the installed date for patches, yet in control panel /Windows update the history will show the actual install date.

this little script “fixes” that

Function Get-HotfixAll
{

        [CmdletBinding()]
        [Alias()]
        [OutputType([int])]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
            $Computername
        )
    Invoke-Command -ScriptBlock {
    $Session = New-Object -ComObject Microsoft.Update.Session            
    $Searcher = $Session.CreateUpdateSearcher()         
    $HistoryCount = $Searcher.GetTotalHistoryCount()            
    # http://msdn.microsoft.com/en-us/library/windows/desktop/aa386532%28v=vs.85%29.aspx            
    $Searcher.QueryHistory(0,$HistoryCount) | ForEach-Object -Process {            
 
          $Title = $null            
        if($_.Title -match "\(KB\d{6,7}\)"){            
            # Split returns an array of strings            
            $Title = ($_.Title -split '.*\((KB\d{6,7})\)')[1]            
        }else{            
            $Title = $_.Title            
        }  
            

        # http://msdn.microsoft.com/en-us/library/windows/desktop/aa387095%28v=vs.85%29.aspx            
        $Result = $null            
        Switch ($_.ResultCode)            
        {            
            0 { $Result = 'NotStarted'}            
            1 { $Result = 'InProgress' }            
            2 { $Result = 'Succeeded' }            
            3 { $Result = 'SucceededWithErrors' }            
            4 { $Result = 'Failed' }            
            5 { $Result = 'Aborted' }            
            default { $Result = $_ }            
        }            
        New-Object -TypeName PSObject -Property @{            
            ComputerName = $ENV:Computername;
            InstalledOn = Get-Date -Date $_.Date;            
            KBArticle = $Title;            
            Name = $_.Title;            
            Status = $Result            
        }            
          
    } | Sort-Object -Descending:$true -Property InstalledOn |             
    Select-Object -Property *  
    } 
}

If you run this code , it will create a function you can run and pass the computer name to

e.g.

Get-HotfixAll -computername PC1
   So now it is just like any other cmdlet, you can store the results in a variable
$result = Get-HotfixAll -computername PC1

and then you can filter that variable , for example to get the latest 4 patches that have been appled, by sorting teh results on descending dat and using the select-object function to return the 4 latest hotfixes applied

$result | Sort-Object -Descending:$true -Property InstalledOn  |Select-Object -first 4

To expand this into scanning a list of servers and get a consolidated list of the last 4 patches applied to all the servers


$servers = Get-Content D:\BritV8\host.txt
$results = foreach ($server in $servers)
        {
         Get-HotfixAll -Computername $Server| Sort-Object -Descending:$true -Property InstalledOn  |Select-Object -first 4
        }

$Results | Select-Object NetBIOS_Name,Description,HotFixID, InstalledBy, InstalledOn | Export-csv -Path D:\BritV8\Patch_Installed_$((Get-date).ToString(‘MM-dd-yyyy’)).csv -NoTypeInformation

NOTE: WinRM is required to be running and ports open or the remote devices firewall

[BRITV8-WIN7-PC] Connecting to remote server BRITV8-WIN7-PC failed with the following error message : The client cannot connect to the destination specified in the request. Verify that the service on the destination is running and is accepting requests. Consult the logs and documentation for the WS-Management service running on the destination, most commonly IIS or WinRM. If the destination is the WinRM service, run the following command on the destination to analyze and configure the WinRM service: “winrm quickconfig”. For more information, see the about_Remote_Troubleshooting Help topic. + CategoryInfo : OpenError: (BRITV8-WIN7-PC:String) [], PSRemotingTransportException + FullyQualifiedErrorId : CannotConnect,PSSessionStateBroken

10 thoughts on “Powershell – Get the actual installed dates of hotfixes

  1. Great script.

    You just need one minor amendment to get $Computername to output. Because it’s inside the Scriptblock, it’s out of scope on the remote computer, therefore you need to change it to $Using:Computername – then it’ll work a treat.

    • Hi!

      I have updates the script so it can pass computernames.
      e.g. save my script as c:\temp\test.ps1
      Create a text file with a list of all computers you want to check
      .e.g. c:\temp\test.txt

      run this code

      $ListOfComputers = Get-Content -path C:\temp\test.txt

      foreach ($item in $ListOfComputers)
      {
      c:\temp\test.ps1 -computername $item
      }
      I will step through the list in the text file and give you the results

  2. Depending on how the hotfixes were installed, this script may return incorrect InstalledOn dates (or miss them altogether). The script will show the dates that are displayed the Control Panel > System and Security > Windows Update > View update history. The same KB install dates in Control Panel > Programs > Programs and Features > Installed Updates can be completely different.

    For example: One of our servers has KB4012215 with an ‘Installed On’ value of 28/02/2019 (Installed Updates) but the same KB has a ‘Date Installed’ value of 07/01/2019 (View update history).

    How hard is it for Microsoft to have one place for all Windows Updates metadata regardless of whether the installed method is via interactive, SCCM, script, …, whatever that can be queried and reported on.

  3. This script doesn’t work on windows 10 and server 2016.
    Even tried to run in locally but failed about WINRM even if WINRM is correctly enable as I can run other command.

    • Hi Frank,
      I have just tested it running on a 2012 R2 server against a 2016 server and running on 2016 server against a 2016 server. It works fine, no errors.

      What error are you getting?

  4. I Have one script ,Which will pull last install KB details .Can anyone help me to modify this and I want to pull last 4 KB details. Can anyone help me to pull last 4 KB details and save the output in excel
    ————-
    $Results = @()
    $servers = Get-Content D:\Bijan\host.txt
    foreach ($servers in $servers){
    $Properties = @{
    NetBIOS_Name = Get-WmiObject Win32_OperatingSystem -ComputerName $servers | select -ExpandProperty CSName
    Description = gwmi win32_quickfixengineering -computer $servers | ?{ $_.installedon }| sort @{e={[datetime]$_.InstalledOn}} | select -last 1 | select -ExpandProperty Description
    HotFixID = gwmi win32_quickfixengineering -computer $servers | ?{ $_.installedon }| sort @{e={[datetime]$_.InstalledOn}} | select -last 1 | select -ExpandProperty HotFixID
    InstalledBy = gwmi win32_quickfixengineering -computer $servers | ?{ $_.installedon }| sort @{e={[datetime]$_.InstalledOn}} | select -last 1 | select -ExpandProperty InstalledBy
    InstalledOn = gwmi win32_quickfixengineering -computer $servers | ?{ $_.installedon }| sort @{e={[datetime]$_.InstalledOn}} | select -last 1 | select -ExpandProperty InstalledOn
    }
    $Results += New-object psobject -Property $Properties
    } $Results | Select-Object NetBIOS_Name,Description,HotFixID, InstalledBy, InstalledOn | Export-csv -Path D:\Bijan\Patch_Installed_$((Get-date).ToString(‘MM-dd-yyyy’)).csv -NoTypeInformation

    • I have updated this post to give you an example of selecting the 4 latest hotfixes applied
      The key is to pipe your output via sort-object sorting descending on installedon and then piping it through select-object and selecting the first 4

Leave a Reply to Junior Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.