Creating a Hyper-V VM running Evaluation versions of Windows server using PowerShell

Sometimes you want to test something, but… You must install a new VM on your machine, which takes time. Microsoft offers evaluation versions of their server operating systems in a VHD format. In a blog post, I will show you how to create a Windows Server VM based on a VHD file which gets you up and running quickly 🙂

Preparation

It would be best to have Hyper-V enabled in your Windows 10/11 installation (And have the virtualization options enabled in your BIOS. Check your vendor’s documentation on how to enable it). You can install all Hyper-V features (The hypervisor and the PowerShell management module) by running this in an elevated PowerShell prompt:

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

How the script works

In a previous blog post (Link), I created a way of getting all download links from the Microsoft Evaluation Center. This was an excellent way of getting all the VHD links available. Microsoft has three versions of those for Windows Server 2012R2, 2019, and 2022 (No Windows 2016 version, strangely enough?!). When running the script, it will ask for the following:

  • The name of the VM that should be created
  • The amount of memory in Gb’s
  • The number of CPU cores

But it will also show you the list of VHD files that you can use in an Out-Gridview window, and you can select the VHD by selecting it and clicking OK. The same goes for the Hyper-V Switch selection, which is handy when you have multiple configured.

After entering the variables and selecting the VHD version and Hyper-V Switch, the script will create a new VM in your default Hyper-V location. After creation, the memory/CPU/Core settings will be applied, and the downloaded VHD will be added to the VM. The VM Generation will be 1, and the disk controller will be an IDE with no Secure boot or TPM capabilities.

VM Initial sizes

The downloaded VHDs do take some time to download. The sizes are:

Operating SystemSize in Gb’s
Windows Server 2012 R27.5
Windows Server 20198.3
Windows Server 20229.5

Deploying a VM

Below are screenshots of all the steps of creating a new VM. In this example, it’s Windows Server 2022:

Console output:

Hyper-V Role is installed, continuing…
Processing https://www.microsoft.com/en-us/evalcenter/download-windows-server-2012-r2, Found 1 Download(s)…
Processing https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016, Found 0 Download(s)…
Processing https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019, Found 1 Download(s)…
Processing https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022, Found 1 Download(s)…

Select the VHD to download. In this example, I choose Windows Server 2019:

Answer the sizing questions:

Select the Virtual Switch:

The download starts, it could take some time, depending on your internet connection, but the Microsoft network is pretty fast, and I got about 500Mbits download speed:

And if there are no errors… It will show you how long the deployment took, and you’re done 🙂 Just over 3 minutes, not bad!

The VM

After deploying, the VM is available in the Hyper-V Manager with all the settings you configured:

The Microsoft Evaluation Center VHDs start with a mini-setup with a language/region selection and configuration of the local Administrator account.

Language and Region:

The License Terms:

The password screen:

After pressing CTRL-ALT-END in the Virtual Machine Connection Window and logging in…

You have a running Windows Server 🙂

The deployment script

Below is the script, and run it in an Administrator PowerShell. The script checks for the Hyper-V components and stops the script when it can’t complete specific settings or if a VM is already registered with the same name.

Note: Run this from a PowerShell 7 prompt, not from a PowerShell 5 or ISE/VSCode session

#Requires -RunAsAdministrator
 
#Start a stopwatch to measure the deployment time
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
 
#Detect if Hyper-V is installed
if ((Get-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V-All -Online).State -ne 'Enabled') {
    Write-Warning ("Hyper-V Role and/or required PowerShell module is not installed, please install before running this script...")
    return
}
else {
    Write-Host ("Hyper-V Role is installed, continuing...") -ForegroundColor Green
}
 
#Retrieve all Server Operating System VHD links from the Microsoft Evaluation Center
$totalcount = $null
 
$urls = @(
    'https://www.microsoft.com/en-us/evalcenter/download-windows-server-2012-r2',
    'https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016',
    'https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019',
    'https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022'
)
  
#Loop through the urls, search for VHD download links and add to totalfound array and display number of downloads
$ProgressPreference = "SilentlyContinue"
$totalfound = foreach ($url in $urls) {
    try {
        $content = Invoke-WebRequest -Uri $url -ErrorAction Stop
        $downloadlinks = $content.links | Where-Object { `
                $_.'aria-label' -match 'Download' `
                -and $_.'aria-label' -match 'VHD'
        }
        $count = $DownloadLinks.href.Count
        $totalcount += $count
        Write-host ("Processing {0}, Found {1} Download(s)..." -f $url, $count) -ForegroundColor Green
        foreach ($DownloadLink in $DownloadLinks) {
            [PSCustomObject]@{
                Name   = $DownloadLink.'aria-label'.Replace('Download ', '')
                Tag    = $DownloadLink.'data-bi-tags'.Split('&')[3].split(';')[1]
                Format = $DownloadLink.'data-bi-tags'.Split('-')[1].ToUpper()
                Link   = $DownloadLink.href
            }
        }
    }
    catch {
        Write-Warning ("{0} is not accessible" -f $url)
        return
    }
}
 
#Select VHD from the list
$VHD = $totalfound | Out-GridView -OutputMode Single -Title 'Please select the VHD file to use and click OK' | Select-Object Name, Link
if (($VHD.Name).Count -ne '1') {
    Write-Warning ("No VHD file selected, script aborted...")
    return
}
 
#Set VM Parameters
$VMname = Read-Host 'Please enter the name of the VM to be created, for example W2K22SRV'
if ((Get-VM -Name $VMname -ErrorAction SilentlyContinue).count -ge 1) {
    Write-Warning ("{0} already exists on this system, aborting..." -f $VMname)
    return
} 
$VMCores = Read-Host 'Please enter the amount of cores, for example 2'
[int64]$VMRAM = 1GB * (read-host "Enter Maximum Memory in Gb's, for example 4")
$VMdir = (get-vmhost).VirtualMachinePath + $VMname
$SwitchName = Get-VMSwitch | Out-GridView -OutputMode Single -Title 'Please select the VM Switch and click OK' | Select-Object Name
if (($SwitchName.Name).Count -ne '1') {
    Write-Warning ("No Virtual Switch selected, script aborted...")
    return
}
 
#Create VM directory
try {
    New-Item -ItemType Directory -Path $VMdir -Force:$true -ErrorAction SilentlyContinue | Out-Null
}
catch {
    Write-Warning ("Couldn't create {0} folder, please check VM Name for illegal characters or permissions on folder..." -f $VMdir)
    return
}
finally {
    if (test-path -Path $VMdir -ErrorAction SilentlyContinue) { 
        Write-Host ("Using {0} as Virtual Machine location..." -f $VMdir) -ForegroundColor Green
    }
}
 
#Download VHD file to the VirtualMachinePath\VMname
write-host ("Downloading {0} to {1}..." -f $vhd.Name, $VMdir) -ForegroundColor Green
$VHDFile = "$($VMdir)\$($VMname)" + ".vhd"
$VMPath = (Get-VMHost).VirtualMachinePath + '\'
Invoke-WebRequest -Uri $vhd.Link -OutFile $VHDFile
 
#Create VM with the specified values
try {
    New-VM -Name $VMname -SwitchName $SwitchName.Name -Path $VMPath -Generation 1 -NoVHD:$true -Confirm:$false -ErrorAction Stop | Out-Null  
}
catch {
    Write-Warning ("Error creating {0}, please check logs and make sure {1} doesn't already exist..." -f $VMname, $VMname)
}
finally {
    if (Get-VM -Name $VMname -ErrorAction SilentlyContinue | Out-Null) {
        write-host ("Created {0}..." -f $VMname) -ForegroundColor Green
    }
}
 
#Configure settings on the VM, Checkpoints, CPU/Memory/Disk/BootOrder, Integration Services
try {
    Write-Host ("Configuring settings on {0}..." -f $VMname) -ForegroundColor Green
    Set-VM -name $VMname -ProcessorCount $VMCores -DynamicMemory -MemoryMinimumBytes 64MB -MemoryMaximumBytes $VMRAM -MemoryStartupBytes 512MB -CheckpointType ProductionOnly -AutomaticCheckpointsEnabled:$false 
    Add-VMHardDiskDrive -VMName $VMname -Path $VHDFile -ControllerType IDE -ErrorAction SilentlyContinue | Out-Null
    Enable-VMIntegrationService -VMName $VMname -Name 'Guest Service Interface' , 'Heartbeat', 'Key-Value Pair Exchange', 'Shutdown', 'Time Synchronization', 'VSS'
}
catch {
    Write-Warning ("Error setting VM parameters, check {0} settings..." -f $VMname)
    return
}
 
#The end, stop stopwatch and display the time that it took to deploy
$stopwatch.Stop()
Write-Host ("Done, the deployment took {0} hours, {1} minutes and {2} seconds" -f $stopwatch.Elapsed.Hours, $stopwatch.Elapsed.Minutes, $stopwatch.Elapsed.Seconds) -ForegroundColor Green

Download the script(s) from GitHub here

2 thoughts on “Creating a Hyper-V VM running Evaluation versions of Windows server using PowerShell

  1. Is it possible to change the Script so that i could choose of a List of locally stored .vhdx Master Images?

    • That’s possible, there are examples of that already but I could do a new blog post about that… This one was specifically about using Evaluation versions for easy lab deployment. I’ll put it on my list 🙂

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.