A while back, I needed to check Windows Update information on a few hosts, so I wanted to script it. Below are a few links that helped me solve this started.
Note: For Windows Update, you need the TiWorker.exe process, which can consume a lot of CPU. See DISM fix for Windows 8.1 high CPU usage of TiWorker.exe which is basically the same for all Windows versions since 8.0.
The infrastructure management on that site was ehm, a bit lacking, so PowerShell modules were out, heck even PowerShell itself was initially problematic (it needed running of unsigned sources.
A few notes on the above links.
Using Microsoft.Update.AutoUpdate
This gets the last date that anything was done (query, actual update, download) on Windows Updates, but does not guarantee the installation date; on some systems it does not even return a result:
$windowsUpdateObject = New-Object -ComObject Microsoft.Update.AutoUpdate
$windowsUpdateObject.Results
This one works better though:
$windowsUpdateObject = New-Object -ComObject Microsoft.Update.AutoUpdate
$windowsUpdateObject.Results.LastInstallationSuccessDate
Based on that, you can get the number of days like this:
(New-TimeSpan -Start $windowsUpdateObject.Results.LastInstallationSuccessDate.Date -End (Get-Date)).Days
Using Get-HotFix
Though some people report that InstalledOn can be empty, I’ve hardly that happen with Get-HotFix. The easiest way to get around that is filtering with | Where-Object InstalledOn -ne $null
The cool thing with Get-HotFix is that you can filter on the kind of security update, so this gets the moment the last security update got installed:
(Get-HotFix -Description "Security Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn
And this the number of days since the last security update got installed:
(New-TimeSpan -Start (Get-HotFix -Description "Security Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days
Step by step:
Get-HotFix -Description "Security Update"
Gets all the security updates.
| Where-Object InstalledOn -ne $null
Filter out entries having an empty InstalledOn.
Sort-Object InstalledOn -Descending
Get the most recent on the top.
| Select-Object -First 1
Select only the top entry.
(Get-HotFix -Description "Security Update"...).InstalledOn
Get only the InstalledOn property.
Get-Date
Get the current timestamp consisting of date and time.
New-TimeSpan -Start (...).InstalledOn -End (Get-Date)
Get a TimeSpan over a start and end timestamp.
(New-TimeSpan ...).Days
Get the Days property of a TimeSpan.
You can do the same for regular updates by changing the -Description parameter:
(Get-HotFix -Description "Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn
(New-TimeSpan -Start (Get-HotFix -Description "Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days
The Description values I found are these:
PS C:\Users\Developer> Get-HotFix | Sort-Object -Unique Description | Select-Object Description
Description
-----------
Hotfix
Security Update
Update
Ironically, since the command is called Get-HotFix, the Hotfix entries on my various Windows systems have been a long long time ago:
(New-TimeSpan -Start (Get-HotFix -Description "Hotfix" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days
When writing this in 2017, on Windows 8.1, this was more than 600 days, Windows 7 more than 400 days and Windows 10 did not have any Hotfix entries.
Old PowerShell versions
On PowerShell 2 and older, you get an error containing “Where-Object : Cannot bind parameter ‘FilterScript'”:
Where-Object : Cannot bind parameter 'FilterScript'. Cannot convert the "InstalledOn" value of type "System.String" to type "System.Management.Automation.ScriptBlock".
At line:1 char:48
+ (New-TimeSpan -Start (Get-HotFix | Where-Object <<<< InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days
+ CategoryInfo : InvalidArgument: (:) [Where-Object], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.WhereObjectCommand
You solve it like this:
(New-TimeSpan -Start (Get-HotFix | Where-Object { $_.InstalledOn -ne $null } | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days
By now code has become almost unreadable, so you can split it using backtick ` characters:
( `
New-TimeSpan -Start `
( `
Get-HotFix | Where-Object { $_.InstalledOn -ne $null } `
| Sort-Object InstalledOn -Descending `
| Select-Object -First 1 `
).InstalledOn `
-End (Get-Date)`
).Days
One more thing
On non-English Windows systems, the InstalledOn might actually be in the future, as you can view this happening by this simple command which I ran on 2017-11-02 :
Get-HotFix | Out-GridView
You solve it by adding a filter:
Get-HotFix | Where-Object InstalledOn -lt (Get-Date) | Out-GridView
If you run them from a script (like a batch file Get-HotFix ^| Out-GridView or ps1 file Get-HotFix | Out-GridView), then the grid-view will pop-up and immediately close because the PowerShell process ends. In that case, you need to change your scripts to add the -Wait parameter:
PowerShell Get-HotFix ^| Out-GridView -Wait
Powershell.exe -Command "Get-HotFix | Out-GridView -Wait"
Get-HotFix | Out-GridView -Wait
See:
In C#
If I ever want to do the same from C#, I need to figure out where to get the WUApiLib from; more on that library is at [WayBack] Use C# to interact with Windows Update – Stack Overflow and [WayBack] Searching, Downloading, and Installing Updates (Windows).
–jeroen