As the Product Manager for Online Demos, I need to install the SolarWinds Orion platform frequently... sometimes as much as 4 times per month. This can get tiresome, but I've gotten some assistance from PowerShell, the community, and some published help documents.
I've thought about scripting these out for a while now and I came up with a list of things to do.
- Build the virtual machines
- Pre-configure the virtual machine's disks
- Prep the machine for installation
- Install the software (silently)
- Finalize the installation (silently)
This post is the first step in this multi-step process - Building your virtual machine.
Now dependent on your hypervisor there are two different paths to follow: Hyper-V or VMware. In my lab, I've got both because I try to be as agnostic as possible. It's now time to start building the script. I'm going to use PowerShell.
Scripting Preference: PowerShell
Preference Reasoning: I know it and I'm comfortable using it.
Hyper-V vs. VMware
Each Hypervisor has different requirements when building a virtual machine, but some are the same for each - specifically the number & size of disks, the CPU count and the maximum memory. The big deviation comes from the way that each hypervisor handles memory & CPU reservations.
Hyper-V handles CPU reservation as a percentage of total whereas VMware handles is via the number of MHz. I've elected to keep the reservation as a percentage. It seemed easier to keep straight (in my head) and only required minor tweaks to the script.
Step 1 - Variable Declaration
- VM Name [string] - both
- Memory (Max Memory for VM) [integer] - both
- CPU Count (number of CPUs) [integer] - both
- CPU Reservation (percentage) [integer] - both
- Disk Letters and Sizes - both
- Memory at Boot (Memory allocated at boot) [integer] - Hyper-V
- Memory (Minimum) [integer] - Hyper-V
- Use Dynamic Disks [Boolean] - Hyper-V
- VLAN (VLAN ID to use for Network Adapter) [integer] - Hyper-V
- vCenter Name [string] - VMware
- ESX Host [string] - VMware
- Disk Format ("thin", "thick", etc.) [string] - VMware
- VLAN (VLAN name to use for Network Adapter) [string] - VMware
- Guest OS (identify the Operating System) [string] - VMware
Step 2 - Build the VM
Building the VM is an easy step that you actually only takes 1 line using the "New-VM" command (regardless of Hypervisor). The syntax and parameters change depending on the hypervisor, but otherwise we just build the shell. In Hyper-V, I do this in two commands and in VMware I do it in one.
Step 3 - Assign Reservations
This is a trickier step in VMware because it uses MHz and not percentages. For that I need to know what the MHz of the processor in the host is running. Thankfully, this can be calculated pretty easily. Then I just set the CPU & Memory reservations based around each hypervisor's requirements
Step 4 - Assign VLAN
Hyper-V uses the VLAN ID (integer) and VMware uses the VLAN Name (string). It's nearly the same command with just a different parameter.
Step 5 - Congratulate yourself.
Hyper-V | VMware |
---|
 |  |
Execution Time: 9 seconds on either architecture.
Time saved: at least 10 minutes.
The full script is below.
#region Variable Declaration
$VMName = "OrionServer" # Virtual Name
$Architecture = "Hyper-V" # (or "VMware")
# Global Variable Declaration
$CPUCount = 4 # Number of CPU's to give the VM
$CPUReserve = 50 # Percentage of CPU's being reserved
$RAMMax = 16GB # Maximum Memory
# Sizes and count of the disks
$VHDSizes = [ordered]@{ "C" = 40GB; # Boot
"D" = 30GB; # Page
"E" = 40GB; # Programs
"F" = 10GB; # Web
"G" = 10GB; # Logs
}
#endregion
# Architecture-specific commands
if ( $Architecture -eq "Hyper-V" )
{
$RAMBoot = 8GB # Startup Memory
$RAMMin = 8GB # Minimum Memory (should be the same as RAMBoot)
$DynamicDisks = $true # Use Dynamic Disks?
$Vlan = 300 # VLAN assignment for the Network Adapter
# Assume that we want to make all the VHDs in the default location for this server.
$VHDRoot = Get-Item -Path ( Get-VMHost | Select-Object -ExpandProperty VirtualHardDiskPath )
# Convert the hash table of disks into PowerShell Objects (easier to work with)
$VHDs = $VHDSizes.Keys | ForEach-Object { New-Object -TypeName PSObject -Property ( [ordered]@{ "ServerName" = $VMName; "Drive" = $_ ; "SizeBytes" = $VHDSizes[$_] } ) }
# Extend this object with the name that we'll want to use for the VHD
# My naming scheme is [MACHINENAME]_[DriveLetter].vhdx - adjust to match your own.
$VHDs | Add-Member -MemberType ScriptProperty -Name VHDPath -Value { Join-Path -Path $VHDRoot -ChildPath ( $this.ServerName + "_" + $this.Drive + ".vhdx" ) } -Force
# Create the VHDs
$VHDs | ForEach-Object {
if ( -not ( Test-Path -Path $_.VHDPath -ErrorAction SilentlyContinue ) )
{
Write-Verbose -Message "Creating VHD at $( $_.VHDPath ) with size of $( $_.SizeBytes / 1GB ) GB"
New-VHD -Path $_.VHDPath -SizeBytes $_.SizeBytes -Dynamic:$DynamicDisks | Out-Null
}
else
{
Write-Host "VHD: $( $_.VHDPath ) already exists!" -ForegroundColor Red
}
}
#region Import the Hyper-V Module & Remove the VMware Module (if enabled)
# This is done because there are collisions in the names of functions
if ( Get-Module -Name "VMware.PowerCLI" -ErrorAction SilentlyContinue )
{
Remove-Module VMware.PowerCLI -Confirm:$false -Force
}
if ( -not ( Get-Module -Name "Hyper-V" -ErrorAction SilentlyContinue ) )
{
Import-Module -Name "Hyper-V" -Force
}
#endregion Import the VMware Module & Remove the Hyper-V Module
# Step 1 - Create the VM itself (shell) with no Hard Drives to Start
$VM = New-VM -Name $VMName -MemoryStartupBytes $RAMBoot -SwitchName ( Get-VMSwitch | Select-Object -First 1 -ExpandProperty Name ) -NoVHD -Generation 2 -BootDevice NetworkAdapter
# Step 2 - Bump the CPU Count
$VM | Set-VMProcessor -Count $CPUCount -Reserve $CPUReserve
# Step 3 - Set the Memory for the VM
$VM | Set-VMMemory -DynamicMemoryEnabled:$true -StartupBytes $RAMBoot -MinimumBytes $RAMMin -MaximumBytes $RAMMax
# Step 4 - Set the VLAN for the Network device
$VM | Get-VMNetworkAdapter | Set-VMNetworkAdapterVlan -Access -VlanId $Vlan
# Step 5 - Add Each of the VHDs
$VHDs | ForEach-Object { $VM | Add-VMHardDiskDrive -Path $_.VHDPath }
}
elseif ( $Architecture -eq "VMware" )
{
#region Import the VMware Module & Remove the Hyper-V Module (if enabled)
# This is done because there are collisions in the names of functions
if ( Get-Module -Name "Hyper-V" -ErrorAction SilentlyContinue )
{
Remove-Module -Name "Hyper-V" -Confirm:$false -Force
}
if ( -not ( Get-Module -Name "VMware.PowerCLI" -ErrorAction SilentlyContinue ) )
{
Import-Module VMware.PowerCLI -Force
}
#endregion Import the VMware Module & Remove the Hyper-V Module
$vCenterServer = "vCenter.Demo.Lab"
$DiskFormat = "Thin" # or "Thick" or "EagerZeroedThick"
$VlanName = "External - VLAN 300"
$GuestOS = "windows9Server64Guest" # OS Identifer of the Machine
#region Connect to vCenter server via Trusted Windows Credentials
if ( -not ( $global:DefaultVIServer ) )
{
Connect-VIServer -Server $vCenterServer
}
#endregion Connect to vCenter server via Trusted Windows Credentials
# Find the host with the most free MHz or specify one by using:
# $VMHost = Get-VMHost -Name "ESX Host Name"
$VmHost = Get-VMHost | Sort-Object -Property @{ Expression = { $_.CpuTotalMhz - $_.CpuUsageMhz } } -Descending | Select-Object -First 1
# Calculate the MHz for each processor on the host
$MhzPerCpu = [math]::Floor( $VMHost.CpuTotalMhz / $VMHost.NumCpu )
# Convert the Disk Sizes to a list of numbers (for New-VM Command)
$DiskSizes = $VHDSizes.Keys | Sort-Object | ForEach-Object { $VHDSizes[$_] / 1GB }
# Create the VM
$VM = New-VM -Name $VMName -ResourcePool $VMHost -DiskGB $DiskSizes -MemoryGB ( $RAMMax / 1GB ) -DiskStorageFormat $DiskFormat -GuestId $GuestOS -NumCpu $CPUCount
# Setup minimum resources
# CPU is Number of CPUs * Reservation (as percentage) * MHz per Processor
$VM | Get-VMResourceConfiguration | Set-VMResourceConfiguration -CpuReservationMhz ( $CPUCount * ( $CPUReserve / 100 ) * $MhzPerCpu ) -MemReservationGB ( $RAMMax / 2GB )
# Set my VLAN
$VM | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $VlanName -Confirm:$false
}
else
{
Write-Error -Message "Neither Hyper-V or VMware defined as `$Architecture"
}
Next step is to install the operating system. I do this with Windows Deployment Services. Your mileage may vary.
After that, we need to configure the machine itself. That'll be the next post.
About this post:
This post is a combination of two posts on my personal blog: Building my Orion Server [VMware Scripting Edition] – Step 1 & Building my Orion Server [Hyper-V Scripting Edition] – Step 1.