{"id":7211,"date":"2015-03-18T00:01:00","date_gmt":"2015-03-18T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2015\/03\/18\/update-offline-virtual-machine-with-powershell-and-wsus-offline-update-part-2\/"},"modified":"2019-02-18T10:30:14","modified_gmt":"2019-02-18T17:30:14","slug":"update-offline-virtual-machine-with-powershell-and-wsus-offline-update-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/update-offline-virtual-machine-with-powershell-and-wsus-offline-update-part-2\/","title":{"rendered":"Update Offline Virtual Machine with PowerShell and WSUS Offline Update: Part 2"},"content":{"rendered":"<p><b style=\"font-size:12px\">Summary<\/b><span style=\"font-size:12px\">: Microsoft PowerShell MVP, Sean Kearney, talks about updating an offline virtual machine with Windows PowerShell and WSUS Offline Update.<\/span>\nHonorary Scripting Guy, Sean Kearney, here. Yesterday, I introduced you to a tool you can use to updating offline virtual machines: <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2015\/03\/17\/update-offline-virtual-machine-with-powershell-and-wsus-offline-update.aspx\" target=\"_blank\">Update Offline Virtual Machine with PowerShell and WSUS Offline Update: Part 1<\/a>. Today, we&#8217;ll learn how to conclude our process.\nWithin the folder structure is a folder called <b>Client<\/b>. This is the structure we need to expose to our offline virtual machines. The process we&rsquo;re going to follow will be very simple:<\/p>\n<ul>\n<li>Create a VHD file structure<\/li>\n<li>Mount the VHD file<\/li>\n<li>Partition and format the VHD file<\/li>\n<li>Copy the folder structure to the VHD file<\/li>\n<li>Dismount the VHD file<\/li>\n<\/ul>\n<p>The following process is repeated for each virtual machine you supply:<\/p>\n<ul>\n<li>Get a list of virtual machines that are off.<\/li>\n<li>Attach the VHD file to the virtual machines, one at a time.<\/li>\n<li>Inject a setting to launch a script into the virtual machine Registry.<\/li>\n<li>Temporarily adjust the Registry to automatically log in as a user with local Admin rights.<\/li>\n<li>Temporarily disable the User Account Control (UAC).<\/li>\n<li>Temporarily change the Windows PowerShell execution policy.<\/li>\n<li>Inject a small script to identify our VHD and launch the update script.<\/li>\n<li>Power up the virtual machine and wait for it to update, then shut down automatically.<\/li>\n<li>Remove the WSUS Offline Updates VHD.<\/li>\n<li>Power up the virtual machine and leave it running for about two hours to allow it to process the updates.<\/li>\n<li>Power down after the allocated time period.<\/li>\n<\/ul>\n<p>We do this until we&rsquo;ve run out of virtual machines that we want to patch&#8230;\nTo get started, we make that new VHD file and format it as one giant NTFS file system. For a detailed explanation of the process that follows, please refer to this most excellent post by Ed Wilson: <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2013\/05\/29\/use-powershell-to-initialize-raw-disks-and-partition-and-format-volumes.aspx\" target=\"_blank\">Use PowerShell to Initialize Raw Disks and to Partition and Format Volumes<\/a>.<\/p>\n<p style=\"margin-left:30px\"># Create the VHD file<\/p>\n<p style=\"margin-left:30px\">$VHDPath=&rsquo;C:WsusOfflineUpdates.VHDX&rsquo;<\/p>\n<p style=\"margin-left:30px\">New-VHD $VHDPath -SizeBytes 20GB -Dynamic<\/p>\n<p style=\"margin-left:30px\"># Attach the VHD to the Windows File system<\/p>\n<p style=\"margin-left:30px\">Mount-VHD $VHDPath<\/p>\n<p style=\"margin-left:30px\"># Partition the VHD<\/p>\n<p style=\"margin-left:30px\">$VHD=Get-VHD $VHDPath | Get-Disk<\/p>\n<p style=\"margin-left:30px\">$Drive=$VHD | Initialize-Disk -PartitionStyle MBR &ndash;PassThru |<\/p>\n<p style=\"margin-left:30px\">New-Partition &ndash;AssignDriveLetter &ndash;UseMaximumSize |<\/p>\n<p style=\"margin-left:30px\">Format-Volume &ndash;FileSystem NTFS &ndash;NewFileSystemLabel &lsquo;WSUSOffline&rsquo; &ndash;Confirm:$false\nYou might notice a coulple differences in this code when compared to Ed&rsquo;s post. First, instead of filtering for <b>Raw<\/b> file systems, I have told <b>Get-Disk<\/b> to work explicitly with the file I&rsquo;m working with.\nThe second key piece is that I am capturing the results of the formatting in a variable called <b>$Drive<\/b>. In this manner, I can programmatically grab the drive letter that is assigned to the newly formatted VHD file. (Remember, we need to copy the client structure from <b>Wsusoffline<\/b> into this VHD file.)\nNow that the disk has been prepared, naturally, we can just copy the folder structure with a little Windows PowerShell:<\/p>\n<p style=\"margin-left:30px\">Copy-Item -path &#8216;C:wsusofflineclient&#8217; -Recurse -destination (&ldquo;$($Drive.DriveLetter):client&rdquo;) -Force\nNow that the files are in the VHD file, we need to dismount it so we can use it within the virtual machines:<\/p>\n<p style=\"margin-left:30px\">Dismount-VHD $VHDPath\nBefore all of this, we need a few commands in Windows PowerShell so our script will:<\/p>\n<ul>\n<li>Find the attached disk with the name <b>WsusOffline<\/b>.<\/li>\n<li>Obtain its drive letter.<\/li>\n<li>Launch the command under the folder named <b>update.cmd<\/b>.<\/li>\n<\/ul>\n<p>All this is simply this little Windows PowerShell script:<\/p>\n<p style=\"margin-left:30px\">$Drive=Get-Volume | where { $_.FileSystemLabel -eq &#8216;WSUSOffline&#8217; }<\/p>\n<p style=\"margin-left:30px\">$Appname=&#8221;$($Drive.Driveletter):clientcmddoupdate.cmd&#8221;<\/p>\n<p style=\"margin-left:30px\">invoke-expression $appname<\/p>\n<p style=\"margin-left:30px\">stop-computer\nNow we access Hyper-V and get a list of all virtual machines that are presently offline with this simple loop:<\/p>\n<p style=\"margin-left:30px\">$VMlist=Get-VM | Where { $_.State &ndash;eq &#8216;Off&#8217; }<\/p>\n<p style=\"margin-left:30px\">Foreach ($VM in $VMlist)<\/p>\n<p style=\"margin-left:30px\">{<\/p>\n<p style=\"margin-left:30px\">}\nThe contents of the loop will look like the following. We will be running under the presumption that the first hard disk is the boot disk.<\/p>\n<p style=\"margin-left:30px\"># Get virtual machine Name<\/p>\n<p style=\"margin-left:30px\">$VMName=$VM.VMName<\/p>\n<p style=\"margin-left:30px\"># Get the location of the VHD<\/p>\n<p style=\"margin-left:30px\">$VMDiskPath=(Get-VM $VMName | Get-VMHardDiskDrive).Path<\/p>\n<p style=\"margin-left:30px\"># Mount the VHD and get it&#8217;s Drive letter<\/p>\n<p style=\"margin-left:30px\">Mount-DiskImage $VMDiskPath<\/p>\n<p style=\"margin-left:30px\">$DriveLetter=((Get-DiskImage $VMDiskpath | get-disk | get-partition | Where { $_.Type -eq &#8216;Basic&#8217; }).DriveLetter)+&#8221;:&#8221;<\/p>\n<p style=\"margin-left:30px\"># Then create a folder to hold our autolaunch Script<\/p>\n<p style=\"margin-left:30px\">$ScriptFolder=$DriveLetter+&#8221;ProgramDataScripts&#8221;<\/p>\n<p style=\"margin-left:30px\">New-Item $ScriptFolder -ItemType Directory -force<\/p>\n<p style=\"margin-left:30px\"># Copy over our little PowerShell script to trigger to update media<\/p>\n<p style=\"margin-left:30px\">Copy-Item &#8220;C:WsusofflineUpdatePC.PS1&#8221; $ScriptFolder\nNow for the fun part&#8230;\nWe&#8217;re going to connect to the remote software registry and adjust the settings. This involves capturing the original settings. We change them for the automatic log in and to disable the UAC. When the process is done, we revert them back:<\/p>\n<p style=\"margin-left:30px\"># Connect to the Registry remotely and grab some settings<\/p>\n<p style=\"margin-left:30px\">$RemoteReg=$DriveLetter+&#8221;WindowsSystem32configSoftware&#8221;<\/p>\n<p style=\"margin-left:30px\"># Load the remote file registry<\/p>\n<p style=\"margin-left:30px\">REG LOAD &#8216;HKLMREMOTEPC&#8217; $RemoteReg<\/p>\n<p style=\"margin-left:30px\"># Capture the original properties for Autologin<\/p>\n<p style=\"margin-left:30px\">$Key=&#8217;HKLM:REMOTEPCMicrosoftWindows NTCurrentVersionWinlogon&#8217;<\/p>\n<p style=\"margin-left:30px\">$Admin=(Get-ItemProperty $KEY -name AutoAdminLogon -ErrorAction SilentlyContinue).AutoAdminLogon<\/p>\n<p style=\"margin-left:30px\">$Domain=(Get-ItemProperty $KEY -name DefaultDomainName -ErrorAction SilentlyContinue).DefaultDomainName<\/p>\n<p style=\"margin-left:30px\">$Username=(Get-ItemProperty $KEY -name DefaultUserName -ErrorAction SilentlyContinue).DefaultUserName<\/p>\n<p style=\"margin-left:30px\">$Password=(Get-ItemProperty $KEY -name DefaultPassword -ErrorAction SilentlyContinue).DefaultPassword<\/p>\n<p style=\"margin-left:30px\"># Then pass in the new values which presume all<\/p>\n<p style=\"margin-left:30px\"># Offline VMs have the same ID and Password for the local<\/p>\n<p style=\"margin-left:30px\"># Admin account.<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name AutoAdminLogon &ndash;value 1 -force<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name DefaultDomainName -value &#8216;localhost&#8217; -force<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name DefaultUserName -Value &#8216;Administrator&#8217; -force<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name DefaultPassword -Value &#8216;P@ssw0rd&#8217; -force<\/p>\n<p style=\"margin-left:30px\"># We now do the same for UAC<\/p>\n<p style=\"margin-left:30px\"># Capture the old settings first<\/p>\n<p style=\"margin-left:30px\">$Key=&#8217;HKLM:REMOTEPCMicrosoftWindowsCurrentVersionPoliciesSystem&#8217;<\/p>\n<p style=\"margin-left:30px\">$ConsentAdmin=(Get-ItemProperty $KEY -name ConsentPromptBehaviorAdmin -ErrorAction SilentlyContinue). ConsentPromptBehaviorAdmin<\/p>\n<p style=\"margin-left:30px\">$ConsentUser=(Get-ItemProperty $KEY -name ConsentPromptBehaviorUser -ErrorAction SilentlyContinue). ConsentPromptBehaviorUser<\/p>\n<p style=\"margin-left:30px\">$LUA=(Get-ItemProperty $KEY -name EnableLUA -ErrorAction SilentlyContinue).EnableLUA<\/p>\n<p style=\"margin-left:30px\">$SecureDesk=(Get-ItemProperty $KEY -name PromptOnSecureDesktop -ErrorAction SilentlyContinue).PromptOnSecureDesktop<\/p>\n<p style=\"margin-left:30px\"># And now we (Quick everybody HIDE!)<\/p>\n<p style=\"margin-left:30px\"># Temporarily turn off UAC!<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name ConsentPromptBehaviorAdmin &ndash;Value 0<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name ConsentPromptBehaviorUser &ndash;Value 0<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name EnableLUA &ndash;Value 1<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name PromptOnSecureDesktop &ndash;Value 0<\/p>\n<p style=\"margin-left:30px\"># Capture the Current Execution Policy for PowerShell<\/p>\n<p style=\"margin-left:30px\">$Key=&#8217;HKLM:REMOTEPCMicrosoftPowershell1ShellidsMicrosoft.Powershell&#8217;<\/p>\n<p style=\"margin-left:30px\">$PowershellPolicy=(Get-ItemProperty $KEY -name ExecutionPolicy -ErrorAction SilentlyContinue). ExecutionPolicy<\/p>\n<p style=\"margin-left:30px\"># Set the Execution Policy to Bypass<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name ExecutionPolicy &ndash;Value &#8216;Bypass&#8217;<\/p>\n<p style=\"margin-left:30px\"># Finally we tell the remote computer at First run to execute the UpdatePC script.<\/p>\n<p style=\"margin-left:30px\">NEW-ITEMPROPERTY &#8220;HKLM:REMOTEPCMicrosoftWindowsCurrentVersionRun&#8221; -Name &#8220;PoshStart&#8221; -Value &#8220;`&#8221;C:windowsSystem32WindowsPowerShellv1.0powershell.exe`&#8221; -file C:ProgramDataScriptsUpdatePC.PS1&#8243;<\/p>\n<p style=\"margin-left:30px\"># Then disconnect the remote registry<\/p>\n<p style=\"margin-left:30px\">REG UNLOAD &#8216;HKLMREMOTEPC&#8217;<\/p>\n<p style=\"margin-left:30px\"># And now dismount the Disk<\/p>\n<p style=\"margin-left:30px\">dismount-diskimage $VMDiskPath\nWe need to attach the VHD to the virtual machine. We&#8217;ll do this by adding in a SCSI controller to our virtual machine and then add the VHD to that SCSI controller. We are only going to add in a new SCSI controller if none already exists.<\/p>\n<p style=\"margin-left:30px\"># Check for total SCSI Controllers<\/p>\n<p style=\"margin-left:30px\">$TotalSCSI=(GET-VMScsiController -vmname $VMName).count<\/p>\n<p style=\"margin-left:30px\">Get-VM &ndash;Vmname $VMName | Add-VMScsiController<\/p>\n<p style=\"margin-left:30px\"># Attach the Updates VHDX file<\/p>\n<p style=\"margin-left:30px\">Get-VM &ndash;Vmname $VMName | Add-VMHarddiskDrive &ndash;Controllertype SCSI &ndash;Path $VHDPath -ControllerNumber ($TotalScsi)\nNow that we&#8217;ve done all the heavy lifting, here&#8217;s the easy part. Start the virtual machine, wait for its first shutdown so we can detach the VHD, and then start it one final time to allow the updates to apply:<\/p>\n<p style=\"margin-left:30px\"># Start the virtual machine<\/p>\n<p style=\"margin-left:30px\">Start-VM -vmname $VMName<\/p>\n<p style=\"margin-left:30px\"># A slight for faster machines to ensure the virtual machine<\/p>\n<p style=\"margin-left:30px\"># State is passed back properly first<\/p>\n<p style=\"margin-left:30px\">Start-Sleep -seconds 60<\/p>\n<p style=\"margin-left:30px\"># Wait until the machine has pulled in the updates<\/p>\n<p style=\"margin-left:30px\"># and Stops when it&#8217;s done.<\/p>\n<p style=\"margin-left:30px\">Do { $status=(Get-VM &ndash;vmname $VMname).state } until ($status &ndash;match &#8216;Off&#8217;)\nNext we will detach the VHD file from the virtual machine to prevent it from continually trying to load the updates:<\/p>\n<p style=\"margin-left:30px\"># Detach Updates VHD from virtual machine<\/p>\n<p style=\"margin-left:30px\">Get-VM &ndash;Vmname $VMName | Remove-VMHarddiskDrive &ndash;Controllertype SCSI &ndash;Path $VHDPath<\/p>\n<p style=\"margin-left:30px\">If(!$TotalSCSI) { Remove-VMScsiController -vmname $VMname -ControllerNumber 0 }\nNow we ask Windows PowerShell to twiddle its thumbs (or maybe thumb through the TV to find some more Doctor Who) to wait for this machine to update. In this case, we&#8217;ll take a nap for a couple of hours. If your virtual machines are a bit more up-to-date, you can crank this down as you need.<\/p>\n<p style=\"margin-left:30px\">Start-VM &ndash;vmname $VMName<\/p>\n<p style=\"margin-left:30px\">Start-Sleep &ndash;seconds 7200; # 60 seconds in a minute, 60 minutes in an hour times 2<\/p>\n<p style=\"margin-left:30px\">Stop-VM &ndash;vmname $VMName\nAfter about two hours of rebooting and operating, all of the updates should have been applied, right? You HAVE been keeping your virtual machines up-to-date, right?\nOur next steps after finishing this round of updates are:<\/p>\n<ul>\n<li>Mount the virtual machine VHD file<\/li>\n<li>Restore all the settings in the virtual machine registry<\/li>\n<li>Remove the Windows PowerShell script we placed on the virtual machine<\/li>\n<li>Detach the VHD file<\/li>\n<\/ul>\n<p>Here&#8217;s our script:<\/p>\n<p style=\"margin-left:30px\"># Reconnect VHD to Host so we can clean the registry back up<\/p>\n<p style=\"margin-left:30px\">Mount-DiskImage $VMDiskPath<\/p>\n<p style=\"margin-left:30px\">$DriveLetter=((Get-DiskImage $VMDiskpath | get-disk | get-partition | Where { $_.Type -eq &#8216;Basic&#8217; }).DriveLetter)+&#8221;:&#8221;<\/p>\n<p style=\"margin-left:30px\"># Remove that PowerShell script<\/p>\n<p style=\"margin-left:30px\">$ScriptFolder=$DriveLetter+&#8221;ProgramDataScripts&#8221;<\/p>\n<p style=\"margin-left:30px\">Remove-Item $ScriptFolder -ItemType Directory &ndash;recurse -force<\/p>\n<p style=\"margin-left:30px\"># Connect to the Registry remotely and grab some settings<\/p>\n<p style=\"margin-left:30px\">$RemoteReg=$DriveLetter+&#8221;WindowsSystem32configSoftware&#8221;<\/p>\n<p style=\"margin-left:30px\"># Load the remote file registry<\/p>\n<p style=\"margin-left:30px\">REG LOAD &#8216;HKLMREMOTEPC&#8217; $RemoteReg<\/p>\n<p style=\"margin-left:30px\"># Restore the original properties for Autologin<\/p>\n<p style=\"margin-left:30px\">$Key=&#8217;HKLM:SoftwareMicrosoftWindows NTCurrentVersionWinlogon&#8217;<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name AutoAdminLogon &ndash;value $Admin -force<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name DefaultDomainName -value $Domain -force<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name DefaultUserName -Value $Username -force<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name DefaultPassword -Value $Password -force<\/p>\n<p style=\"margin-left:30px\"># We now do the same for UAC<\/p>\n<p style=\"margin-left:30px\"># Restore the old settings first<\/p>\n<p style=\"margin-left:30px\">$Key=&#8217;HKLM:SoftwareMicrosoftWindows NTCurrentVersionPoliciesSystem&#8217;<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name ConsentPromptBehaviorAdmin &ndash;Value $ConsentAdmin<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name ConsentPromptBehaviorUser &ndash;Value $ConsentUser<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name EnableLUA &ndash;Value $SilentlyContinue<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name PromptOnSecureDesktop &ndash;Value $SecureDesk<\/p>\n<p style=\"margin-left:30px\"># Restore the original Execution Policy<\/p>\n<p style=\"margin-left:30px\">$Key=&#8217;HKLM:REMOTEPCMicrosoftPowershell1ShellidsMicrosoft.Powershell&#8217;<\/p>\n<p style=\"margin-left:30px\">Set-ItemProperty $KEY -name ExecutionPolicy &ndash;Value $PowershellPolicy<\/p>\n<p style=\"margin-left:30px\"># Then Remove the autostart for the Script<\/p>\n<p style=\"margin-left:30px\">SET-ITEMPROPERTY &#8220;HKLM:REMOTEPCMicrosoftWindowsCurrentVersionRun&#8221; -Name &#8220;PoshStart&#8221; -Value $NULL<\/p>\n<p style=\"margin-left:30px\"># Then disconnect the remote registry<\/p>\n<p style=\"margin-left:30px\">REG UNLOAD &#8216;HKLMREMOTEPC&#8217;<\/p>\n<p style=\"margin-left:30px\"># And now dismount the Disk<\/p>\n<p style=\"margin-left:30px\">dismount-diskimage $VMDiskPath\nThere you have it. The interesting thing to consider is that you require no network access from any of these virtual machines for this to operate. If you play more with WSUS Offline Update, you&#8217;ll see that there are settings in the .ini files to automate more of its update process.\nOf course, depending on how far out-of-date those virtual machines are, you may have to repeat the process a few times. But because this is automatable, you can sit back with a nice glass of iced tea and watch back-to-back episodes of Serenity while it happens.\nKeep in mind that the media we created today only contains media for Windows&nbsp;8.1 and Windows Server&nbsp;2008&nbsp;R2. You can expand it to contain all of the currently supported operating systems and Microsoft Office suites. All you need to do is check more boxes.\nThis was a long read today; but of course, what a volume of information and neat tricks in Windows PowerShell we got to play with today! Think about it. Turning on a virtual machine and programming it to do something without network access&mdash;and then undoing all of that in the end.<span style=\"font-size:12px\">&nbsp;<\/span>\nWe invite you to follow us on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow.\n<b>Sean Kearney<\/b>, Microsoft PowerShell MVP and Honorary Scripting Guy<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft PowerShell MVP, Sean Kearney, talks about updating an offline virtual machine with Windows PowerShell and WSUS Offline Update. Honorary Scripting Guy, Sean Kearney, here. Yesterday, I introduced you to a tool you can use to updating offline virtual machines: Update Offline Virtual Machine with PowerShell and WSUS Offline Update: Part 1. Today, we&#8217;ll [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[56,271,3,154,572,130,45,380],"class_list":["post-7211","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-hyper-v","tag-scripting-guy","tag-sean-kearney","tag-server-side-update","tag-servers","tag-windows-powershell","tag-windows-update"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft PowerShell MVP, Sean Kearney, talks about updating an offline virtual machine with Windows PowerShell and WSUS Offline Update. Honorary Scripting Guy, Sean Kearney, here. Yesterday, I introduced you to a tool you can use to updating offline virtual machines: Update Offline Virtual Machine with PowerShell and WSUS Offline Update: Part 1. Today, we&#8217;ll [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/7211","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=7211"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/7211\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=7211"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=7211"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=7211"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}