Automate the deployment of guest virtual machines with PowerShell

Back to Blog

Automate the deployment of guest virtual machines with PowerShell

Small businesses often bemoan the cost and complexity of implementing System Center. But who needs System Center when you’ve already got PowerShell?! In the previous post, I shared a series of snippets / scripts that I use to quickly configure a standalone Hyper-V host, including host name & timezone, storage, networking, and so on. To continue that semi-automated deployment model, we will turn our attention in this post toward building guest virtual machines, and then configuring them.

Build Virtual Machines via PowerShell

You can deploy a single virtual machine quickly with the following snippet, just edit the variables to suit your needs. Here I have the variables setup for a typical Windows Server Essentials Experience deployment, with:

  • 2x CPU cores
  • 8 GB RAM (dynamic)
  • Small partition for Windows Server installation “C:\” (140 GB)
  • Large data partition for storing SYSVOL & file data (400 GB)

#This script deploys a single Gen 1 VM with dynamic RAM & disks
#Intended use: virtual Domain Controller & file server (e.g. Essentials Experience)

#Set these variables for your VM & VHD’s
$VMName= “SRVDC”
$RAM= 8GB
$SwitchName= “HVSwitch”
$CPUCount= 2
$VHDPath= “C:\Production\SRVDC-C.vhdx”
$VHDSize= 140GB
$DataVHDPath= “C:\Production\SRVDC-E.vhdx”
$DataVHDSize= 400GB

#Deploy the new virtual machine
New-VM -NewVHDPath $VHDPath -NewVHDSizeBytes $VHDSize -Generation 1 -MemoryStartupBytes $RAM -Name $VMName -SwitchName $SwitchName
Set-VM -Name $VMName -ProcessorCount $CPUCount

#Add a VHD for file/data partition
New-VHD -Path $DataVHDPath -SizeBytes $DataVHDSize -Dynamic
Add-VMHardDiskDrive –ControllerType SCSI -ControllerNumber 0 -VMName $VMName -Path $DataVHDPath

Note RE: Static RAM/Fixed Disks vs. Dynamic RAM/Dynamic Disks

For many workloads, dynamic RAM and disks are acceptable to use, however, if you are installing Exchange mailbox or SQL database servers on-premises, it is recommended that you deploy fixed style virtual disks and static RAM. Therefore when you build your VM’s, be sure to set the properties appropriately. For example:

New-VHD -Path $DataVHDPath -SizeBytes $DataVHDSize -Fixed
Set-VM -Name $VMName -StaticMemory

Note RE: Integration Services

I recommend disabling Time Synchronization with the host server, then enabling the primary Domain Controller to sync with an external NTP host. Therefore, you might add this line when building your VMs:

Disable-VMIntegrationService -Name “Time Synchronization” -VMName $VMName

From here you can deploy additional virtual machines, or begin installing guest operating systems.

Deploy VM’s from a template with PowerShell

Alternatively, you could deploy many virtual machines at once. This could be done with a function, as we saw before with configuring the host’s disks, and even combined with the Import-CSV cmdlet.

Let’s say you have a sysprep/templated VHD file that you’d like to copy and deploy over and over again for various VM’s. If you arranged a series of “parameters” (variables) in your script’s function, you could then import a small CSV file containing these parameters as fields, resulting in multiple VM’s based on a single VHD.  Example:

#This function builds a Gen2 virtual machine using a VHD template

function Make-NewVM {

param(
$VMName,
$RAM,
$CPU,
$DataVHDBytes,
$BasePath= “C:\Production”,
$SourceVHD= “C:\Templates\template.vhdx”
)

 

#Copy the VHD from template
New-Item -ItemType Directory -Path “$($BasePath)\$($VMName)”
Copy-Item -Path $SourceVHD -Destination “$($BasePath)\$($VMName)\$($VMName)-C.vhdx”

#Create the new virtual machine
New-VM -VHDPath “$($BasePath)\$($VMName)\$($VMName)-C.vhdx” -Generation 2 -MemoryStartupBytes $RAM -Name $VMName -SwitchName HVSwitch
Set-VM -Name $VMName -StaticMemory -ProcessorCount $CPU

#Add a VHD for data partition
New-VHD -Path “$($BasePath)\$($VMName)\$($VMName)-E.vhdx” -SizeBytes $DataVHDBytes -Dynamic
Add-VMHardDiskDrive –ControllerType SCSI -ControllerNumber 0 -VMName $VMName -Path “$($BasePath)\$($VMName)\$($VMName)-E.vhdx”

}

 

#Import parameters from a CSV file to build multiple virtual machines
Import-CSV MakeVMs.csv | FOREACH { Make-NewVM –VMName $_.VMName –RAM $_.RAM –CPU $_.CPU -DataVHDBytes $_.DataVHDBytes }

The CSV file (MakeVMs.csv) would be saved in the same working directory as the above script, and might look like this:

VMName,RAM,CPU,DataVHDBytes
SRVDC,2147483648,1,429496729600
SRVWEB1,4294967296,2,21474836480
SRVWEB2,4294967296,2,21474836480
SRVSQL,17179869184,4,214748364800

You can easily expand the function to include any number of other variables–attach additional VHD’s as data partitions, change between dynamic & static RAM, tweak integration services and so on. Have fun building your own version of this.

Windows Server 2016 & PowerShell Direct

In Windows Server 2016 you will have the ability to pass PowerShell commands directly into a guest virtual machine from the host server (running PowerShell as Administrator).  Which means you can script deployment of a Hyper-V host and its guest virtual machines from start to finish–all from the host server.

At the time of this writing, the WS2016 technical preview only supports Windows 10 / Windows Server 2016 Technical Preview guest virtual machines. We are hoping they add at least 2012 R2 guest support before RTM.

You have two choices for entering remote commands into your guest VM:

1. Interactive session

Enter-PSSession -VMName $VMName

This will open an interactive PowerShell session within the virtual machine, and you must supply administrative credentials for the guest VM. You would then be able to manually input commands such as “Rename-Computer -NewName VMName” and others, individually.

2. Non-interactive session

Invoke-Command -VMName $VMName -ScriptBlock { Commands }

Option 2 is non-interactive, allowing you to pass a script block into the virtual machine. You will still need to supply credentials for the guest VM. My suggestion would be to use this option if you wanted to quickly apply a series of configurations.

$VMName= “SRVWSE”
# optional # $VMVlan= “70” # e.g. to place VM on vlan 70
# optional # Set-VMNetworkAdapterVlan –VMName $VMName –Access –VlanId $VMVlan

Invoke-Command -VMName $VMName -ScriptBlock {

#Set time zone, rename computer, install the Essentials role
$TimeZone= “Central Standard Time”
$VMName= “SRVWSE”
TZUtil /s $TimeZone
Rename-Computer -NewName $VMName -Confirm:$False
Add-WindowsFeature ServerEssentialsRole

#Prepare the new disks / partitions
Get-Disk | Where-Object IsOffline -EQ $True | Set-Disk -IsOffline $False
$NewDisks= Get-Disk | Where-Object PartitionStyle -Eq RAW
Initialize-Disk -InputObject $NewDisks -PartitionStyle GPT
New-Partition -InputObject $NewDisks -UseMaximumSize
Get-Partition | Where-Object IsBoot -EQ $False | Where-Object Type -EQ Basic | Format-Volume -FileSystem NTFS -Confirm:$False

#Set the variables to define network settings
$IPAdd=”192.168.70.21″ # (e.g. “192.168.70.21”)
$Gateway=”192.168.70.1″ # (e.g. “192.168.70.1”)
$DNSAdd=”192.168.70.4″ # (e.g. “192.168.70.4”)

#Configure the network settings
New-NetIPAddress -InterfaceAlias “Ethernet” -IPAddress $IPAdd -PrefixLength 24 -DefaultGateway $Gateway
Set-DnsClientServerAddress -InterfaceAlias “Ethernet” -ServerAddresses $DNSAdd

#Set variables to join the domain
$Domain=”Company.local” # (e.g. “Company.local”)
$User=”hvadmin” # (e.g. “hvadmin”)
$OUPath=”OU=HyperV,DC=Company,DC=local” # (e.g. “OU=HyperV,DC=Company,DC=local”)

#Join the domain & restart the VM
Add-Computer -Credential $Domain\$User -DomainName $Domain -OUPath $OUPath
Restart-Computer

}

Conclusion

If you are a service provider of any kind, I believe it is an absolute crime not to be using this kind of approach, and thinking in this direction.  By standardizing and automating your deployments, you will save yourself and your customers valuable time & expense. Not to mention, you will eliminate room for mistakes and be able to replicate successful deployment models over and over again without risk of errors.

How do you think cloud systems are built? The auto-provisioning capabilities of a cloud are only possible with extreme standardization and that is achieved largely through the use of scripts. You might not be a cloud provider, but that doesn’t mean you cannot realize some of the benefits of their tools and methodology. PowerShell is included with Windows Server at no charge, which makes it perfect for the small to mid-sized business market. Therefore, do not be afraid to get your hands dirty. Once you get started, you’ll be glad you did.

Leave a Reply

Back to Blog

Helping IT Consultants Succeed in the Microsoft Cloud

Have a Question? Contact me today.