Creating a Generation 2 Disk with PowerShell

Table of contents

 

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. If you’ve been reading this blog for a while, you should have seen my other articles on creating and managing VHD and VHDX files using Windows PowerShell. 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 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:VHDGen2Demo.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.

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.

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. I’m 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.

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
exit
"@ | diskpart

 

Next, you need to create the System partition. You might think it would be as easy as the Recovery partition and I certainly was 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:ScriptsNew-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

I saved the output to a variable so that I could grab the partition number which I 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 I 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 I start with a 25GB drive, subtracting the Recovery Tools (300MB), System (100MB), MSR (128MB) and the 15GB recovery image, I’m left with a smaller partition. So instead of the previous command I can run this.

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

Finally, I 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, I’m using maximum size but it should be pretty close the 15GB that should be left.

At this point you have some options, which I’ll cover in another article. But for now, this is complete. 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.

[email protected]{
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 I wrote to create a Gen 2 disk.

#requires -version 4.0
#requires -modules Hyper-V,Storage
#requires -RunAsAdministrator

Function New-Gen2Disk {
<#
.SYNOPSIS
Create a Generation 2 VHDX
.DESCRIPTION
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 
128MB MSR
Windows
15GB Recovery Image

The size of the windows partition will be whatever is left over give or take a few KB.

.EXAMPLE
PS C:> New-Gen2Disk d:disksdisk001.vhdx -dynamic -size 50GB
#>

[cmdletbinding(SupportsShouldProcess)]
Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter the path for the new VHDX file")]
[ValidateNotNullorEmpty()]
[ValidatePattern(".vhdx$")]
[ValidateScript({
  #get parent
  if (Split-Path $_ | Test-Path) {
    $True
  }
  else {
    Throw "Failed to find parent folder for $_."
  }

})]
[string]$Path,
[ValidateRange(25GB,64TB)]
[uint64]$Size=25GB,
[switch]$Dynamic,
[UInt32]$BlockSizeBytes=2MB,
[ValidateSet(512,4096)]
[Uint32]$LogicalSectorSizeBytes=512,
[ValidateSet(512,4096)]
[Uint32]$PhysicalSectorSizeBytes=512

)

#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
    Return
}

#create the VHDX file
Write-Verbose "Creating the VHDX file for $path"
[email protected]{
 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
  Return
}

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
exit
"@ | 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 
exit
@" | 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
exit
"@ | diskpart | Out-Null

    #dismount
    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.

If you are thinking building new virtual machines as generation 2, then I think this tool will come in quite handy. In my next article we’ll explore some other steps you can take with the VHDX file prior to creating your VM.

 

Threat Monitor
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

5 thoughts on "Creating a Generation 2 Disk with PowerShell"

  • Patrick says:

    Hi Jeffrey. Nice post, thanks a lot. I try to create a VM template based on such a VHDX. I use imagex.exe to apply my customized WIM file to the system partition. So far so good, works. But when creating the boot entries with “V:windowssystem32bcdboot V:windows /s V:” (where V is the mounted system partition of my virtual disk), i am not able to get the vhdx bootable. Do you have any tips how i can manage this?

  • David Jones says:

    I got an error about invalid GPT type.
    according to
    https://technet.microsoft.com/library/fd9eaa13-fd1a-4c95-a03e-842dbd1604be(v=wps.630).aspx
    The System Partition should be {c12a7328-f81f-11d2-ba4b-00a0c93ec93b}

  • Malina says:

    I know it’s very old, but just in case others are wondering: In his example, he created a efi partition, which is what the (uefi) bios will use to boot the gpt disk. Thus, it is very likely, that you should have assigned a letter to the efi partition (system), and used THAT letter instead of V: at the end.

    Again, not tried this, but just makes sense, and is for anyone reading it nowadays.

    Mali

Leave a comment or ask a question

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.