There are still a lot of servers I manage that are not a part of WSUS yet. This means going in and manually installing patches. I knew that I had a lot of these servers and just going down the list one by one isn’t very efficient. One thing that would really help is if I could get a list that is ordered by the last time it was patched. Then I could target the worst offenders first, and if I had time I could move on to the servers with fewer patches needed.
This simple command will get a list of all the patches that have been installed with a few other pieces of information.
wmic qfe get > c:wu.txt
For my computer this report is formatted with fixed width columns and could be parsed. I could add a node parameter to the above command and run it for each server. Then I would have all the data I need to get my report. However, that is just too much data and a lot of parsing. I stepped back over an older project that I still use and pulled out some of the WMI code. To dump everything in one heap and then sort through it would be inefficient. To make this more efficient I looked at the WMI class Win32_QuickFixEngineering or qfe.
The qfe has this nice column “InstallDate” which really sounded like a winner to me. Its actually a date type and could be very useful. However, its always NULL for every server I checked. I went back to the text report scratching my head and saw that there was another column “InstalledOn”. That is the column I need.
select InstalledOn from Win32_QuickFixEngineering where InstalledOn <> ''
I noticed that on some servers this value was blank for a lot of patches. I am assuming a bit here but it appears they are patches that were installed during sysprep or in some other fashion early in the servers life. The next thing I realized is that all I really need is the top 1 in descending order. This is when I found out there is no sorting in WMI. I would have to sort the results after the fact.
The MangementObject that gets returned is a bit tricky to work with so the first thing I do is read the results into an array. ArraysLists are easy to add to “.add” and also easy to sort. A .Sort() and then a .Reverse() is all I needed to pick out the last patch date. It was sitting in my array(0).
I ran into a snag rather quickly. Turns out vista era Microsoft decided it would be a great idea to start returning this as a 16 character hex value. Part of my code had to handle 4 different date types if you count NULLs.
'oReturn is one record in the managementobject. You get one for each patch Dim oDate As Object If oReturn("InstalledOn").Length = 16 Then oDate = DateTime.FromFileTimeUtc(Int64.Parse(oReturn("InstalledOn"), System.Globalization.NumberStyles.AllowHexSpecifier)) ElseIf oReturn("InstalledOn").Length > 7 And oReturn("InstalledOn").Length < 11 Then oDate = CDate(oReturn("InstalledOn")) Else 'I don't know what date type you are... you must not be a recent patch. oDate = DateTime.Now.AddYears(-100) End If
When searching for answers to my questions I did read some complaints about all patches not showing up in the results. To solve my problem it wasn’t really necessary to be totally accurate. I just needed a date the last time the server had some tlc. So far it has been very accurate.
Here are some links that I used to get to my end game.