Jump to content
Sign in to follow this  
cherdeg

Automatic Windowsupdate / ServicePack question

Recommended Posts

cherdeg

Hi!

We're currently using the CUI script below to windowsupdate newly provisioned VMs. Basically it works just perfect (although it's a bit talkative), but there is one serious drawback: When using it on a new system that is currently not updated to the latest ServicePack, the script of course installs that "Patch" also. Problem: A ServicePack requires interaction when installed this way. Goal is to avoid this. I know that I could install a ServicePack silently using the parameters "/quiet /norestart" - but therefore I had to start its executable seperately (which is, what you'll probably have guessed by now, is not desired). Does anyone of you know a way to avoid that interaction?

;####################################################################
;#
;#  Christoph Herdeg, May 2009
;#  http://www.cs-it-solutions.de
;#
;####################################################################


; Define the include files
; ==================================================================================================
#include <array.au3>


; Hide the damn Tray-Icon that nobody needs
; ==================================================================================================
#NoTrayIcon


; Read the UpdateIDs of all unwanted Packages from the INI-File
; ==================================================================================================
$s_IniFile = @ScriptDir & "\" & "Patchit.ini"
$a_UnwantedPatches = IniReadSection($s_IniFile, "unwanted")
Local $a_tmpUnwantedPatches[UBound($a_UnwantedPatches)]
For $i_i=0 to UBound($a_UnwantedPatches) -1
    $a_tmpUnwantedPatches[$i_i] = $a_UnwantedPatches[$i_i][1]
Next
$a_UnwantedPatches = $a_tmpUnwantedPatches
$s_UnwantedPatches = _ArrayToString($a_UnwantedPatches, "*")
$s_UnwantedPatches = StringReplace($s_UnwantedPatches, "*", "' and UpdateID != '")
$s_UnwantedPatches = StringTrimLeft($s_UnwantedPatches, 2)
$s_UnwantedPatches = $s_UnwantedPatches & "'"


; Opt in for the "Microsoft Update" Service, GUID
; ==================================================================================================
ConsoleWrite("Enabling Microsoft Update..." & @CRLF)
$o_ServiceManager = ObjCreate("Microsoft.Update.ServiceManager")
$o_ServiceManager.ClientApplicationID = "My App"
$o_NewUpdateService = $o_ServiceManager.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",7,"")


; Create the COM-Object for Microsoft-Update
; ==================================================================================================
ConsoleWrite("Creating COM-Object for Microsoft Update..." & @CRLF & @CRLF)
$o_updateSession = ObjCreate("Microsoft.update.Session")


; Start to search for approved Updates on the Windowsupdate-Source defined
; ==================================================================================================
ConsoleWrite("Searching for updates, please wait..." & @CRLF)
$o_updateSearcher = $o_updateSession.CreateupdateSearcher()


; Create an object containing the list of applicable Updates...if there are none, exit
; (The packages for Windows Search 4.0, the Office Live-Add-Ins and Windows Live Essentials are
;  being excluded due to their impossibly high crappiness factor)
; ==================================================================================================
$o_searchResult = $o_updateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0 " & $s_UnwantedPatches)

ConsoleWrite("The applicable updates for this machine are:" & @CRLF & @CRLF)
For $i_i = 0 To $o_searchResult.Updates.Count - 1
    $s_update = $o_searchResult.Updates.Item($i_i)
    ConsoleWrite($i_i + 1 & "> " & $s_update.Title & @CRLF)
Next

If $o_searchResult.Updates.Count = 0 Then
    ConsoleWrite("...there are no applicable updates. Your system is up-to-date!" & @CRLF)
    Exit
EndIf


; Create an object list containing the list of Updates to download
; ==================================================================================================
ConsoleWrite(@CRLF & "Creating collection of updates to download:" & @CRLF & @CRLF)
$s_updatesToDownload = ObjCreate("Microsoft.update.UpdateColl")

For $i_i = 0 To $o_searchResult.Updates.Count - 1
    $s_update = $o_searchResult.Updates.Item($i_i)
    ConsoleWrite($i_i + 1 & "> adding: " & $s_update.Title & @CRLF)
    $s_updatesToDownload.Add($s_update)
Next


; Do the download using the object list created above
; ==================================================================================================
ConsoleWrite(@CRLF & "Downloading updates..." & @CRLF)
$o_downloader = $o_updateSession.CreateUpdateDownloader()
$o_downloader.Updates = $s_updatesToDownload
$o_downloader.Download()

ConsoleWrite(@CRLF & "List of downloaded updates:" & @CRLF)
For $i_i = 0 To $o_searchResult.Updates.Count - 1
    $s_update = $o_searchResult.Updates.Item($i_i)
    If $s_update.IsDownloaded Then
        ConsoleWrite($i_i + 1 & "> " & $s_update.Title & @CRLF)
    EndIf
Next


; Create an object list containing the list of Updates to install
; ==================================================================================================
ConsoleWrite(@CRLF & "Creating collection of downloaded updates to install:" & @CRLF & @CRLF)
$o_updatesToInstall = ObjCreate("Microsoft.update.UpdateColl")

For $i_i = 0 To $o_searchResult.Updates.Count - 1
    $s_update = $o_searchResult.Updates.Item($i_i)
    If $s_update.IsDownloaded = True Then
        ConsoleWrite($i_i + 1 & "> adding: " & $s_update.Title & @CRLF)
        $o_updatesToInstall.Add($s_update)
    EndIf
Next


; Do the installation using the object list created above
; ==================================================================================================
ConsoleWrite(@CRLF & "Installing updates..." & @CRLF & @CRLF)
$i_installer = $o_updateSession.CreateUpdateInstaller()
$i_installer.Updates = $o_updatesToInstall
If $o_updatesToInstall.Count > 0 Then
    $o_installationResult = $i_installer.Install()
    ; Output results of install
    ConsoleWrite("Installation Result: " & $o_installationResult.ResultCode & @CRLF)
    ConsoleWrite("Listing of updates installed " & "and individual installation results:" & @CRLF & @CRLF)
    For $i_i = 0 To $o_updatesToInstall.Count - 1
        ConsoleWrite($i_i + 1 & "> " & $o_updatesToInstall.Item($i_i).Title & ": " & $o_installationResult.GetUpdateResult($i_i).ResultCode & @CRLF)
    Next
Else
    ConsoleWrite(@CRLF & "Nothing to install!" & @CRLF & @CRLF)
EndIf


; Restart the "Automatic Updates" Service
; ==================================================================================================
ConsoleWrite(@CRLF & "Restarting the Windows Update Service" & @CRLF)
RunWait(@SystemDir & "\net.exe stop wuauserv", "", @SW_HIDE)
sleep(2000)
RunWait(@SystemDir & "\net.exe start wuauserv", "", @SW_HIDE)


; Detect and report the current patchstate to a WSUS server if present
; ==================================================================================================
ConsoleWrite(@CRLF & "Executing wuauctl /detectnow" & @CRLF)
RunWait(@SystemDir & "\wuauctl /detectnow", "", @SW_HIDE)
sleep(2000)
ConsoleWrite(@CRLF & "Executing wuauctl /reportnow" & @CRLF & @CRLF)
RunWait(@SystemDir & "\wuauctl /reportnow", "", @SW_HIDE)

BTW.: If you give the "Specify intranet Microsoft update service location"-local policy a working value, the script will use that WSUS instead of http://update.microsoft.com.

Regards,

Chris

Share this post


Link to post
Share on other sites
storme

G'day cherdeg

Very nice bit of code you have there.

I've written my own updater that uses "autopatcher" to load all the individual updates and saved myself downloading the same update over and over for different computers. But it doesn't load the service packs so I've resorted to the brute force method below.

It maybe worth tacking something like this on to the start of your code to load the patches then execute your more elegant method for the individual patches.

If StringRight(@OSServicePack, 1) < 3 Then
        ;MsgBox(0, "Service pack", "@OSServicePack = " & StringRight(@OSServicePack, 1))
        writeLog("LOAD SP3")
        ; LOAD SP3
        $ExitCode = RunWait(@ScriptDir & '\UPDATER-Files\SP3\WindowsXP-KB936929-SP3-x86-ENU.exe /S', @TempDir)
        
        If @error Then
            writeLog("ERROR: " & $UpdateStage & " Exit Code = " & $ExitCode)
            MsgBox(0, "ERROR: RunWait " & $UpdateStage, "Stage = " & $UpdateStage & @CR & "Exit Code = " & $ExitCode)
            Exit
        EndIf

        Reboot(30) ; restart
    EndIf

I am curious what you have in your "Patchit.ini" file. Any chance of getting you to post it?

BTW I agree with your view on "Windows Search 4.0, the Office Live-Add-Ins and Windows Live Essentials". I've had so much trouble with them on customers computers...sigh

Good Luck

John Morrison

Share this post


Link to post
Share on other sites
cherdeg

Very nice bit of code you have there.

Thanks-a-lot "for them flowers" :D

I am curious what you have in your "Patchit.ini" file. Any chance of getting you to post it?

Yes of course...getting rid of "the pest" is quite easy...here's the INI-File:

CODE
[unwanted]

; Windows Live Essentials

ID1=9514bb43-f49d-499b-a655-9d64caf68976

; Office Live Add-Ins

ID2=af5c516f-1764-4dc0-b1e9-1e20dbee815c

; Windows Search 4.0

ID3=f1b1a591-bb75-4b1c-9fbd-03eedb00cc9d

If nobody else comes up with something, e.g. a switch/parameter, to control $i_installer.Install() to install everything unattended (btw. IE8 would be another problematic package), probably I'll modify and use your idea...

1. get Update IDs of all SPs and exclude them via the INI-File

2. check which OS and which currently installed SP

3. read from INI-File which is the latest SP for respective OS and if it is already installed set a Registry Key for the updater to execute after next login and reboot

4. IF NO THEN install separately from local repository, install RunOnce

5. IF YES THEN do the "default" updating

Your opinion?

Best Regards,

Chris

EDIT: Btw., I use the executable from my 1st post in conjunction with psexec over hosts drawn from our AD to update them remotely. Cool? Like it? :D

Edited by cherdeg

Share this post


Link to post
Share on other sites
cherdeg

For the people interested in this topic here the UpdateIDs our WSUS reports for the different ServicePacks:

; Latest Service Packs
; ==============================

; Windows Server 2000 Service Pack 4 (32-Bit x86)
SPID1=b91f8a5d-56f6-4e5c-b7c0-fd95360f7a27

; Windows XP Service Pack 3 (32-Bit x86)
SPID2=34b52415-a5ba-41e9-8072-9d2dee8b22a9

; Windows XP Service Pack 3 (64-Bit x64)
; SPID3=No ID yet because XPx64SP3 = W2K3x64SP3

; Windows Server 2003 Service Pack 2 (32-Bit x86)
; SPID=5c23105e-2007-449d-b9c0-5f60735df63b (???)
SPID4=9452503c-05be-4a64-99ed-9a59f7d65398

; Windows Server 2003 Service Pack 2 (64-Bit x64)
SPID5=a024086f-17fd-4875-8d84-a5d9a6811ed4

; Windows Vista Service Pack 2 (32-Bit x86)
SPID6=cad30b29-e5a2-42ed-bcdc-501208b47b91

; Windows Vista Service Pack 2 (64-Bit x64)
SPID7=ba19a750-2fab-41c5-ab41-822c4373974d

; Windows Server 2008 Service Pack 2 (32-Bit x86)
SPID8=363855a3-39b4-4b6e-944e-da8ccea428d7

; Windows Server 2008 Service Pack 2 (64-Bit x64)
SPID9=56e25ed4-8d4a-4e8c-81d8-cc71983c58e0

Please note that I found two Versions of Windows Server 2003 Service Pack 2 (32-Bit x86) on our WSUS (which disturbs me a bit). Therefore there are also two UpdateIDs: 5c23105e-2007-449d-b9c0-5f60735df63b (430 hits on google) and 9452503c-05be-4a64-99ed-9a59f7d65398 (5080 hits on google). I have no clue why - maybe one of you guys has?

EDIT: Corrected typo in UID of Windows Server 2003 Service Pack 2 (64-Bit x64).

Edited by cherdeg

Share this post


Link to post
Share on other sites
storme

Thanks-a-lot "for them flowers" :D

Yes of course...getting rid of "the pest" is quite easy...here's the INI-File:

Thanks! Sorry for not getting back sooner. My interenet was down for a couple of days... :D

If nobody else comes up with something, e.g. a switch/parameter, to control $i_installer.Install() to install everything unattended (btw. IE8 would be another problematic package), probably I'll modify and use your idea...

I'd definatle take a look a "autopatcher". I use it to save on all the downloads but they also look at interactions and save me a lot of heart ache. :D As all the machines I update are in my work shop it's a lot quicker to have all the files on hand. ;)

They have included IE8 in the latest update so it even does that.

1. get Update IDs of all SPs and exclude them via the INI-File

2. check which OS and which currently installed SP

3. read from INI-File which is the latest SP for respective OS and if it is already installed set a Registry Key for the updater to execute after next login and reboot

4. IF NO THEN install separately from local repository, install RunOnce

5. IF YES THEN do the "default" updating

Just a suggestion.

In step 1 check if a SP is in the list of updates.

If it is then load it from the local repository

Set auto restart after load to run the script again and check for updates - I do this in my script.

If there are no SPs then do regular updates

EDIT: Btw., I use the executable from my 1st post in conjunction with psexec over hosts drawn from our AD to update them remotely. Cool? Like it? :P

sounds good. I love AutoIT the more I use it more I love it. ;)

John Morrison

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.