Hyper-V Manager is fine for clicking around once. For repeatable work — and for Server Core where there's no GUI at all — you drive Hyper-V from PowerShell. The cmdlets are clean and you can paste a whole VM into existence in one block.
TL;DR:
Install-WindowsFeature Hyper-V -IncludeManagementTools -Restart
# after reboot:
New-VMSwitch -Name "extSwitch" -NetAdapterName "Ethernet" -AllowManagementOS $true
New-VM -Name "web01" -MemoryStartupBytes 4GB -Generation 2 `
-NewVHDPath "D:\VMs\web01\web01.vhdx" -NewVHDSizeBytes 60GB -SwitchName "extSwitch"
Set-VMMemory web01 -DynamicMemoryEnabled $true -MinimumBytes 1GB -MaximumBytes 8GB
Set-VMProcessor web01 -Count 4
Add-VMDvdDrive web01 -Path "D:\ISO\WinServer2025.iso"
Set-VMFirmware web01 -FirstBootDevice (Get-VMDvdDrive web01)
Start-VM web01
Details below.
Check the host can actually run Hyper-V
Hyper-V needs hardware virtualization and SLAT. If the box is a VM itself, it needs nested virtualization exposed by the host first.
Get-ComputerInfo -Property "HyperV*"
You want all four HyperVRequirement* properties reading True: VM monitor mode extensions, virtualization enabled in firmware, SLAT, and DEP. If HyperVisorPresent is already True, the role is on (or another hypervisor is — they don't coexist).
If virtualization shows disabled, it's a BIOS/UEFI setting (Intel VT-x / AMD-V), not something PowerShell fixes. Reboot into firmware and enable it.
Install the role
Install-WindowsFeature Hyper-V -IncludeManagementTools -Restart
-IncludeManagementTools pulls in the Hyper-V PowerShell module and (on full GUI installs) Hyper-V Manager. -Restart is mandatory — the hypervisor loads at boot, not on demand. On Server Core, same command, you just won't get the MMC snap-in.
After reboot, confirm:
Get-WindowsFeature Hyper-V
Get-Command -Module Hyper-V | Measure-Object
Create a virtual switch
A VM with no switch has no network. Three types:
- External — bridges to a physical NIC. The VM is on your LAN like any other host. This is what you want for servers.
- Internal — host and VMs talk to each other, no physical network. Good for a NAT lab.
- Private — VMs talk only to each other. Host is excluded. Isolated test segments.
External, bound to your physical adapter:
Get-NetAdapter | Where-Object Status -eq "Up" # find the NIC name
New-VMSwitch -Name "extSwitch" -NetAdapterName "Ethernet" -AllowManagementOS $true
The exception: creating an External switch briefly drops the host's own connectivity while the NIC rebinds to the virtual switch. Don't do this over an RDP session on the same NIC unless you enjoy locking yourself out — do it on the console, an out-of-band link, or a second adapter. -AllowManagementOS $true keeps the host reachable through the switch afterward; set it $false to dedicate the NIC purely to VMs.
Internal and Private don't touch hardware, so they're safe to create remotely:
New-VMSwitch -Name "intSwitch" -SwitchType Internal
New-VMSwitch -Name "labSwitch" -SwitchType Private
Create the VM
Generation 2 unless you have a specific reason not to — UEFI firmware, Secure Boot, SCSI boot, PXE over a standard adapter. Gen 1 is legacy BIOS; only use it for ancient guests or 32-bit OSes Gen 2 won't run.
New-VM -Name "web01" -MemoryStartupBytes 4GB -Generation 2 `
-NewVHDPath "D:\VMs\web01\web01.vhdx" -NewVHDSizeBytes 60GB `
-SwitchName "extSwitch"
-NewVHDSizeBytes is the maximum. A .vhdx is dynamically expanding by default — it only consumes real disk as the guest writes data, so a 60 GB VHDX on a fresh install sits around 10 GB. Put VHDXs on a fast data volume, not the OS drive.
Memory and CPU
MemoryStartupBytes is the boot allocation. Turn on dynamic memory so idle VMs hand RAM back to the pool:
Set-VMMemory web01 -DynamicMemoryEnabled $true -MinimumBytes 1GB -MaximumBytes 8GB
Set-VMProcessor web01 -Count 4
The exception: some workloads refuse to start or behave badly under dynamic memory — older SQL Server, anything that pins a large buffer pool. Leave those static at a fixed MemoryStartupBytes with dynamic disabled.
Processor count can't exceed the host's logical processors. Overcommitting CPU across VMs is fine (the scheduler time-slices); overcommitting per-VM beyond physical cores is not.
Attach the install ISO and fix boot order
A fresh VHDX is empty. Mount an installer ISO as a DVD drive:
Add-VMDvdDrive web01 -Path "D:\ISO\WinServer2025.iso"
Gen 2 VMs boot from the disk by default, which is empty, so they fall through to PXE. Point the firmware at the DVD for the first boot:
Set-VMFirmware web01 -FirstBootDevice (Get-VMDvdDrive web01)
After the OS is installed, set it back to the disk (or just remove the DVD drive) so it doesn't re-enter the installer.
The Linux-on-Gen2 gotcha
Gen 2 Secure Boot uses the Microsoft Windows certificate by default. Boot a Linux ISO under that and you get a Secure Boot failure — black screen, no obvious error. The fix is the UEFI CA template:
Set-VMFirmware web01 -SecureBootTemplate MicrosoftUEFICertificateAuthority
Modern Ubuntu, Debian, RHEL ship shim binaries signed against that CA, so they boot fine with Secure Boot still on. Only disable Secure Boot entirely (-EnableSecureBoot Off) for distros without a signed shim.
Start it and connect
Start-VM web01
vmconnect.exe localhost web01
vmconnect is the console window — you need it to click through OS install. On Server Core there's no vmconnect; run it from a management workstation with the Hyper-V tools installed, pointed at the host. Once the guest has an OS and an IP off your External switch, you stop needing the console and just RDP/SSH in.
Checkpoints — and why they aren't backups
A checkpoint is point-in-time state you can roll back to. Two kinds:
Set-VM web01 -CheckpointType Production # default; uses VSS, app-consistent
# vs Standard — captures live memory + disk, restores to running state
Production quiesces the guest via VSS (or filesystem freeze on Linux) for a crash-consistent, application-safe snapshot — the right default. Standard also saves RAM so you restore to the exact running moment, which is handy for debugging a live state but is not application-consistent.
Checkpoint-VM web01 -SnapshotName "pre-patch"
Restore-VMSnapshot -VMName web01 -Name "pre-patch" -Confirm:$false
Use them before a risky change, then delete them. Do not treat checkpoints as backups: they live on the same disk as the VM, the differencing chain grows and slows the VM the longer it exists, and a long-lived chain that merges badly can corrupt the VHDX. For real backups, copy the VHDX out to other media — see my restic-to-S3 setup for the offsite side.
Nested virtualization
Want to run Hyper-V (or Docker's Hyper-V backend, or WSL2) inside a VM? Expose the virtualization extensions to the guest — VM off first:
Stop-VM web01
Set-VMProcessor web01 -ExposeVirtualizationExtensions $true
Set-VMMemory web01 -DynamicMemoryEnabled $false # nested needs static memory
Start-VM web01
Nested requires static memory, so this trades away the dynamic-memory benefit. Fine for a CI runner or a lab box, wasteful otherwise.
On a Linux host instead?
If your hypervisor host runs Linux, this whole article is the wrong tool — use Proxmox (KVM under the hood) and drive it with qm. The mental model maps cleanly: VMSwitch → Linux bridge, VHDX → qcow2/zvol, checkpoint → snapshot, and cloud-init replaces clicking through an installer entirely. I cover the fast path in Proxmox + cloud-init VM templates. Pick one hypervisor per host — KVM and Hyper-V both want the CPU virtualization extensions and won't share.
Verify
Get-VM # state, CPU, memory, uptime
Get-VM web01 | Select-Object Name, State, Generation, ProcessorCount
Get-VMNetworkAdapter web01 # confirm it's on the right switch
Get-VMFirmware web01 # boot order + Secure Boot template
Get-VM showing Running with a non-zero uptime means the hypervisor is live and your VM is up. From here it's just OS setup — my Windows Server initial setup checklist and DHCP setup are the next two things I run on every fresh guest.
Gotchas
- No reboot after install → Hyper-V cmdlets exist but VMs won't start. The hypervisor only loads at boot.
-Restartor reboot manually. - External switch over RDP → you drop yourself. Console or second NIC only.
- Linux Gen2 black screen → Secure Boot template. Set
MicrosoftUEFICertificateAuthority. - VM won't boot off ISO → Gen2 boot order defaults to disk/PXE.
Set-VMFirmware -FirstBootDevice. - Checkpoints piling up → differencing chain bloat, eventual VHDX corruption risk. Delete after use; they are not backups.
- Nested virt won't enable → VM must be off, and dynamic memory must be disabled.