Dynamic Provisioning with VMM: Proxy, Windows Updates, and Scripts

In our environment, there are two things that are critical to success of an environment that is dynamically built from scratch – Updates & Internet connectivity.  This might seem odd since most would believe that we would utilize WSUS & the Software Update Points in Configuration Manager to do our patching and truth be known we do.  However, non-compliant servers with specific hot-fixes are not allowed to come on the network and to avoid big delays we do not push the ConfigMgr clients to our servers “immediately.”  Thus, we depend on Windows Update connectivity for our servers and also proxies as we can’t get to the internet without them.

In today’s post, I’m going to share a settings configuration we use in our unattend.xml to ensure that our automated scripts effectively reach the internet and secondly the script itself.  The script is shared as-is and credit goes to Ben Shy & Michael Schmidt on my team for the actual building of the script though I’m consuming it in my design of dynamic provisioning.

Setting Proxy Settings to work during Dynamic Provisioning Servers

In some situations, you will have the following settings in your unattend.xml file though upon completion you will not have any proxy settings set.  This is very troublesome and I couldn’t locate much of any data or information regarding how to correct this on the internet and had to use internal resources to troubleshoot (Thanks Eric!).  For the longest time, I had the following settings in my unattend XML:

Code Snippet

  1.   <settings pass="oobeSystem">

  2.     <component name="Microsoft-Windows-IE-ClientNetworkProtocolImplementation" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="https://schemas.microsoft.com/WMIConfig/2002/State">

  3.       <HKLMProxyEnable>true</HKLMProxyEnable>

  4.       <HKLMProxyServer>itgproxy</HKLMProxyServer>

  5.     </component>

  6.   </settings>

This is all that is discussed in the WAIK unattended help file and thus should just work, right?  Wrong!  Unfortunately, there was a little unknown (to me) key called ProxySettingsPerUser that needed to get set though this wasn’t outlined anywhere in the documentation.  I can’t, unfortunately, provide you a lot of insight as to what is so important other than what is covered in TechNet.  It basically changes the Windows behavior where proxy settings are per machine rather than per user.  However, I had no idea this was needed nor that I could include it in my unattend.xml file.  However, with a little digging, some help, and testing we found that it was absolutely possible to include this in the WAIK’s unattend.xml and it would be honored.  Thus, my working XML is the following that sets my proxy settings so that scripts running on the server but not as a user would honor the proxy settings:

Code Snippet

  1. <settings pass="oobeSystem">
  2.     <component name="Microsoft-Windows-IE-ClientNetworkProtocolImplementation" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="https://schemas.microsoft.com/WMIConfig/2002/State">
  3.       <POLICYProxySettingsPerUser>0</POLICYProxySettingsPerUser>
  4.       <HKLMProxyEnable>true</HKLMProxyEnable>
  5.       <HKLMProxyServer>OurProxy</HKLMProxyServer>
  6.     </component>
  7.   </settings>

After testing, the script ran successfully and completely thus ensuring that our virtual machines were compliant quickly after they hit the network.

Scripting Critical Updates for Windows Server from Windows Update

In this section, we will focus on the script itself that does the work of connecting to Windows update, providing the appropriate client-based information, and installing the updates.  The first thing to note that is critical is the error message received when you do not have a viable connection to the internet.

Using Windows Update vs. Microsoft Update:  That is the question…

A great number of folks don’t quite know the reason that Microsoft has two viable update engines, Windows (WU) and Microsoft update (MU).  The former focuses only on the Windows platform and will only serve the core Windows operating system binaries.  The latter, though, is an opt-in service, that offers the ability for servers to get updated and patched that are not only running Windows but additional Microsoft software such as Office, etc.  In order for the Microsoft update engine to work, the Windows update agent client is replaced with the Microsoft update agent in Windows Vista & above.  For older operating systems, a different ActiveX control is installed in Internet Explorer.

For our environment, we wanted to ensure that we downloaded only critical updates from Windows Update at the time of server creation.  In order to do this, we utilized the following sample script:

Code Snippet

  1. '================================================================================

  2. ' Microsoft Update

  3. '================================================================================

  4. ' Authors: BENSHY/OTHERS/MSDN

  5. ' Comments/Cleanup: MICHS 5.14.09

  6. '

  7. '================================================================================

  8. '#############################

  9. ' Create Session

  10. '#############################

  11. wscript.echo "-------------------------- Create Session --------------------------"

  12. Set UpdateSession = CreateObject("Microsoft.Update.Session")

  13. Set UpdateSearcher = UpdateSession.CreateUpdateSearcher()

  14. '#############################

  15. ' Register Microsoft Update

  16. '#############################

  17. RegisterMu

  18. updateSearcher.ServerSelection = 2

  19. updateSearcher.ServiceID = "7971f918-a847-4430-9279-4a52d1efe18d"

  20. '#############################

  21. ' Create List of Updates to Download

  22. '#############################

  23. wscript.echo "-------------------------- Create List --------------------------"

  24. Set UpdatesToDownload = CreateObject("Microsoft.Update.UpdateColl")

  25. '#############################

  26. ' Search for Updates

  27. '#############################

  28. wscript.echo "-------------------------- Search for Updates ---------------Start: " & Now()

  29. Set SearchResult = UpdateSearcher.Search("Isinstalled=0")

  30. wscript.echo "---------------------------Search for Updates Complete-------End:   " & Now()

  31. '#############################

  32. ' Quit if No Updates Found

  33. '#############################

  34. If SearchResult.Updates.Count = 0 Then

  35.     WScript.Quit

  36.     WScript.Echo "No updates found."

  37. End If

  38. Dim strSpacer

  39. For I = 0 To SearchResult.Updates.Count-1

  40.     Set Update = SearchResult.Updates.Item(I)

  41.     

  42.     ' formatting helper

  43.     If I < 10 Then

  44.         strSpacer = " "

  45.     Else

  46.         strSpacer = ""

  47.     End If

  48.     

  49.     ' write to console

  50.     WScript.Echo "[" & strSpacer & I & "]  Found Update, Marking For Download:  " & update.Title

  51.     UpdatesToDownload.Add(Update)

  52. Next

  53. '#############################

  54. ' Download Updates

  55. '#############################

  56. wscript.echo "-------------------------- Downloading Updates ----------------------Start: " & Now()

  57. Set Downloader = UpdateSession.CreateUpdateDownloader()

  58. Downloader.Updates = UpdatesToDownload

  59. On Error Resume Next

  60. Downloader.Download()

  61. If Err.number <> 0 Then

  62.     Wscript.Echo "An error occurred in  Downloader.Download() of updates"

  63.     Wscript.Echo "Number: " & err.number

  64.     Wscript.Echo "Description:  " & err.Description

  65.     Wscript.Quit (Err.number)

  66. End If

  67. On Error Goto 0

  68. wscript.echo "-------------------------- Downloading Complete ----------------------End: " & Now()

  69. '#############################

  70. ' Create List of Updates to Install

  71. '#############################

  72. wscript.echo "-------------------------- Create List of Updates to Install --------------------------"

  73. Set UpdatesToInstall = CreateObject("Microsoft.Update.UpdateColl")

  74. For I = 0 To SearchResult.Updates.Count-1

  75.     set Update = SearchResult.Updates.Item(I)

  76.     If Update.IsDownloaded = true Then

  77.         UpdatesToInstall.Add(Update)

  78.         WScript.Echo "Marking update for install: [" & Update.Title & "]"

  79.     End If

  80. Next

  81. '#############################

  82. ' Install Updates

  83. '#############################

  84. wscript.echo "-------------------------- Installing Updates --------------------------"

  85. Set Installer = UpdateSession.CreateUpdateInstaller()

  86. Installer.Updates = UpdatesToInstall

  87. Set InstallationResult = Installer.Install()

  88. wscript.Echo "Installation Result: " & InstallationResult.ResultCode

  89. wscript.Echo "Reboot Required: " & InstallationResult.RebootRequired

  90. wscript.Echo "Listing of updates installed and individual installation results:"

  91.     

  92.     For I = 0 to UpdatesToInstall.Count - 1

  93.         WScript.Echo I + 1 & "> " & _

  94.         UpdatesToInstall.Item(I).Title & ": " & TranslateMuCode(InstallationResult.GetUpdateResult(i).ResultCode)

  95.     Next

  96. '#############################

  97. ' Quit

  98. '#############################

  99. WScript.Quit

  100. '================================================================================

  101. ' Translate Microsoft Update Installation Results

  102. '================================================================================

  103. Function TranslateMuCode(theCode)

  104.   TranslateMuCode = "[" & theCode & "] "

  105.   if (theCode = 0) Then TranslateMuCode = TranslateMuCode & "Not Started"

  106.   if (theCode = 1) Then TranslateMuCode = TranslateMuCode & "In Progress"

  107.   if (theCode = 2) Then TranslateMuCode = TranslateMuCode & "Succeeded"

  108.   if (theCode = 3) Then TranslateMuCode = TranslateMuCode & "Succeeded with Errors"

  109.   if (theCode = 4) Then TranslateMuCode = TranslateMuCode & "Failed"

  110.   if (theCode = 5) Then TranslateMuCode = TranslateMuCode & "Aborted"

  111. End Function

  112. '================================================================================

  113. ' Register Microsoft Update (if never registered)

  114. '================================================================================

  115. Function RegisterMu

  116.     Dim fso

  117.     Dim file

  118.     Dim WshShell

  119.     Dim updateService

  120.     Dim updateServiceManager

  121.     

  122.     found = false

  123.     

  124.     Set fso = CreateObject("Scripting.FileSystemObject")    

  125.     Set WshShell = WScript.CreateObject ("WScript.Shell")

  126.     

  127.     Set updateServiceManager = CreateObject("Microsoft.Update.ServiceManager")

  128.     Set updateService = updateServiceManager.Services

  129.     

  130.     If err <> 0 Then

  131.         WScript.Echo "CreateObject(Microsoft.Update.ServiceManager) failed with error 0x" & Hex(err.Number)  & err.Description

  132.         WScript.Quit(2)

  133.     End If

  134.     For I=0 to updateService.Count - 1

  135.         Set item = updateService.Item(i)

  136.         If item.ServiceID = "7971f918-a847-4430-9279-4a52d1efe18d" Then

  137.             found = true

  138.         End IF

  139.     Next    

  140.       

  141.     IF found = false Then

  142.         updateServiceManager.AddService2 "7971f918-a847-4430-9279-4a52d1efe18d", 2, ""

  143.         

  144.         If err <> 0 Then

  145.             WScript.Echo "updateServiceManager.AddService() failed with error 0x" & Hex(err.Number) & err.Description

  146.         Else

  147.             WScript.Echo "MU is registered with WU Agent"

  148.         End IF

  149.     End IF

  150. END Function

Using Microsoft Update instead of Windows Update

In the above script, the updateSearcher.ServerSelection option can be modified to instead use Microsoft update.  This depends on your scenario and your goal.  For our environment, we didn’t need to focus on Microsoft update rather just Windows update.  For example, if i set server selection to 1 then I would get a ton of Microsoft updates including items like Windows Live Essentials (ouch!) and other bogus updates.  I corrected this behavior changing the value for ServerSelection to 2 which uses Windows Update and only gets Critical Updates.

Summary

In this post, I shared how I modified our unattend.xml file that is used with our provisioning of new virtual machines to ensure that the servers not only were ready to serve clients but also they were correctly patched.  This is very important for the security of the network and your sanity.  In order to do this, I used a unknown setting in my unattend.xml file that is called PolicyProxySettingsPerUser and set this value to 0 that forced the proxy settings per machine.

Lastly, I shared a sample script managed and maintained by my team for patching clients using MU and I altered it to use WU instead. 

Enjoy!

-Chris

Digg This