Building VM Templates for your preferred Hypervisor can take a lot of time and also involves a couple of manual steps. Most importantly, when was the last time you updated your VM Templates?
Templates are like tomatoes, they’re rotten after just a couple of days. Even though you have Windows Updates being applied, the deployment time will get slower and slower each and every day.
Screenshot below Windows 2012 R2 Vanilla ISO, missing only 166 updates!
Therefore in a perfect world you should use a deployment solution like MDT or SCCM to deploy VMs instead of templates. You should also Automate Your Reference Image Creation.
Unfortunately we don’t live in a perfect world so today I’m going to show you how to Build VM Templates Automatically On a Schedule with the latest Windows Updates.
In the following example I’m going to use VMware to build VM Templates, but the same applies to Hyper-V and XenServer.
Download and install vSphere PowerCli.
I’m using the free Microsoft Deployment Toolkit (MDT) with my Automation Framework on top to create the VM that later gets converted to a template.
Pick a unique MAC Address that’s going to be used by this VM. This is required for MDT to work 100% Zero Touch. Here’s my CustomSettings.ini
1 2 3 4 5 6 7 8 9 10 |
[Settings] Priority=MACAddress, ByVM, UUID, Default [00:50:56:00:56:03] WindowsUpdate=True SkipTaskSequence=YES SkipComputerName=YES TaskSequenceID=WS2012-017 SkipFinalSummary=YES FinishAction=SHUTDOWN |
This tells MDT that it will automatically start the Task Sequnce named WS2012-017 and shut down the VM when finished.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# This script configures the VMware virtual machines # PowerShell 3.0 and Windows Server 2012 or Windows 8 Pro are required to perform this setup. # PowerCLI is required to perform this setup - basic premise borrowed from Magnus Andersson RTS # Variables $Date = Get-Date -Format g $VCenter = "10.110.88.10" # vCenter Server $VCenterUser = "vsphere.local\administrator" # vCenter Server username $VCenterUserPassword = "Brasil2015@" # vCenter Server user password $VMName = "WS12R2 Template $Date" # Name of VM running Client Operating System $VRAM = 4096 # MB RAM assigned to Client Operating System $VCPU = 2 # vCPU assigned to Client Operating System. Recommend 2 vCPUs for Build $VMDiskGB = 40 # Size of Hard-Drive in GB for Client Operating System $VMDiskType = "Thin" # Set guest disk type to thin provision $VMDS = "Intel750" # Datastore #$VMCluster = "LAB" # Optionally determine in which cluster to create the VM #$VMFolder = "Folder" # Optionally determine in which folder to create the VM $VMGuestOS = "windows8Server64Guest" # Set Guest OS to Windows Server 2012 R2 $NICType = "Vmxnet3" # Set NIC to type VMXnet3 $NetName = "Internal" # Name of the Network Switch $MAC = "00:50:56:00:56:03" # Set the MAC. This MUST match the CustomSettings.ini, and MUST be within VMW's MAC Address pool. $ISO = "[Samsung850EVO]\ISO\MDTProduction_x64.iso" # Set the ISO $ESXi = "10.110.88.9" # ESXi Standalone Host when not using $VMCluster # Start $StartDTM = (Get-Date) # Connect to vCenter Server Connect-viserver $VCenter -user $VCenterUser -password $VCenterUserPassword -WarningAction 0 # Randomly choose ESXi host on which to create the VM # $ESXi=Get-Cluster $VMCluster | Get-VMHost -state connected | Get-Random # Create Virtual Machine New-VM -Name $VMName -VMHost $ESXi -numcpu $VCPU -MemoryMB $VRAM -DiskGB $VMDiskGB -DiskStorageFormat $VMDiskType -Datastore $VMDS -GuestId $VMGuestOS -NetworkName $NetName -CD # Configure Virtual Machine Get-VM $VMName | Get-NetworkAdapter | Set-NetworkAdapter -Type $NICType -MacAddress $MAC -StartConnected:$true -Confirm:$false Get-VM $VMName | Get-CDDrive | Set-CDDrive -ISOPath $ISO -StartConnected:$true -Confirm:$false Start-VM -VM $VMName -confirm:$false -RunAsync Start-Sleep -s 30 # Waiting for VM to Shutdown after MDT Deployment # http://powershell.com/cs/media/p/17924.aspx $vm = get-vm -Name $vmname if ($vm.PowerState -eq "PoweredOn") { Write-Verbose "VM is still running" do { #Wait 5 seconds Start-Sleep -s 5 Write-Verbose "VM is still running" #Check the power status $vm = Get-VM -Name $vmname $status = $vm.PowerState }until($status -eq "PoweredOff") } # Eject CD Rom and Set Automated Mac Address Get-VM $VMName | Get-CDDrive | Remove-CDDrive -Confirm:$false # Convert VM to Template # Set-VM $VMName -ToTemplate -Confirm:$false #Stop $EndDTM = (Get-Date) Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalSeconds) Seconds" -Verbose Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose Write-Verbose "Open vCenter and change from Manual to Automatic Mac Address & Convert to Template" -Verbose |
My Powershell script will constantly check the VMs Powered State.
6.5 minutes to build a Windows 2012 R2 template with all the latest Windows Updates. This is only possible if you follow the steps in the post Automate Your Reference Image Creation.
Note: I’m new to vSphere Powercli and haven’t found a way to switch from Manual to Automatic MAC Address. Until then you need to do this manually and also do Sysprep if you don’t automate that part.
Convert the VM to a Template inside of vCenter.
Now just create a Schedule Task and you get it all running, almost automatically.
for the mac: Function Set-NetworkAdapterMacAddressGenerated {
param([parameter(mandatory=$true,ValueFromPipeLine=$true)]
[VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.NetworkAdapter]$NetworkAdapter)
process {
$vm = $NetworkAdapter.Parent
$nicName = $NetworkAdapter.Name
$macType = “generated”
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$dev = New-Object VMware.Vim.VirtualDeviceConfigSpec
$dev.operation = “edit”
$vm.ExtensionData.Config.Hardware.Device |
Where-Object {$_.DeviceInfo.Label -eq $nicName} |
ForEach-Object {
$dev.device = $_
$dev.device.addressType = $macType
$dev.device.MacAddress = $null
}
$spec.DeviceChange += $dev
$vm.ExtensionData.ReconfigVM($spec)
$vm | Get-NetworkAdapter | Where-Object {$_.Name -eq $nicName}
}
}
Thanks Beeper