Creating Generation 2 Disk (PowerShell – HyperV)

To totally unlock this section you need to Log-in


When Windows Server 2012 R2 hit the datacenter, and with it a new version of Microsoft Hyper-V, it offered the option to create a new kind of virtual machine. These “Gen 2”, or generation 2, virtual machines These VMs can now support UEFI boot, among other features. However, they also require a specific disk configuration. So now it seems, we need to see what it takes to create one of these new Gen 2 devices which by the way are only compatible with 64-bit flavors of Windows 8.1, Windows 10 and Windows Server 2012 R2.

To follow along, you will need to be running the most current version of Hyper-V, either on a server or a Windows 8.1 client. In addition to PowerShell 4.0 you will also need the Storage module. As you will see, you will need to create and format some partitions and the Storage cmdlets are very convenient. Let’s begin by defining a variable that holds the path to the new disk file, which has to be a VHDX file.

Creating the VHDX file is pretty easy.

$path = "D:\VHD\Gen2Demo.vhdx"
$disk = New-VHD -Path $path -Dynamic -SizeBytes 25GB

The New-VHD cmdlet doesn’t know how to create a Gen 2 disk, so we’ll have to handle that ourselves. First, you need to mount the disk image.

Mount-DiskImage -ImagePath $path

You will be referring to this disk several times which you will be able to do with its number.

Creating Generation 2 Disk (PowerShell - HyperV)

Let’s save that value to a variable.

$disknumber = (Get-DiskImage -ImagePath $path).Number

The first step in the process is to initialize the disk with a GPT partition, which is what makes it a Gen 2 disk.

  • Obviously, if your partition needs to be > 2 TB, you need GPT.
  • If you need > 4 partitions, GPT.
  • If you have an older OS, MBR only (Gen 1).
Initialize-Disk -Number $disknumber -PartitionStyle GPT

Next, create the recovery partition and while you are at it, go ahead and format it as well.

New-Partition -DiskNumber $disknumber -GptType '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}' -Size 300MB | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Windows RE Tools" -confirm:$false

This is a one line Powershell command. We are using the –confirm:$false parameter in Format-Volume to suppress being prompted to continue. It is recommended to mark this partition as protected so that it can’t be removed. However, there are no cmdlets that will do that so you’ll have to turn to the DISKPART command line utility.

Fortunately, this is easy to use in PowerShell because DISKPART has a rudimentary automation interface. But, you will need to know the partition number for the Recovery partition.

Creating Generation 2 Disk (PowerShell - HyperV)

We’ll save that result to a variable as well.

$partitionNumber = (get-disk $disknumber | Get-Partition | where {$_.type -eq 'recovery'}).PartitionNumber

With this, you can pipe directions to DISKPART with a here string.

select disk $disknumber
select partition $partitionNumber
gpt attributes=0x8000000000000001
"@ | diskpart

Creating Generation 2 Disk (PowerShell - HyperV)

Next, you need to create the System partition. You might think it would be as easy as the Recovery partition and we certainly were as too. Unfortunately, there appears to be a bug with formatting a System partition on this type of disk.

Format-Volume : No matching MSFT_Volume objects found by CIM query for enumerating instances of the ROOT/Microsoft/Windows/Storage/MSFT_Volume class on the CIM server, that are associated with the following instance: MSFT_Partition (DiskId = "\\?\scsi#disk&ven_msft&prod_virtual_dis..., Offset = 449839104). Verify query parameters and retry.

At C:\Scripts\New-Gen2VHD.ps1:106 char:5
+ Format-Volume -FileSystem FAT32 -NewFileSystemLabel "System" + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (MSFT_Volume:String) [Format-Volume], CimJobException + FullyQualifiedErrorId : CmdletizationQuery_NotFound,Format-Volume

You can still create the partition with PowerShell, but you’ll need to use DISKPART to format it. Or you could use DISKPART for the entire step.

$sysPart = New-Partition -DiskNumber $disknumber -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93bc}' -Size 100MB

We saved the output to a variable so that we could grab the partition number which we can then use with a DISKPART command.

$systemNumber = $sysPart.partitionNumber

"@ select disk $disknumber select partition $systemNumber format quick fs=fat32 label=System exit @" | diskpart

Next, you need to create a small, unformatted reserved partion or MSR. 128MB is the recommended size.

New-Partition -disknumber $disknumber -GptType '{e3c9e316-0b5c-4db8-817d-f92df00215ae}' -Size 128MB

All that remains is to create a partition for the operating system, using all of the remaining space.

New-Partition -DiskNumber $disknumber -UseMaximumSize

While you could get by with this configuration, Microsoft recommends you create a partition for the recovery image after the operating system partition. If you look through the Microsoft DISKPART example, they all use a 15GB partition, so we will do the same. This means though, that my operating system partition can only use any space left after allowing for the other partitions. So if we start with a 25GB drive, subtracting the Recovery Tools (300MB), System (100MB), MSR (128MB) and the 15GB recovery image, we are left with a smaller partition. So instead of the previous command we can run this.

$winsize = (25gb-300mb-100mb-128mb-15gb)
New-Partition -DiskNumber $disknumber -size $winsize

Finally, we can create the recovery image partition.

New-Partition -DiskNumber $disknumber -GptType '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}' -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Windows Recovery" -confirm:$false

Yeah, we are using maximum size but it should be pretty close the 15GB that should be left.

This partition is unformatted under the assumption that you will format it during the install process. The final step is to dismount the disk.

Dismount-DiskImage -ImagePath $path

Now it is ready to use in a new Gen 2 Hyper-V virtual machine.

Name = "Demo Gen2 VM"
MemoryStartupBytes = 512MB
VHDPath = $path
Generation = 2
SwitchName ="Work Network"
BootDevice = "CD"

New-VM @param

It is important to configure the VM to boot for CD or something other than the VHD, since there is no operating system. When you start the virtual machine you should be able to install the OS from a DVD or ISO file.

But since creating generation 2 disks is likely to be a repeatable process, how about a tool? Here is a function wrote to create a Gen 2 disk.

#requires -version 4.0
#requires -modules Hyper-V,Storage
#requires -RunAsAdministrator
Function New-Gen2Disk {
< #
Create a Generation 2 VHDX
This command will create a generation 2 VHDX file. Many of the parameters are
from the New-VHD cmdlet. The disk name must end in .vhdx. 
The disk will be created with these partitions in this order:
300MB Recovery Tools
100MB System 
15GB Recovery Image
The size of the windows partition will be whatever is left over give or take a few KB.
PS C:\> New-Gen2Disk d:\disks\disk001.vhdx -dynamic -size 50GB
[Parameter(Position=0,Mandatory,HelpMessage="Enter the path for the new VHDX file")]
  #get parent
  if (Split-Path $_ | Test-Path) {
  else {
    Throw "Failed to find parent folder for $_."
#initialize some variables
$RESize = 300MB
$SysSize = 100MB
$MSRSize = 128MB
$RecoverySize = 15GB
Write-Verbose "Creating $path"
#verify the file doesn't already exist
if (Test-Path -Path $path) {
    Throw "Disk image at $path already exists."
    #bail out
#create the VHDX file
Write-Verbose "Creating the VHDX file for $path"
 ErrorAction= "Stop"
 Path = $Path
 SizeBytes = $Size
 Dynamic = $Dynamic
 BlockSizeBytes = $BlockSizeBytes
 LogicalSectorSizeBytes = $LogicalSectorSizeBytes
 PhysicalSectorSizeBytes = $PhysicalSectorSizeBytes
Try {
  Write-verbose ($vhdParams | out-string)
  $disk = New-VHD @vhdParams
Catch {
  Throw "Failed to create $path. $($_.Exception.Message)"
  #bail out
if ($disk) {
    #mount the disk image
    Write-Verbose "Mounting disk image"
    Mount-DiskImage -ImagePath $path
    #get the disk number
    $disknumber = (Get-DiskImage -ImagePath $path | Get-Disk).Number
    $WinPartSize = (Get-Disk -Number $disknumber).Size - ($RESize+$SysSize+$MSRSize+$RecoverySize)
    #initialize as GPT
    Write-Verbose "Initializing disk $DiskNumber as GPT"
    Initialize-Disk -Number $disknumber -PartitionStyle GPT 
    #clear the disk
    Write-Verbose "Clearing disk partitions to start all over"
    get-disk -Number $disknumber | Get-Partition | Remove-Partition -Confirm:$false
    #create the RE Tools partition
    Write-Verbose "Creating a $RESize byte Recovery tools partition on disknumber $disknumber"
    New-Partition -DiskNumber $disknumber -GptType '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}' -Size $RESize |
    Format-Volume -FileSystem NTFS -NewFileSystemLabel "Windows RE Tools" -confirm:$false | Out-null
   $partitionNumber = (get-disk $disknumber | Get-Partition | where {$_.type -eq 'recovery'}).PartitionNumber
    Write-Verbose "Retrieved partition number $partitionnumber"
    #run diskpart to set GPT attribute to prevent partition removal
    #the here string must be left justified
select disk $disknumber
select partition $partitionNumber
gpt attributes=0x8000000000000001
"@ | diskpart | Out-Null
    #create the system partition
    Write-Verbose "Creating a $SysSize byte System partition on disknumber $disknumber"
    < #
     There is a known bug where Format-Volume cannot format an EFI partition
     so formatting will be done with Diskpart
    $sysPartition = New-Partition -DiskNumber $disknumber -GptType '{c12a7328-f81f-11d2-ba4b-00a0c93ec93bc}' -Size $SysSize
    $systemNumber = $sysPartition.PartitionNumber
    Write-Verbose "Retrieved system partition number $systemNumber"
select disk $disknumber
select partition $systemNumber
format quick fs=fat32 label=System 
@" | diskpart | Out-Null
    #create MSR
    write-Verbose "Creating a $MSRSize MSR partition"
    New-Partition -disknumber $disknumber -GptType '{e3c9e316-0b5c-4db8-817d-f92df00215ae}' -Size $MSRSize | Out-Null
    #create OS partition
    Write-Verbose "Creating a $WinPartSize byte OS partition on disknumber $disknumber"
    New-Partition -DiskNumber $disknumber -Size $WinPartSize | Out-Null
    #create recovery
    Write-Verbose "Creating a $RecoverySize byte Recovery partition"
    $RecoveryPartition = New-Partition -DiskNumber $disknumber -GptType '{de94bba4-06d1-4d40-a16a-bfd50179d6ac}' -UseMaximumSize | Out-Null
    $RecoveryPartitionNumber = $RecoveryPartition.PartitionNumber
    $RecoveryPartition | Format-Volume -FileSystem NTFS -NewFileSystemLabel "Windows Recovery" -confirm:$false
    #run diskpart to set GPT attribute to prevent partition removal
    #the here string must be left justified
select disk $disknumber
select partition $RecoveryPartitionNumber
gpt attributes=0x8000000000000001
"@ | diskpart | Out-Null
    Write-Verbose "Dismounting disk image"
    Dismount-DiskImage -ImagePath $path
    #write the new disk object to the pipeline
    Get-Item -Path $path
} #if $disk
} #end function

Here’s the function in action.