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

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

Preparation

You need to have Hyper-V enabled in your Windows 10/11 installation (And have the virtualization options enabled in your BIOS, check the documentation of your vendor 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 blogpost (Link), I created a way of getting all download links from the Microsoft Evaluation Center. This is something that was nice 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 name of the VM that should be created
  • The amount of memory in Gb’s
  • The amount of CPU cores

But it will also show you the list of VHD files that you can use in a 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 of those 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/Cores 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 a IDE one, no Secure boot or TPM capabilities in this case.

VM Initial sizes

The downloaded VHD’s do take some time to download, 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 VHD’s 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’re have a running Windows Server 🙂

The deployment script

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

#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-host 'Hyper-V Role and/or required PowerShell module is not installed, please install before running this script...' -ForegroundColor Red
}
else {
    Write-host 'Hyper-V Role is installed, continuing...' -ForegroundColor Green
}

#Retrieve all Server Operating System VHD links from the Microsoft Evaluation Center
$totalfound = @()
$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"
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 $($url), Found $($Count) Download(s)..." -ForegroundColor Green
        foreach ($DownloadLink in $DownloadLinks) {
            $found = [PSCustomObject]@{
                Title  = $content.ParsedHtml.title.Split('|')[0]
                Name   = $DownloadLink.'aria-label'.Replace('Download ', '')
                Tag    = $DownloadLink.'data-bi-tags'.Split('"')[3].split('-')[0]
                Format = $DownloadLink.'data-bi-tags'.Split('-')[1].ToUpper()
                Link   = $DownloadLink.href
            }
            $totalfound += $found
        }
    }
    catch {
        Write-host $url is not accessible -ForegroundColor Red
        break
    }
}

#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-host 'No VHD file selected, script aborted...' -ForegroundColor Red
    break
}

#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-host "$($VMname) already exists on this system, aborting..." -ForegroundColor Red
    break
} 
$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-host 'No Virtual Switch selected, script aborted...' -ForegroundColor Red
    break
}

#Create VM directory
try {
    New-Item -ItemType Directory -Path $VMdir -Force:$true -ErrorAction SilentlyContinue | Out-Null
}
catch {
    write-host "Couldn't create $($VMdir) folder, please check VM Name for illegal characters or permissions on folder..." -ForegroundColor Red
    break
}
finally {
    if (test-path -Path $VMdir -ErrorAction SilentlyContinue) { 
        Write-Host "Using $($VMdir) as Virtual Machine location... " -ForegroundColor Green
    }
}

#Download VHD file to the VirtualMachinePath\VMname
write-host "Downloading $($vhd.Name) to $($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-Host "Error creating $($VMname), please check logs and make sure $($VMname) doesn't already exist..." -ForegroundColor Red
}
finally {
    if (Get-VM -Name $VMname -ErrorAction SilentlyContinue | Out-Null) {
        write-host "Created $($VMname)..." -ForegroundColor Green
    }
}

#Configure settings on the VM, CPU/Memory/Disk/BootOrder
try {
    Write-Host "Configuring settings on $($VMname)..." -ForegroundColor Green
    Set-VM -name $VMname -ProcessorCount $VMCores -DynamicMemory -MemoryMinimumBytes 64MB -MemoryMaximumBytes $VMRAM -MemoryStartupBytes 512MB  -ErrorAction SilentlyContinue | Out-Null 
    Add-VMHardDiskDrive -VMName $VMname -Path $VHDFile -ControllerType IDE -ErrorAction SilentlyContinue | Out-Null
}
catch {
    Write-Host "Error setting VM parameters, check $($VMname) settings..." -ForegroundColor Red
    break
}

#The end, stop stopwatch and display the time that it took to deploy
$stopwatch.Stop()
Write-Host "Done, the deployment took $($stopwatch.Elapsed.Hours) hours, $($stopwatch.Elapsed.Minutes) minutes and $($stopwatch.Elapsed.Seconds) seconds" -ForegroundColor Green

Download the script(s) from GitHub here

Leave a Reply

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