Building a Lab in Hyper-V with PowerShell, Part 3
February 17th, 2017 by Charlie Russel
Creating VMs with New-myVM.ps1 – Part 2
So, as I showed in the previous post, I’ve got my new VM built, but it’s not really ready for use yet. For one thing, it needs a DVD attached and the boot order set, plus I want to add a second NIC, and change the number of processors assigned to it. First, setting up the memory, processors, static MAC address for the NIC and configuring the DVD if we’re booting from DVD. (Which, I admit, I don’t often do. Mostly I copy over a SysPrep’d VHDX file.)
To do this, I have a function, of course, called Set-myVMConfig, to do most of it, and a separate one that I use to configure the second NIC, Add-myNetAdapter
Function Set-myVMConfig { Write-Verbose "Setting Processor Count to 4 for $VMName" Set-VMProcessor -VMName $VMName -Count 4 Write-Verbose "Enabling Dynamic Memory" Set-VMMemory -VMName $VMName -DynamicMemoryEnabled $True Write-Verbose "Assigning static MAC address of $MacAdd" Get-VMNetworkAdapter -VMName $VmName ` | Set-VMNetworkAdapter -StaticMacAddress "$MacAdd" if ($DVD) { Write-Verbose "Building from DVD, so adding DVD drive, and configuring boot order" if (! $client) { Add-VMDvdDrive -VMName $VmName Set-VMDvdDrive -VMName $VmName -Path $ServerISO $vmDVD = Get-VMDvdDrive -VMName $VmName $vmDrive = Get-VMHardDiskDrive -VMName $VmName Set-VMFirmware -VMName $VmName -FirstBootDevice $vmDVD Set-VMFirmware -VMName $VmName -BootOrder $vmDVD,$vmDrive } else { Add-VMDvdDrive -VMName $VmName Set-VMDvdDrive -VMName $VmName -Path $ClientISO $vmDVD = Get-VMDvdDrive -VMName $VmName $vmDrive = Get-VMHardDiskDrive -VMName $VmName Set-VMFirmware -VMName $VmName -FirstBootDevice $vmDVD Set-VMFirmware -VMName $VmName -BootOrder $vmDVD,$vmDrive } } }
This sets the # of processors to 4, enables dynamic memory, sets a static MAC address on the first NIC, adds a DVD drive if appropriate, and sets the boot order to boot from the specified ISO file.
Almost done – now, all we need to do is add a second network adapter, and set it to a fixed MAC address as well.
Function Add-myNetAdapter { Write-Verbose "Adding second network adapter" Add-VmNetworkAdapter -VMName $VmName ` -SwitchName '199 Network' ` -StaticMacAddress "$199MacAdd" ` -Name '199 Ethernet' }
Now, that we have all the functions, all we need to do is execute them, and that all happens with:
If (! ( Get-VM -Name $VmName -ErrorAction Continue 2>$NULL) ) { Test-SourcePath Test-Clean Copy-myVHD -wait Write-Verbose "VHD's copied if we were doing that, now creating the VM..." Create-myVM Write-Verbose "VM Created" $myVM = Get-VM -VMName $VMName } Set-myVMConfig Add-myNetAdapter $myVM | Format-List
And, since this whole thing has been broken up across a couple of posts, here’s the whole script, including full Get-Help support.
New-myVM.ps1
<# .Synopsis Creates a new VM .Description New-myVM and New-myClientVM make a new VM of Name $1 and MAC Address in the $MacBase range of MAC addresses. If the command is run as New-myClientVM, then the -Client parameter is assumed unless overridden at the command line. .Example New-myVM -VMName Trey-DC-02 02 Creates a new Server VM of name "Trey-DC-02" in the default MAC address range with the "02" as the final octet of MAC address. .Example New-myVM -Name trey-client-22 -MacFinal 16 -DVD -Client $True Creates a new Client VM of name "trey-client-22" in the default MAC address range with 16 as the final octet of MAC address. The VM is installed from the default Server 2016 DVD. .Example New-myVM Trey-DC-02 02 -DVD Creates a new Server VM of name "Trey-DC-02" in the default MAC address range with the "02" as the final octet of MAC address. The VM is installed from the default Server 2016 DVD. .Example New-myVM -VmName "Trey-WDS-11" -MacFinal "0B" -MacBase "00-15-5D-32-64-" Creates a new Server VM of name Trey-WDS-11 in a non-default MAC address range. .Example New-myClientVM -Name trey-client-01 Creates a new Windows 10 client VM, 'trey-client-01' using the default VHD, and will prompt for the final 2 digits of the MAC address. .Example New-myVM -Name trey-client-01 -MACFinal 65 -Client $True -Source 'V:\Source' -Path 'Y:\' Creates a new Windows 10 client VM, 'trey-client-01' using the sysprep'd image at V:\Source, and creating the VM at Y:\trey-client-01. The final two digits of the MAC address will be 65. .Parameter VmName The name of the new VM .Parameter MacFinal The last two digits in the MAC address of the VM .Parameter MacBase The base MAC address for this VM. The default base is "00-15-5D-32-10-" .Parameter DVD A switch that controls whether a DVD is added to the VM and used to mount an ISO for the install. The default is to build the VM with no DVD drive. .Parameter Client A Boolean. When run as New-myVM, $Client defaults to False. If run as New-myClientVM, the default is true. In either case, the command line parameter overrides the default. .Parameter Path The target path for the virtual machine. Default is to V:\. This is the base path, to which the VMName is added to build the final path. .Parameter Source The source path of the DVD or VHD used to build the virtual machine. Default is V:\Source. .Parameter vmSwitch The Hyper-V network switch to connect the VM to. New-myVM creates two network adapters. One is connected to the 199 Network, and the second is controlled by the vmSwitch parameter. The default is "Local-10", the internal lab switch. .Parameter 2012R2 The 2012R2 switch specifies the use of the Server 2012 R2 image. .Inputs [string] [string] [string] [switch] [Boolean] [string] [string] [string] .Notes Author: Charlie Russel Copyright: 2017 by Charlie Russel : Permission to use is granted but attribution is appreciated ModHist: 1/1/2014 Initial : 1/31/2015 Mod for new parameter handling and comment header : 3/20/2015 Mod to use Sysprepped VHD and -10 MAC : 4/19/2015 Mod for verbose and running in a wrapper : 5/16/2016 Mod for new labhost : 9/24/2016 Mod for New-myClientVM : 12/21/2016 Added additional parameters, updated help. (cpr) : 02/17/2017 Fixed problem with DVD and Gen2, updated help. (cpr) #> [CmdletBinding()] Param ([Parameter(Mandatory = $True,Position = 0)][alias("Name")][string]$VmName, [Parameter(Mandatory = $True,Position = 1)][alias("Final")][string]$MacFinal, [Parameter(Mandatory = $False)][alias("Base")][string]$MacBase = "00-15-5D-32-0A-", [Parameter(Mandatory = $False)][string]$199MacBase = "00-15-5D-32-CE-", [Parameter(Mandatory=$False)][Switch]$DVD, [Parameter(Mandatory=$False)][Boolean]$Client=($myInvocation.myCommand.Name -match "Client"), [Parameter(Mandatory=$False)][alias("Target")][string]$Path = "V:", [Parameter(Mandatory=$False)][alias("VHDSource","DVDBase")][string]$Source = "V:\Source", [Parameter(Mandatory=$False)][alias("LocalSwitch","Network")][string]$vmSwitch = "Local-10", [Parameter(Mandatory=$False)][switch]$2012R2 ) $MacAdd = $MacBase + $MacFinal $199MacAdd = $199MacBase + $MacFinal Write-Verbose "MacFinal is $MacFinal" Write-Verbose "MacAdd is $MacAdd on switch $vmSwitch" Write-Verbose "VMName is $VMName" Write-Verbose "Client is $Client" Write-Verbose "Path is $Path, Source is $Source, and 199 MAC address is $199MacBase + $MacFinal" Write-Verbose "Sleeping for 5 seconds to give you a chance to exit..." sleep 5 $VMBase = "$Path\$VMName" $VHDSource = $Source $DVDBase = $Source $VHDBase = "$VMBase\Virtual Hard Disks" $SysVHD = "$VMBase\Virtual Hard Disks\$VmName-System.vhdx" $MachineBase= "$VMBase\Virtual Machines" $ServerISO = "$DVDBase\en_windows_server_2016_x64_dvd_9718492.iso" $ClientISO = "$DVDBase\en_windows_10_enterprise_version_1607_updated_jan_2017_x64_dvd_9714415.iso" $ClientVHD = "$Source\Generalized-client.vhdx" if ($2012R2) { $ServerVHD = "$Source\Generalized-2012R2.vhdx" } else { $ServerVHD = "$Source\Generalized-System.vhdx" } Function Test-SourcePath () { if ($Client) { if ($dvd) { if (Test-Path $ClientISO) { Write-Verbose "Install DVD found at $ClientISO" } else { Throw "Client ISO not found at $ClientISO" } } elseif (Test-Path $ClientVHD) { Write-Verbose "Source VHD found at $ClientVHD" } } else { if ($dvd) { if (Test-Path $ServerISO) { Write-Verbose "Install DVD found at $ServerISO" } else { Throw "Server ISO not found at $ServerISO" } } elseif (Test-Path $ServerVHD) { Write-Verbose "Source VHD found at $ServerVHD" } } } if (! (Test-Path $VHDBase ) ) { mkdir $VHDBase } if (! (Test-Path $MachineBase ) ) { mkdir $MachineBase } Function Test-Clean () { If (Test-Path $VHDBase\*.vhdx ) { Throw "Found an existing VHD. Please clean up the target path and try again." } } function Copy-myVhd () { if ( $DVD ) { Write-Verbose "DVD specified. Not copying source VHD to $SysVHD" } else { if ( $Client ) { Write-Verbose "Creating VM from Sysprep'd VHD base $ClientVHD" cp $ClientVHD $SysVHD } else { Write-Verbose "Creating VM from Sysprep'd VHD base $ServerVHD" cp $ServerVHD $SysVHD } } } function Create-myVM () { if ($DVD ) { Write-Verbose "Creating $vmname from DVD with the following command:" Write-Verbose "New-VM -Name $VmName -MemoryStartupBytes 1024MB -BootDevice VHD -Generation 2 -SwitchName $vmSwitch -NewVHDPath $SysVHD -NewVHDSize 200GB -Path $MachineBase " Sleep 3 New-VM -Name $VmName ` -MemoryStartupBytes 1024MB ` -BootDevice VHD ` -Generation 2 ` -SwitchName $vmSwitch ` -NewVHDPath $SysVHD ` -NewVHDSize 200GB ` -Path $MachineBase } else { New-VM -Name $VmName ` -MemoryStartupBytes 1024MB ` -BootDevice VHD ` -Generation 2 ` -SwitchName $vmSwitch ` -VHDPath $SysVHD ` -Path $MachineBase } } Function Set-myVMConfig { Write-Verbose "Setting Processor Count to 4 for $VMName" Set-VMProcessor -VMName $VMName -Count 4 Write-Verbose "Enabling Dynamic Memory" Set-VMMemory -VMName $VMName -DynamicMemoryEnabled $True Write-Verbose "Assigning static MAC address of $MacAdd" Get-VMNetworkAdapter -VMName $VmName ` | Set-VMNetworkAdapter -StaticMacAddress "$MacAdd" if ($DVD) { Write-Verbose "Building from DVD, so adding DVD drive, and configuring boot order" if (! $client) { Add-VMDvdDrive -VMName $VmName Set-VMDvdDrive -VMName $VmName -Path $ServerISO $vmDVD = Get-VMDvdDrive -VMName $VmName $vmDrive = Get-VMHardDiskDrive -VMName $VmName Set-VMFirmware -VMName $VmName -FirstBootDevice $vmDVD Set-VMFirmware -VMName $VmName -BootOrder $vmDVD,$vmDrive } else { Add-VMDvdDrive -VMName $VmName Set-VMDvdDrive -VMName $VmName -Path $ClientISO $vmDVD = Get-VMDvdDrive -VMName $VmName $vmDrive = Get-VMHardDiskDrive -VMName $VmName Set-VMFirmware -VMName $VmName -FirstBootDevice $vmDVD Set-VMFirmware -VMName $VmName -BootOrder $vmDVD,$vmDrive } } } Function Add-myNetAdapter { Write-Verbose "Adding second network adapter" Add-VmNetworkAdapter -VMName $VmName ` -SwitchName "199 Network" ` -StaticMacAddress "$199MacAdd" ` -Name "199 Ethernet" } If (! ( Get-VM -Name $VmName -ErrorAction Continue 2>$NULL) ) { Test-SourcePath Test-Clean Copy-myVHD -wait Write-Verbose "VHD's copied if we were doing that, now creating the VM..." Create-myVM Write-Verbose "VM Created" $myVM = Get-VM -VMName $VMName } Set-myVMConfig $myVM Add-myNetAdapter $myVM $myVM | Format-List
I hope you find this script useful, and I’d love to hear comments, suggestions for improvements, or bug reports as appropriate. As always, if you use this script as the basis for your own work, please respect my copyright and provide appropriate attribution.
Next up in the Building a Lab with PowerShell series will how to configure your DHCP server with PowerShell. This will take advantage of the fixed MAC addresses I create for all my lab machines and use these to populate DHCP Reservations.
Posted in Building Labs, Hyper-V, PowerShell, Windows 10, Windows Server, Windows Server 2016 | Comments Off on Building a Lab in Hyper-V with PowerShell, Part 3