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
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.
Cheers, I have changed it to use $env:Computername
Guys, i need this script for mutiple servers.
Someone can ajust this or share a new one.
Thanks a lot
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
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.
Can we pull last 3-4 hostfix. What needs to modify if i need to pull last 4 updates.
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?
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