During last year’s VMworld #NotSupported sessions one of the hot topics was William Lam‘s vInception talk. “Nested ESXi” has since then become indispensable in the homelab of everyone tinkering with virtualisation !
As a follow-up to that session, William posted several blog posts on the subject. You can find them all in a handy overview.
But as much as I like his clear instructions on how to set up nested ESXi, I wanted to automate the process 🙂 In my homelab I create, and remove, ESXi VMs on a regular basis. So with the “If you do it more than once, automate it” in mind, I decided to create a function for the process.
The Script
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
function New-NestedHypervisor { <# .SYNOPSIS Create a nested hypervisor .DESCRIPTION The function will create a VM that is configured to run a nested hypervisor .NOTES Author: Luc Dekens .PARAMETER Name The name of the VM that will be created .PARAMETER VMHost The ESXi server where the VM shall be created .PARAMETER DiskGB The size of the virtual disk assigned to the VM .PARAMETER NumCpu The number of vCPU allocated to the VM .PARAMETER MemoryGB The amount of memory to be allocated to the VM .PARAMETER Portgroup An array of VirtualPortgroupBase objects. For each entry a NIC will be assigned to the VM, and it will be connected to the portgroup .PARAMETER CD A switch to define if a CD/DVD shall be created on the VM .PARAMETER ISOPath If a CD is allocated to the VM, this variable allows to specify an ISO file that will be connected. .PARAMETER ESXi A switch indicating if the VM shall be configured for a nested ESXi hypervisor .PARAMETER HyperV A switch indicating if the VM shall be configured for a nested Hyper-V hypervisor .PARAMETER Force If a VM with the same Name already exists then setting this switch to $True will remove it before creating the new VM. .EXAMPLE PS> New-NestedHypervisor @splattedParameters #> [CmdletBinding(DefaultParameterSetName = "ESXi")] Param( [Parameter(Mandatory=$true)] [string]$Name, [PSObject]$VMHost, [int]$DiskGB, [int]$NumCpu, [int]$MemoryGB, [VMware.VimAutomation.ViCore.Impl.V1.Host.Networking.VirtualPortGroupImpl[]]$Portgroup, [switch]$CD, [string]$ISOPath, [Parameter(Mandatory=$true,ParameterSetName = "ESXi")] [switch]$ESXi, [Parameter(Mandatory=$true,ParameterSetName = "Hyper-V")] [switch]$HyperV, [switch]$Force ) process { # Simple VMHost OBN if($VMHost -is [System.String]){ try { $VMHost = Get-VMHost -Name $VMHost } catch { Write-Error "Invalid hostname $VMHost" return } } # Check this is on a ESXi 5.1 ESXi if($VMHost.ApiVersion -ne "5.1"){ Write-Error "Host $($VMHost).Name doesn't seem to be an ESXi 5.1" return } # Check if nested hypervisor is allowed if(!$VMHost.ExtensionData.Capability.NestedHVSupported){ Write-Error "Host $($VMHost).Name doesn't seem to support HVT" return } # Check if the VM already exists. If yes, remove it $hv = Get-VM -Name $Name -ErrorAction SilentlyContinue if($hv){ if($Force){ if($hv.PowerState -eq "PoweredOn"){ Stop-VM -VM $hv -Confirm:$false } Remove-VM -VM $hv -DeletePermanently -Confirm:$false } else{ Write-Error "There is already a VM called $Name present" return } } # Create the VM $osId = "vmkernel5Guest" if($HyperV){ $osId = "windows8Server64Guest" } $hvParams = @{ Name = $Name VMHost = $VMHost GuestId = $osId Version = "v9" DiskGB = $DiskGB NumCpu = $NumCpu MemoryGB = $MemoryGB Portgroup = $Portgroup CD = $CD } $hv = New-VM @hvParams # Boot order (CD) & nested enabled $spec = New-Object VMware.Vim.VirtualMachineConfigSpec if($CD){ $spec.BootOptions = New-Object VMware.Vim.VirtualMachineBootOptions $spec.BootOptions.bootOrder += New-Object VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice } $spec.nestedHVEnabled = $true if($HyperV){ $vmxEntry = New-Object VMware.Vim.OptionValue $vmxEntry.Key = "hypervisor.cpuid.v0" $vmxEntry.Value = "FALSE" $spec.ExtraConfig += $vmxEntry } try { $hv.ExtensionData.ReconfigVM($spec) } catch { "Reconfig failed" return } # Mount the ISO if($CD -and $ISOPath){ Get-CDDrive -VM $hv | Set-CDDrive -IsoPath $ISOPath -StartConnected $true -Confirm:$false | Out-Null } # Return the newly created VM Get-VM -Name $Name } } |
Annotations
Line 60-68: A rather simple implementation of the Object By Name concept for the VMHost parameter.
Line 71-74: A test to verify that the function is running against an ESXi 5.1. Note that the function doesn’t test on “5.1 or higher“, the reason is that the concept in a future ESXi build may change.
Line 77-80: The function verifies that the ESXi server passed in VMHost supports Virtual Hardware-Assisted Virtualization (VHV).
Line 83-95: When a VM with the same Name already exists, the function will return unless the Force parameter is used.
Line 98: Contrary to the procedure to be used with the Web Client, we can immediately select the ESXi guestId.
Line 102-111: The “splatted” New-VM parameters
Line 116-119: If the CD switch was set to $True, the function will set the VM to boot from the CD/DVD.
Line 121-126: In case the HyperV switch was set to $True, the function needs to add the “hypervisor.cpuid.vo = FALSE” entry to the VMX file.
Line 137-141: If the CD switch was set to $True and an ISOPath was given, the function will connect the CD/DVD drive to the ISO.
Sample Usage
With the function you can now automate the creation of a number of nested hypervisors on your homelab server.
In the following example I create 3 VMs that will run nested ESXi hypervisors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$esx = Get-VMHost -Name MyLab $pgName = "Internal" $pgObj = Get-VirtualPortGroup -Name $pgName $pg = 1..4 | %{$pgObj} $nestedParams = @{ Name = $null VMHost = $esx DiskGB = 8 NumCpu = 2 MemoryGB = 8 Portgroup = $pg CD = $true ISOPath = "[datastore1] ISO/VMware-VMvisor-Installer-5.1.0-799733.x86_64.iso" ESXi = $true } 1..3 | %{ $nestedParams.Name = "ESX$($_)" $newesx = New-NestedHypervisor @nestedParams Start-VM -VM $newesx -Confirm:$false } |
In a bit more than 1 minute my 3 nested ESXi VMs have been created, and are ready to be installed. Hardly enough time to get me a decent Belgian beer from the fridge 🙂
And the same can of course be done for the creation of a nested Hyper-V hypervisor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$esx = Get-VMHost -Name MyLab $pgName = "Internal" $pgObj = Get-VirtualPortGroup -Name $pgName $pg = 1..4 | %{$pgObj} $nestedParams = @{ Name = "HV1" VMHost = $esx DiskGB = 8 NumCpu = 2 MemoryGB = 8 Portgroup = $pg CD = $false HyperV = $true } $hv1 = New-NestedHypervisor @nestedParams |
Enjoy !
Mayur
Hi Luc-
I have been using your great script in my ESXi home training lab for quite a while. It has proved to be very useful and as made it so easy for me to deploy nested ESXi VM’s on demand saving a lot of time manually configuring the custom parameters not to mention clicking!
I tried to customize it to modify the script to do the following :
(a) thin provision the disk
(b) provide a datastore path
(c) add a Resource Pool name
Due to my somewhat limited POSH scripting abilities, I seem to have hit a small road block and hope you can provide me with some help on how I could modify the script to do the above.
Many thanks
Mayur
Manoj
Hi Luc, came across your very interesting blog which has some really cool scripts. I have to say you are a pro when it comes to automating. Whereas me I am a total novice when it comes to creating scripts from scratch but generally get by taking examples and modifying here and there to get by.
When I try to add the Force=true in order to check for existing machines, I get errors:
“Start-VM : 12/5/2013 4:15:34 PM Start-VM The object has already been deleted or has not been completely created”
I can see that the existing VMs if powered ON are powered OFF and deleted, what I notice is for a few seconds it displays as an orphaned vm and immediately a new VM with the same name is created and I think the above error is caused when it tries to power ON.
I was not sure where to put the Force = true in the below section: Is this where it should go??
Thanks!
$nestedParams = @{
Force = $true
Name = $null
VMHost = $esx
DiskGB = 8
NumCpu = 2
MemoryGB = 8
Portgroup = $pg
CD = $false
ISOPath = “[datastore1] ISO/VMware-VMvisor-Installer-5.1.0-799733.x86_64.iso”
ESXi = $true
MattBoren
Howdy, Luc-
Great function, thanks for sharing! One thing: the “VMHost” key/value seems to be missing from the $hvParams hashtable (starting at line 102). Without it, the subsequent New-VM call is sad (talks about, ‘Please specify at least one of the following parameters: “ResourcePool”, “VMHost” or “VApp” ‘).
Anyway, thanks again — very useful.
Matt
admin
Thanks for noticing that Matt, I corrected the code.