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.

  1. Build the virtual machines
  2. Pre-configure the virtual machine's disks
  3. Prep the machine for installation
  4. Install the software (silently)
  5. 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.



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
# 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
            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
    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.