With Microsoft’s new Windows Update strategy an Automated Build & Capture solution is highly recommended.
I did a post many years ago, but thought it would cool to write a new one that covers all the Hypervisors; Hyper-V G2, XenServer, VMware and Nutanix AHV.
In this setup we’re using Microsoft Deployment Toolkit and a fixed MAC Addresses to seamlessly start the correct Task Sequence. Please refer to the old post Automated Master Image Creation for setup and configuration.
Normally you would add the hypervisor connection details inside the actual PowerShell script, but since there will be one script per Operating System I prefer to use s Settings.xml file to store these settings. However in the case of the ISO image I’m overwriting that variable to use the other ISO.
Microsoft Hyper-V G2
Hyper-V is a bit more complex that I would like, but I guess some of you have some tips to improve this so we can get it 100% automated. At the moment I just paste the code into a RDP session. My problem is PSSession, InvokeCommand and Secure Boot. Anyhow just paste from line 15.
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 |
Write-Verbose "Setting Arguments" -Verbose $StartDTM = (Get-Date) $LogPS = "${env:SystemRoot}" + "\Temp\WS16RI-G2.log" Start-Transcript $LogPS $passwd = convertto-securestring -AsPlainText -Force -String P@ssw0rd $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "NUC08\Administrator",$passwd Write-Verbose "Customization" -Verbose Enter-PSSession "nuc08.ctxlab.local" -Credential $cred Start-Sleep -s 10 $Network = "Internal" $VMLoc = "C:\Virtual Hard Disks" $ISO = "C:\ISO\MDTBuildLab_x64.iso" $VMName = "WS16RI-G2" $MAC = "06:91:c9:1d:21:01" $VMGen = "2" $vCPU = "2" # Create Virtual Machines New-VM -Name $VMName -Path $VMLoc -Generation $VMGen -MemoryStartupBytes 2GB -NewVHDPath $VMLOC\$VMName.vhdx -NewVHDSizeBytes 32GB -SwitchName $Network Set-VMNetworkAdapter -StaticMacAddress $MAC -VMName $VMName # Configure Virtual Machines Add-VMDvdDrive -VMName $VMName -Path $ISO $DVD = Get-VMDvdDrive -VMName $VMName Set-VMFirmware -VMName $VMName -FirstBootDevice $DVD Set-VMProcessor -VMName $VMName -Count $vCPU Start-VM $VMName $VM = Get-VM -Name $VMName while ($VM.State -ne "off") { write-host "The VM is still running" sleep 30 } # Delete the VM Remove-VM -Name $VMName -Force Remove-Item -Recurse -Force $VMLOC\$VMName.vhdx #Stop $EndDTM = (Get-Date) Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalSeconds) Seconds" -Verbose Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose Stop-Transcript |
VMware ESXi
Prior to running the script you’ll need to download and install VMware PowerCli.
1 2 |
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force Install-Module -Name VMware.PowerCLI -Force |
Settings.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<POST> <VCenter>192.168.2.17</VCenter> <VCUser>root</VCUser> <VCPwd>P@ssw0rd</VCPwd> <VMDiskType>Thin</VMDiskType> <VMDS>NVMe</VMDS> <VMCluster>LAB</VMCluster> <VMFolder>DEV</VMFolder> <NICType>Vmxnet3</NICType> <NetName>Internal</NetName> <ISO>[SSD]\ISO\MDTProduction_x64.iso</ISO> <ESXi>192.168.2.17</ESXi> <VMGuestOS>windows9Server64Guest</VMGuestOS> </POST> |
Auto-WS16RI.ps1
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 |
Write-Verbose "Setting Arguments" -Verbose $StartDTM = (Get-Date) $MyConfigFileloc = ("Settings.xml") [xml]$MyConfigFile = (Get-Content $MyConfigFileLoc) $VCenter = $MyConfigFile.Post.VCenter $VCUser = $MyConfigFile.Post.VCUser $VCPwd = $MyConfigFile.Post.VCPwd $VMDiskType = $MyConfigFile.Post.VMDiskType $VMDS = $MyConfigFile.Post.VMDS $VMCluster = $MyConfigFile.Post.VMCluster $VMFolder = $MyConfigFile.Post.VMFolder $NICType = $MyConfigFile.Post.NICType $NetName = $MyConfigFile.Post.NetName $VMGuestOS = $MyConfigFile.Post.VMGuestOS $ISO = "[SSD]\ISO\MDTBuildLab_x64.iso" $ESXi = $MyConfigFile.Post.ESXi # ESXi Standalone Host when not using $VMCluster # Add Module Import-Module VMware.DeployAutomation # Connect to vCenter Server Connect-viserver $VCenter -user $VCUser -password $VCPwd -WarningAction 0 # WS16RI $VMName = "WS16RI" $MAC = "00:50:56:00:56:10" $VRAM = 2048 $VCPU = 2 $VMDiskGB = 60 $LogPS = "${env:SystemRoot}" + "\Temp\$VMName.log" Start-Transcript $LogPS New-VM -Name $VMName -VMHost $ESXi -numcpu $VCPU -MemoryMB $VRAM -DiskGB $VMDiskGB -DiskStorageFormat $VMDiskType -Datastore $VMDS -GuestId $VMGuestOS -NetworkName $NetName -CD 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 10 # 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" #$vm | Shutdown-VMGuest -Confirm:$false #Wait for Shutdown to complete do { #Wait 5 seconds Start-Sleep -s 5 #Check the power status $vm = Get-VM -Name $vmname $status = $vm.PowerState }until($status -eq "PoweredOff") } elseif ($vm.PowerState -eq "PoweredOff") { Write-Verbose "$vmname is powered down" } # Delete the VM Remove-VM $VMName -DeletePermanently -Confirm:$false #Stop $EndDTM = (Get-Date) Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalSeconds) Seconds" -Verbose Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose Stop-Transcript |
Citrix XenServer
Prior to running the script you’ll need to install XenCenter, download the XenServer SDK, extract it and copy .\XenServerPSModule to C:\Program Files\WindowsPowerShell\Modules.
You’ll also need to create a VM template called MDT Template 32GB with the MDTBuildLab_x64.iso but without a network interface.
Settings.xml
1 2 3 4 5 6 7 |
<POST> <XSMaster>nuc03.ctxlab.local</XSMaster> <XSUser>root</XSUser> <XSPwd>P@ssw0rd</XSPwd> <NetName>Internal</NetName> <ISO>MDTProduction_x64.iso</ISO> </POST> |
Auto-WS16RI.ps1
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 |
Write-Verbose "Setting Arguments" -Verbose $StartDTM = (Get-Date) $MyConfigFileloc = ("Settings.xml") [xml]$MyConfigFile = (Get-Content $MyConfigFileLoc) $XSMaster = $MyConfigFile.Post.XSMaster $XSUser = $MyConfigFile.Post.XSUser $XSPwd = $MyConfigFile.Post.XSPwd $Network = $MyConfigFile.Post.NetName $VMName = "WS16RI" $MAC = "06:91:c9:1d:21:01" $ISO = "MDTBuildLab_x64.iso" $LogPS = "${env:SystemRoot}" + "\Temp\$VMName.log" Start-Transcript $LogPS Write-Verbose "Customization" -Verbose import-module XenServerPSModule connect-xenserver -url http://$XSMaster -username $XSUser -password $XSPwd -SetDefaultSession # Create VM $Template = Get-XenVM -Name "MDT Template 32GB" Invoke-XenVM $Template -XenAction Clone -NewName $VMName -Async -PassThru | Wait-XenTask -ShowProgress Invoke-XenVM $VMName -XenAction Provision -Async -PassThru | Wait-XenTask -ShowProgress $VM = Get-XenVM -Name $VMName $NET = Get-XenNetwork -name $Network New-XenVIF -VM $VM -Network $NET -Device "0" -MAC $MAC CD "C:\Program Files (x86)\Citrix\XenCenter\" .\xe.exe -s $XSMaster -u $XSUser -pw $XSPwd vm-cd-eject vm=$VMName .\xe.exe -s $XSMaster -u $XSUser -pw $XSPwd vm-cd-insert cd-name=$ISO vm=$VMName Invoke-XenVM -VM $VM -XenAction Start function Test-XenVMRunning { param( [string]$VMName ) if ((Get-XenVM -Name $VMName ).power_state -eq 'running' ) { return $true } else { return $false } } do { #Wait 15 seconds Start-Sleep -Seconds 15 write-host "VM is still running" -Verbose }until(!(Test-XenVMRunning -VMName $VMName)) # Delete VM get-xenvm -Name ('$VMName' + '*') | Remove-xenvm -Name $VMName get-xenvdi -Name ('$VMName' + '*') | Remove-xenvdi -Name $VMName #Stop $EndDTM = (Get-Date) Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalSeconds) Seconds" -Verbose Write-Verbose "Elapsed Time: $(($EndDTM-$StartDTM).TotalMinutes) Minutes" -Verbose Stop-Transcript |
Nutanix AHV
Prior to running the script you’ll need to install the Nutanix Cmdlets.
Create a boot image that contains the Fedora Drivers. The Nutanix Virtio drivers doesn’t work with Windows PE.
Upload to ISO image
You will also need to connect through Putty to get the Network UUID.
1 2 |
Get-NTNXNetwork -> NetworkUuid Get-NTNXContainer -> ContainerName |
Settings.xml
1 2 3 4 5 6 7 |
<POST> <CVM>192.168.2.31</CVM> <User>admin</User> <Pwd>Brasil2017@</Pwd> <NetName>2d52c325-4098-46a3-a050-c3f6d43308d3</NetName> <ISO>MDTProduction_x64</ISO> </POST> |
Auto-WS16RI.ps1
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 |
Write-Verbose "Setting Arguments" -Verbose $StartDTM = (Get-Date) $MyConfigFileloc = ("Settings.xml") [xml]$MyConfigFile = (Get-Content $MyConfigFileLoc) $CVM = $MyConfigFile.Post.CVM $User = $MyConfigFile.Post.User $Pwd = $MyConfigFile.Post.Pwd $Password = ConvertTo-SecureString $Pwd -AsPlainText -Force $Network = $MyConfigFile.Post.NetName $ISO = "MDTBuildLab_x64" Write-Verbose "Connecting to CVM" -Verbose Import-Module "C:\Program Files (x86)\Nutanix Inc\NutanixCmdlets\Modules\NutanixCmdletsPSSnapin.dll" Add-PSSnapin nutanixcmdletspssnapin connect-ntnxcluster -server $CVM -username $User -password $Password -AcceptInvalidSSLCerts -ForcedConnection get-ntnxcluster # WS16MCS-MI-NTX $VMName = "WS16RI" $MAC = "50:6b:8d:1d:20:06" $VRAM = 2048 $VCPU = 2 New-NTNXVirtualMachine -Name $VMName -NumVcpus $VCPU -MemoryMb $VRAM ## Disk Creation - Setting the SCSI disk of 50GB on Containner ID 1025 (get-ntnxcontainer -> ContainerName) $diskCreateSpec = New-NTNXObject -Name VmDiskSpecCreateDTO $diskcreatespec.containerName = "default-container-81102177377574" $diskcreatespec.sizeMb = 51200 Start-Sleep -s 3 # Get the VmID of the VM $vminfo = Get-NTNXVM | where {$_.vmName -eq $VMName} $vmId = ($vminfo.vmid.split(":"))[2] # Set NIC for VM on default vlan (Get-NTNXNetwork -> NetworkUuid) $nic = New-NTNXObject -Name VMNicSpecDTO $nic.networkUuid = $Network $nic.macAddress = $MAC Add-NTNXVMNic -Vmid $vmId -SpecList $nic # Creating the Disk $vmDisk = New-NTNXObject –Name VMDiskDTO $vmDisk.vmDiskCreate = $diskCreateSpec # Mount ISO Image $diskCloneSpec = New-NTNXObject -Name VMDiskSpecCloneDTO $ISOImage = (Get-NTNXImage | ?{$_.name -eq $ISO}) $diskCloneSpec.vmDiskUuid = $ISOImage.vmDiskId #setup the new ISO disk from the Cloned Image $vmISODisk = New-NTNXObject -Name VMDiskDTO #specify that this is a Cdrom $vmISODisk.isCdrom = $true $vmISODisk.vmDiskClone = $diskCloneSpec $vmDisk = @($vmDisk) $vmDisk += $vmISODisk # Adding the Disk ^ ISO to the VM Add-NTNXVMDisk -Vmid $vmId -Disks $vmDisk # Power On the VM Set-NTNXVMPowerOn -Vmid $VMid |
The script above do miss the Power-State Loop, but I didn’t have time to finished that up. Please share if you figure it out.