New-WindowsImage -size small | Test-Lab
One of the time consuming steps to deploying new VMs is the time spend managing Images and and applying patches. I’m not big on Golden images. I tend to use a fully patched VHDX or VMDK and let DSC handle the configuration and software. This is not the fastest, and at scale you need to create more then one image based on what saves the most time. (IIS, SQL, Exchange, etc…). In this series, I’m going to go over how I create a a baseline image of 2012r2 with PowerShell. Also because Install-WindowsFeature has issues when the target VM has been patched, we are also going to create a fully patched WIM to use as -source.
Blogs in this Series
- Part 1 VHDX from ISO
- Part 2 Patching and Cleanup via PowerShell
- Part 3 SysPrep and Compacting Images
- Part 4 Bringing it all together with automation
Patching and Cleanup via PowerShell
Patching The Core Image
Starting with our Core image we are going to Patch and clean up extra files. At the command prompt I’m going to type: start Powershell Now I could use sconfig to patch, but we are doing this via PowerShell. Using James Oniel’s blog as a reference. I have cut the code down to just the needed commands because the paste keystrokes has a limited size. And sometimes opening a fresh VM does not give me the options for an Advanced Session. I’m going to take the code below and “Paste from clipboard” into PowerShell to download the all patches, including optional patches
$Criteria = "IsInstalled=0 and Type='Software'" $updateSession = New-Object -ComObject 'Microsoft.Update.Session' $updates = $updateSession.CreateupdateSearcher().Search($Criteria).Updates $downloader = $updateSession.CreateUpdateDownloader() $downloader.Updates = $updates "Downloading $($downloader.Updates.count) updates" $Result = $downloader.Download() if (($Result.Hresult -eq 0) -and (($Result.resultCode -eq 2) -or ($Result.resultCode -eq 3)) ) { $updatesToInstall = New-Object -ComObject 'Microsoft.Update.UpdateColl' $updates | Where-Object -FilterScript { $_.isdownloaded } | ForEach-Object -Process { $null = $updatesToInstall.Add($_) } $installer = $updateSession.CreateUpdateInstaller() $installer.Updates = $updatesToInstall "Installing $($installer.Updates.count) updates" $installationResult = $installer.Install() $Global:counter = -1 $installer.updates | Format-Table -AutoSize -Property Title, EulaAccepted, @{ label = 'Result' expression = { $resultcode[$installationResult.GetUpdateResult($Global:counter++).resultCode ] } } } $installationResult
This takes a while. Once it’s done I’m going to reboot the VM and paste the script again until there are 0 updates. It can take 2-3 times depending on the age of your ISO.
Restart-Computer
Once we have updated Core with every patch, it’s time to remove unneeded file. If you going install any features, special software or setup boot time scripts. now is the time to do so. I’m not going to this time. I will start by removing the source file for adding windows features. We will be creating a WIM from our GUI image for that purpose.
Get-WindowsFeature | where-object{$_.Installed -eq 0 -and $_.InstallState -eq 'Available'} | uninstall-windowsfeature -remove dism /online /cleanup-image /StartComponentCleanup /ResetBase defrag c: /UVX
- First we removes the source from SxS
- Second we remove any “superseded” items in SxS. So any older patches that have been updated with a newer one will be removed.
- Third, we defrag the drive and consolidate free space (this probably is not necessary)
Preparing and Patching the GUI Image
With that done lets move to the GUI VM. I’m going to first make sure every feature is available
get-windowsfeature | Where InstallState -eq Removed
Display Name Name Install State ------------ ---- ------------- [ ] .NET Framework 3.5 (includes .NET 2.0 and 3.0) NET-Framework-Core Removed [ ] Windows PowerShell 2.0 Engine PowerShell-V2 Removed
So .NET 3.5 and PowerShell 2.0 are removed. If your never going to use them that is ok. I find that .NET 3.5 is frequently needed so to be safe I’m going to add them both to SxS.
get-windowsfeature | Where InstallState -eq Removed | Install-WindowsFeature -Source D:\sources\sxs
Success Restart Needed Exit Code Feature Result ------- -------------- --------- -------------- True No Success {.NET Framework 3.5 (includes .NET 2.0 and... WARNING: Windows automatic updating is not enabled. To ensure that your newly-installed role or feature is automatically updated, turn on Windows Update.
Because we want the WIM to be a source when adding to VM’s created from our Core Image, and we want the feature to be patched from the get-go. They need to be installed and patched on this VM. If they are not all installed, that is OK, it will still work as a source so long as the installed features in the WIM are the same version or newer then the VM your adding the feature to.So if we don’t mind patching the new items after the fact when we can just remove the GUI, patch and move on. I’m going to keep the GUI, and add some features I know I will be using in my lab. (now before you go and think I’ll just run Get-WindowsFeature | Add-WindowsFeature, that does not work because some features are mutually exclusive)
Install-WindowsFeature DNS, DHCP, File-Services, RSAT -IncludeAllSubFeature Install-WindowsFeature AD-Domain-Services, AD-Certificate, ADCS-Cert-Authority
There will be a few warnings but everything should install Now I’m going to use the same code used on the Core VM to run updates. Once that is patched and shutdown it’s ready for Part 3.