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="http://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="http://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
Skip to main content