Introduction
A few years ago, I posted to Symantec Connect our vbscript which triggers an audited Windows Update at the close of our computer build process.
http://www.symantec.com/connect/downloads/vbscript-windows-updates
The critical feature of this script was that it provided us a patching process which had a definitive start and end. This allowed us to streamline patching directly into our automated build process and continue with other tasks on completion in a timely fashion such as the sysprep and final image upload.
This script has served us well through the years, but for one issue; the windows executable that manages the installation of the updates in Windows Vista and beyond, TrustedInstall.exe, consumes an increasing amount of resource with each update it installs. The result is that when we've got a fresh build with well over a hundred updates queued up, the TrustedInstaller.exe process quickly grows in memory to a whopping 3GB, permanently hogging the CPU, and grinding the machine to a near-halt. The update cycle will still eventually complete, but for us that can be over 8 hours due to this resource exhaustion.
Turns out this issue was well documented by Marc Durdin, http://marc.durdin.net/2011/08/reproducing-the-trustedinstaller-exe-leak-2, where he clearly documents what appears to be trustedinstaller.exe's inability to clean-up after itself.
Until Microsoft fix the root cause here, the only way I currently know to tackle this is to feed the TrustedInstaller process windows updates in manageable chunks. This keeps the process within reasonable resource boundaries, thus enabling a speedy install. This release (v1.2) of our vbscript to manage the update installation therefore contains a variable that allows you to fix the batch size.
v1.2 of our Windows Update Script
Today's download contains the script windowsupdate_v1.2.vbs. This script can be executed on Windows machines with the Windows Update Agent installed. To get the WindowsUpdateAgent, see Microsoft KB 949104 -How to obtain the latest version of the Windows Update Agent.
This script is based on a Microsoft script, with some changes by our Darren Collins here at Oxford to enable an entirely automated process suitable for Altiris deployments.
Key points are,
- Script will exit with code 98 if updates were installed
- Script will exit with code 99 if no updates found
- All activity logged to c:\Logs
- Batch size default is 30, (can be changed using the iMaxUpdates variable)
For googlability, the script is also pasted below.
'v1.0 WindowsUpdates.vbs by Microsoft 'v1.1 Ammendment by D. Collins to increase logging 'v1.2 Ammendment by I. Atkin to introduce batching ' ~~~ With 100 pending updates to install, TrustedInstaller.exe can consume ' ~~~ >3GB RAM and impede the updates process. So here limit the number of updates ' ~~~ that can be queued and installed at any one time. Dim I, I2, oSession, oSearcher, oSearchResult, oUpdate, oUpdatesToDownload, oUpdatesToInstall, oDownloader, oInstaller, oInstallationResult Dim iMaxUpdates Dim oFileSystem, oLog dim oWinFolder Dim blNoUpdates, qVal, oRunLog blNoUpdates = False iMaxUpdates=30 ' ~~~ --------------------------------------------- ' ~~~ Create objects ' ~~~ --------------------------------------------- Set oSession = CreateObject("Microsoft.Update.Session") Set oSearcher = oSession.CreateupdateSearcher() Set oFileSystem = CreateObject("Scripting.FileSystemObject") Set oWinFolder = oFileSystem.GetSpecialFolder (0) Set oLog = oWinFolder.CreateTextFile("SCTWindowsUpdates.log", True) Set oRunLog = oFileSystem.OpenTextFile("C:\Logs\WindowsUpdates-All.log", 8, True) Public Function DoWindowsUpdates On Error Resume Next ' ~~~ --------------------------------------------- ' ~~~ Search for updates ' ~~~ --------------------------------------------- oLog.WriteLine FormatDateTime (Now) & " Searching for updates..."& vbCRLF oLog.WriteLine FormatDateTime (Now) & " List of applicable items on the machine:" I2=0 For I = 0 To oSearchResult.Updates.Count-1 Set oUpdate = oSearchResult.Updates.Item(I) I2=I2+1 oLog.WriteLine I + 1 & "> "& oUpdate.Title oRunLog.WriteLine Now() & " - "& I + 1 & "> "& oUpdate.Title Next If I2 = 0 Then oLog.WriteLine FormatDateTime (Now) & " There are no applicable updates." blNoUpdates = True Exit Function Else oRunLog.WriteLine Now() & " - Applicable updates: "& I2 End If ' ~~~ --------------------------------------------- ' ~~~ Create collection of upates to download ' ~~~ --------------------------------------------- oLog.WriteLine vbCRLF & FormatDateTime (Now) & " Creating collection of updates to download:" Set oUpdatesToDownload = CreateObject("Microsoft.Update.UpdateColl") Dim iUpdatesCount iUpdatesCount=oSearchResult.Updates.Count if iUpdatesCount > iMaxUpdates then iUpdatesCount=iMaxUpdates oRunLog.WriteLine Now() & " - Too many updates. To limit trustedinstaller overhead, reducing to "& iMaxUpdates End if For I = 0 to iUpdatesCount-1 Set oUpdate = oSearchResult.Updates.Item(I) oLog.WriteLine I + 1 & "> adding: "& oUpdate.Title oRunLog.WriteLine I + 1 & "> adding: "& oUpdate.Title oUpdatesToDownload.Add(oUpdate) Next ' ~~~ --------------------------------------------- ' ~~~ Download updates ' ~~~ --------------------------------------------- oLog.WriteLine vbCRLF & FormatDateTime (Now) & " Downloading updates..." Set oDownloader = oSession.CreateUpdateDownloader() oDownloader.Updates = oUpdatesToDownload oDownloader.Download() ' ~~~ --------------------------------------------- ' ~~~ Create a collection of downloaded updates to install ' ~~~ --------------------------------------------- oLog.WriteLine vbCRLF & FormatDateTime (Now) & " Creating collection of downloaded updates to install:" Set oUpdatesToInstall = CreateObject("Microsoft.Update.UpdateColl") For I = 0 To iUpdatesCount-1 Set oUpdate = oSearchResult.Updates.Item(I) oLog.WriteLine I + 1 & "> adding: "& oUpdate.Title oUpdatesToInstall.Add(oUpdate) Next ' ~~~ --------------------------------------------- ' ~~~ Install updates ' ~~~ --------------------------------------------- oLog.WriteLine FormatDateTime (Now) & " Installing updates..." Set oInstaller = oSession.CreateUpdateInstaller() oInstaller.Updates = oUpdatesToInstall oInstaller.ForceQuiet = true Set oInstallationResult = oInstaller.Install() ' ~~~ --------------------------------------------- ' ~~~ Output results of install ' ~~~ --------------------------------------------- oLog.WriteLine FormatDateTime (Now) & " Installation Result: "& oInstallationResult.ResultCode oLog.WriteLine FormatDateTime (Now) & " Reboot Required: "& oInstallationResult.RebootRequired & vbCRLF oLog.WriteLine FormatDateTime (Now) & " Listing of updates installed and individual installation results:" For I = 0 to oUpdatesToInstall.Count - 1 oLog.WriteLine I + 1 & "> "& oUpdatesToInstall.Item(i).Title & ": "& oInstallationResult.GetUpdateResult(i).ResultCode Next End Function On Error Resume Next oRunLog.WriteLine Now() & " - Start: Windows Updates All script starting." oLog.WriteLine FormatDateTime (Now) & " Downloading and Installing All Available Software Updates" Set oSearchResult = oSearcher.Search("IsAssigned=1 and IsHidden=0 and IsInstalled=0 and Type='Software'") Call DoWindowsUpdates oLog.WriteLine vbCRLF & vbCRLF oLog.WriteLine FormatDateTime (Now) & " Updates Complete" oLog.Close If blNoUpdates = True Then oRunLog.WriteLine Now() & " - No Updates Found." qVal = 99 Else oRunLog.WriteLine Now() & " - Updates found and installed. See %WinDir%\WindowsUpdate.log for details." qVal = 98 End If oRunLog.WriteLine Now() & " - End: Windows Updates All script Complete." If qVal = 0 Then WScript.Quit(Err.Number) WScript.Quit(qVal)
Running the Script
You can run this script a multitude of ways to get your Windows Updates installed in a nicely logged and auditable fashion.
The way we utilise this script is using an Altiris DS6.9 job. This job keeps calling itself in a loop with reboots until the script yields a return code of 99 (which indicated no more updates left to install).
Below is a screen grab of this job (a .bin file for Altiris import can for this is also attached to this download) which shows how we accomplish this using five 'Run Script' tasks.
The 2nd task is the one which contains this script, and it will halt progress when the 99 return code is encountered,
The last task simply re-calls the job, giving us the loop.
Let's now look at some log outputs for the scenario of updates found/not found.
Log Extract for Update Found and Installed Scenario
Here is an example of a log entry from the log file, C:\Logs\WindowsUpdates-ALL.log for when a patch is found,
14/09/2012 09:19:43 - Start: Windows Updates All script starting. 14/09/2012 09:21:14 - 1 > Update for Office File Validation 2010 (KB2553065), 32-bit Edition 14/09/2012 09:21:14 - Applicable updates: 1 14/09/2012 09:21:55 - Updates found and installed. See %WinDir%\WindowsUpdate.log for details. 14/09/2012 09:21:55 - End: Windows Updates All script Complete.
Log Extract for No Updates Found Scenario
Here is an example of a log entry from the log file, C:\Logs\WindowsUpdates-ALL.log for when no updates are found,
14/09/2012 09:26:10 - Start: Windows Updates All script starting. 14/09/2012 09:26:17 - No Updates Found. 14/09/2012 09:26:17 - End: Windows Updates All script Complete.
In this case the script exits with return code 99. In your setup, you'd probably want to initiate a reboot and then simply continue with your task chain.
Gotchas
It's very important to have jobs preceding this script which ensure you have a suitable version of the MSI service and Windows Update agent installed.
Next, from time to time, you'll find that certain updates never get installed properly and result in you never getting a 99 return code to say all is now fine. If you find this happens for you, consider moving the problem update into it's own task. I've seen this for example with the Microsoft Office File Validation Add-in (to resolve we just added the OFV.EXE install into our MS Office installation job directly) and with Microsoft's Malicious Software Removal Update (which again we have a job to download and install automatically).
Finally, please note that this script will download updates from the Microsoft Updates server as configured on the client computer. In many organisations, the settings for the local WSUS server come down from GPO for your local server, and are not present when image building (before domain join). In this situation you have to be aware that these updates will therefore come down from Microsoft (unless you reg hack for otherwise).
Kind Regards,
Ian./