Hyper-V. Part 3: Setting up virtual disks
All parts of the series “Hyper-V”In the first part the machine's disk appeared by itself: New-VM -NewVHDPath … -NewVHDSizeBytes 80GB created a dynamic VHDX and attached it to the machine. Here we will look at what kinds of virtual disks there are, how to change their size, return space to the volume and convert between formats, how to attach and detach disks on a running machine, and when a physical disk is given instead of a file.
VHD and VHDX
A Hyper-V virtual disk is an ordinary file in one of two formats. VHD is the old format, limited to 2040 GB. VHDX holds up to 64 TB (Hyper-V limits), keeps a log of changes to its metadata, so it survives a power loss in the middle of a write, and supports 4 KB logical sectors — the format is described in the MS-VHDX specification. Today VHD is needed only for exchange with systems that do not understand VHDX; all new disks are VHDX.
By the way space is allocated, disks come in three types (New-VHD):
- Fixed (
-Fixed) — the file immediately takes up the whole declared size. There is no overhead for growth, and the space is guaranteed. - Dynamic (
-Dynamic, the default type) — the file grows in blocks as data is written. It saves space, but lets you "oversell" the volume beyond what it has: if all dynamic disks grow at the same time, the volume runs out, and Hyper-V pauses the machines that have nowhere to write. - Differencing (
-Differencing) — an empty "child" disk on top of a parent (-ParentPath): reads come from the parent, all changes are written to the child file. This is how checkpoints work, and it is a convenient way to bring up many identical machines from one prepared image. After that the parent must not be changed — all child disks would break.
New-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\app1-os.vhdx' -SizeBytes 80GB -Dynamic
New-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\sql1-data.vhdx' -SizeBytes 200GB -Fixed
New-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\test1.vhdx' -Differencing `
-ParentPath 'D:\Hyper-V\Templates\ws2025-core.vhdx'
The GB and TB suffixes are built-in PowerShell multipliers; 80GB equals 85,899,345,920 bytes.
What Get-VHD shows
Get-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\app1-os.vhdx' |
Select-Object VhdFormat, VhdType, FileSize, Size, MinimumSize, LogicalSectorSize,
ParentPath, Attached, FragmentationPercentage
FileSize is how much the file takes up on the volume, Size is how much the guest sees. For a fixed disk they are almost equal; for a dynamic one the first grows as data is written, but does not shrink when the guest deletes files. MinimumSize is the boundary of the last partition inside: the disk cannot be shrunk below it. ParentPath is filled in for differencing disks, and Attached shows whether the file is currently open by a machine or by the host.
Size
Both VHD and VHDX can be expanded; only VHDX can be shrunk, and not below its MinimumSize. A disk on a SCSI controller changes size on a running machine, a disk on IDE (only on generation 1 machines) only on a machine that is turned off (Resize-VHD).
Expanding takes two commands: the file grows on the host, the partition in the guest, as in the part about disks of the Windows Server series. The command in the guest is conveniently run from the host through PowerShell Direct — a channel over VMBus that does not need the guest's network; it is covered in detail in the part about management from a workstation, and here the guest administrator's credentials are enough:
$cred = Get-Credential 'app1\Administrator'
$disk = 'D:\Hyper-V\Virtual Hard Disks\app1-data.vhdx'
Resize-VHD -Path $disk -SizeBytes 300GB
Invoke-Command -VMName app1 -Credential $cred {
Update-HostStorageCache
Resize-Partition -DriveLetter E -Size (Get-PartitionSupportedSize -DriveLetter E).SizeMax
}
Shrinking goes in the reverse order: first the partition in the guest (Resize-Partition to a smaller size), then the file. -ToMinimumSize cuts off everything after the last partition:
Invoke-Command -VMName app1 -Credential $cred { Resize-Partition -DriveLetter E -Size 150GB }
Resize-VHD -Path $disk -ToMinimumSize
Compaction, conversion, merging
A dynamic disk does not give back the space freed by the guest. Compaction helps return it (Optimize-VHD): the disk must then be either not attached or attached to the host read-only.
Mount-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\app1-os.vhdx' -ReadOnly
Optimize-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\app1-os.vhdx' -Mode Full
Dismount-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\app1-os.vhdx'
Conversion between formats and types (Convert-VHD) creates a new file and works only with a disk that is not attached (Convert-VHD). This is how an old VHD is turned into a VHDX, or a dynamic disk into a fixed one:
Convert-VHD -Path 'D:\old\legacy.vhd' -DestinationPath 'D:\Hyper-V\Virtual Hard Disks\legacy.vhdx' -VHDType Dynamic
Merging a differencing disk into its parent is Merge-VHD -Path child.avhdx -DestinationPath parent.vhdx (Merge-VHD). It is rarely needed by hand: chains are usually merged by Hyper-V itself when a checkpoint is deleted, and orphaned chains will be covered in the part about checkpoints.
Disk on the host
Mount-VHD attaches the file to the host itself as an ordinary disk — after that the Storage module cmdlets work. This is how a data disk is prepared before it is given to a machine, or files are retrieved from the disk of a machine that is turned off (Mount-VHD):
New-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\share.vhdx' -SizeBytes 50GB -Dynamic |
Mount-VHD -Passthru |
Initialize-Disk -PartitionStyle GPT -PassThru |
New-Partition -UseMaximumSize -AssignDriveLetter |
Format-Volume -FileSystem NTFS -NewFileSystemLabel 'Share' -Confirm:$false
Dismount-VHD -Path 'D:\Hyper-V\Virtual Hard Disks\share.vhdx'
A disk attached to the host cannot be attached to a running machine at the same time — the file is opened by only one party.
The machine's disks
Disks are attached to the machine's controllers. A generation 2 machine has only SCSI controllers — up to four with 64 devices each (limits), and disks on them are attached and detached on a running machine. On a generation 1 machine the boot disk must be on IDE, and nothing on IDE changes on the fly.
See what is attached and where:
Get-VMHardDiskDrive -VMName app1 | Format-Table ControllerType, ControllerNumber, ControllerLocation, Path
Get-VMScsiController -VMName app1
Add a new data disk — create the file, attach it to a SCSI controller and partition it inside the guest (Add-VMHardDiskDrive):
$disk = 'D:\Hyper-V\Virtual Hard Disks\app1-data.vhdx'
New-VHD -Path $disk -SizeBytes 200GB -Dynamic
Add-VMHardDiskDrive -VMName app1 -ControllerType SCSI -Path $disk
Invoke-Command -VMName app1 -Credential $cred {
Get-Disk | Where-Object PartitionStyle -eq 'RAW' |
Initialize-Disk -PartitionStyle GPT -PassThru |
New-Partition -UseMaximumSize -DriveLetter E |
Format-Volume -FileSystem NTFS -NewFileSystemLabel 'Data' -Confirm:$false
}
Without -ControllerNumber and -ControllerLocation the disk goes into the first free slot. Attach an existing disk — the same command with the path to an existing file.
Add a SCSI controller — when there are more than 64 disks or you want to spread them across controllers; controllers are added only on a machine that is turned off (Add-VMScsiController):
Stop-VM -Name app1
Add-VMScsiController -VMName app1
Start-VM -Name app1
Detach a disk. Remove-VMHardDiskDrive detaches the disk from the machine, and the file stays in place — it has to be deleted separately, when you are sure it is not needed (Remove-VMHardDiskDrive). Before detaching on the fly, it is worth taking the disk offline in the guest, otherwise to the guest it looks like a pulled-out disk:
Invoke-Command -VMName app1 -Credential $cred { Get-Partition -DriveLetter E | Get-Disk | Set-Disk -IsOffline $true }
Get-VMHardDiskDrive -VMName app1 -ControllerType SCSI -ControllerNumber 0 -ControllerLocation 1 | Remove-VMHardDiskDrive
Move a disk to another controller or slot — Set-VMHardDiskDrive with the -ToControllerType, -ToControllerNumber, -ToControllerLocation parameters (Set-VMHardDiskDrive).
Limit a disk by IOPS, so that one machine does not take the whole volume away from its neighbours. Hyper-V counts "normalized" operations: the I/O volume per second divided by 8 KB (Set-VMHardDiskDrive):
Get-VMHardDiskDrive -VMName app1 | Set-VMHardDiskDrive -MaximumIOPS 2000
Get-VMHardDiskDrive -VMName app1 | Format-Table Path, MinimumIOPS, MaximumIOPS
A physical disk directly
Pass-through: a host disk is taken offline, and the machine gets it whole, without a VHDX file (Add-VMHardDiskDrive). This is how a LUN or a disk with data that cannot be moved into a file is attached; the price is that such a disk has no checkpoints and no storage migration.
Get-Disk | Format-Table Number, FriendlyName, Size, OperationalStatus
Set-Disk -Number 3 -IsOffline $true
Add-VMHardDiskDrive -VMName app1 -ControllerType SCSI -DiskNumber 3
DVD
Inserting and ejecting an ISO is Set-VMDvdDrive with the path to the image or with $null (Set-VMDvdDrive):
Set-VMDvdDrive -VMName app1 -Path 'D:\ISO\tools.iso'
Set-VMDvdDrive -VMName app1 -Path $null
Get-VM | Get-VMDvdDrive | Where-Object Path | Format-Table VMName, Path
The last command finds machines with forgotten ISOs. An image from the host's local disk gets in the way of live migration: the other host has no such path. That is why images are ejected after installation.
Shared disk for a guest cluster
When a cluster is built not from hosts but from virtual machines (for example, two SQL Servers in FCI mode), they need a shared disk. For this there is the VHD Set format (.vhds): the same file is attached to several machines on cluster storage (creating a VHD Set):
New-VHD -Path 'C:\ClusterStorage\Volume1\sqlclu\shared.vhds' -SizeBytes 100GB -Dynamic
Add-VMHardDiskDrive -VMName sql1 -ControllerType SCSI -Path 'C:\ClusterStorage\Volume1\sqlclu\shared.vhds' -SupportPersistentReservations
Add-VMHardDiskDrive -VMName sql2 -ControllerType SCSI -Path 'C:\ClusterStorage\Volume1\sqlclu\shared.vhds' -SupportPersistentReservations
The C:\ClusterStorage path is the shared volume of a Hyper-V cluster, covered in the part about the cluster.
Looking at it by hand
All disks of all machines with their actual and declared size and checkpoint chains — forgotten .avhdx files and thin disks that can overflow the volume are visible at once:
Get-VM | Get-VMHardDiskDrive | Where-Object Path | ForEach-Object {
$d = Get-VHD -Path $_.Path
[pscustomobject]@{
VM = $_.VMName
Path = $_.Path
Type = $d.VhdType
FileGB = [math]::Round($d.FileSize/1GB, 1)
SizeGB = [math]::Round($d.Size/1GB, 1)
Parent = $d.ParentPath
}
} | Format-Table -AutoSize
How much space dynamic disks have promised on each volume — compare it with the volume's free space:
Get-VM | Get-VMHardDiskDrive | Where-Object Path | Get-VHD |
Group-Object { Split-Path -Qualifier $_.Path } |
ForEach-Object {
$vol = Get-Volume -DriveLetter $_.Name.TrimEnd(':')
[pscustomobject]@{
Volume = $_.Name
FreeGB = [math]::Round($vol.SizeRemaining/1GB)
GrowthGB = [math]::Round((($_.Group | Measure-Object -Property Size -Sum).Sum -
($_.Group | Measure-Object -Property FileSize -Sum).Sum)/1GB)
}
}
If GrowthGB is greater than FreeGB, the volume is oversold: if the disks grow at the same time, the machines will be paused. Where-Object Path filters out pass-through disks — they have no file.
Sources
- Plan for Hyper-V scalability — VHD and VHDX sizes, the number of SCSI controllers and disks;
- MS-VHDX: Virtual Hard Disk v2 File Format — the VHDX format;
- New-VHD and Resize-VHD — disk types, resizing and its conditions;
- Optimize-VHD, Convert-VHD and Merge-VHD — compaction, conversion, merging;
- Mount-VHD — attaching a disk to the host;
- Add-VMHardDiskDrive, Set-VMHardDiskDrive, Remove-VMHardDiskDrive and Add-VMScsiController — the machine's disks and controllers, IOPS, pass-through;
- Set-VMDvdDrive — ISO images;
- Create Hyper-V VHD Set files — a shared disk for a guest cluster.
A machine's disk is a VHDX file that can be expanded on the fly, compacted, converted and attached to the host; the machine sees it through a SCSI controller, and when needed gets a whole physical disk.
Next — part 4: setting up virtual machine networking. The three types of virtual switches, VLANs and private VLANs in Hyper-V, SET NIC teaming, the machines' network adapters — adding them on the fly, names, MAC addresses, protection against rogue DHCP and bandwidth limits.
Only registered users can leave a comment.
Sign in or register to leave a comment.