Hyper-V generation 2 virtual machines – part 8

Part 1: Introduction to generation 2 virtual machines
Part 2: Networking and boot order
Part 3: Storage
Part 4: Keyboard for Windows 8 & Windows Server 2012
Part 5: Kernel debugging
Part 6: Secure Boot
Part 7: FAQ
Part 8: Manually migrating generation 1 virtual machines to generation 2
Part 9: Installing from ISO
Part 10: Utility for converting generation 1 virtual machines to generation 2 (Convert-VMGeneration)

Forewarning, this is a very long article!

This part of the series on generation 2 virtual machines in Hyper-V talks about migration. Not “Live Migration”. Nor “Storage Migration”. Specifically it answers the question “Can I change the generation of a virtual machine, or migrate my existing guest operating system in a generation 1 virtual machine to generation 2?”

Let me answer it the short way first. No. There is no way in Hyper-V to change the generation of a virtual machine. Neither is there a means to migrate a generation 1 virtual machine to a generation 2 virtual machine. I could end the post there, but I guess my forewarning would have been a little premature…!

For those of you thinking – well what if I export a generation 1 virtual machine and import it as a generation 2 virtual machine after tweaking the export contents. My initial response would be that this is absolutely unsupported and does not work due to firmware differences among other things. Followed by a “good luck to you… taking the re-installation and application migration route may be quicker”

However, there is a longer answer which is supported in certain circumstances, due to the use of standard inbox tools and capabilities for deployment. Let’s rule out first what definitely cannot be migrated. The obvious categories are any virtual machine running a 32-bit guest operating system; any virtual machine not running Windows (although there may be other solutions I’m not aware of); any virtual machine running a version of Windows prior to Windows 8/Windows Server 2012.

So let’s drill in and do an example walkthrough of migrating a Windows 8 64-bit client guest operating system. I want to stress up front, the tools used here are absolutely NOT my area of expertise, so answers to any questions you might have will be few and far between. Also, it’s pretty in-depth and not for the feint-hearted to try.

For this example, I just used the defaults during the new virtual machine wizard, except increasing the memory to 1GB, to end up with a generation 1 virtual machine with one IDE attached boot disk, and a Windows 8 installation ISO inserted into an IDE attached DVD drive. No network is connected to the virtual machine, and no further storage is attached to a SCSI controller.

Here’s our unhappy VM who yearns to be a trendier generation. Let’s make him smile by migrating him to a generation 2 virtual machine. I want to emphasise also that the process I’m following below may NOT be perfect. In the screenshot, I have opened regedit to show that this is 64-bit Windows 8, and msinfo32 showing the BIOS mode is Legacy. The only other change to the VM from a default installation is the desktop background image.

clip_image002

Here’s the settings for this VM

clip_image004

And here’s the disk partition layout. Notice it’s a typical MBR style layout

clip_image005

At a high level, the steps I’m going to follow are:

  • Disable the recovery environment
  • Make a copy of the Windows image as installed
  • Create a new VHDX
  • Partition it in GPT format and make it bootable
  • Put the copy of Windows we previously made onto the new disk
  • Create a new generation 2 virtual machine and attach the new VHDX
  • Fix up the recovery environment

Besides a couple of minor gotchas, none of the above is rocket science. However, what I’m going to entirely ignore are considerations such as:

  • The source VM using a VHD (this should ‘just work’ actually following the same process, but changing VHDX to VHD where I refer to the source disk)
  • The source VM being multi-boot
  • The source VHDX having ‘unusual’ partitioning
  • Data or other utility partitions, including the optional recovery partition containing a ‘restore’ WIM, on the source VHDX
  • Any additional storage such as Fibre Channel or SCSI attached disks.
  • Making the new VM mirror the source VM in regard to configuration, particularly advanced configuration such as networking. But to be honest, pretty much everything under this category is not covered in this post.
  • Any form of disk encryption
  • Shared VHDX or guest clustering
  • Implications of replica
  • Implications of checkpoints (snapshots)

The first thing I’m going to do is to disable the recovery environment on the source machine while it is running, by running the following command from an elevated command prompt (note DON’T do this on the parent partition, it’s inside the VM!):

reagentc /info

clip_image007

If the output indicates the Windows recovery environment (WinRE) is enabled, disable it.

reagentc /disable

clip_image009

The reasons for disabling it are due to tight coupling between BCD and the recovery environment configuration. It’s just much simpler to migrate the virtual machine when it is not configured. We will re-enable it once the virtual machine has been migrated.

Next, I’m going to cleanly shutdown the generation 1 virtual machine (I really hope you don’t need instructions for that….), and loopback mount its VHDX.

From an elevated PowerShell prompt where the VHDX resides, run:

mount-vhd –readonly .\migrateme.vhdx

At this point you could do some fancy PowerShell to find out what drive letter has popped up on your local machine as the Windows installation for the generation 1 virtual machine. In my case, I just used Explorer and can see it’s drive H:. The system partition was also given a drive letter, G:. If no drive letters are present, it could be policy on the parent partition, but you can use diskpart or diskmgmt.msc to assign letters and/or online the disk.

We’re going to use dism to capture the image. Note that you can use regular inbox dism for this, it doesn’t need to be the version of dism from the ADK which I used in a previous part of this series. From an elevated prompt, run the following replacing H: with the appropriate drive letter for the Windows Installation.

dism /capture-image /imagefile:captured.wim /name:”Captured” /capturedir=”H:\”

The next job is to create a new VHDX which we will partition in a format suitable for use by a generation 2 virtual machine (note that MBR is required for generation 1, GPT is strongly recommended for generation 2). I am going to use GPT, as should you.

This gets a little tricky for sizing purposes, especially when your source disk is almost full. From the disk partition layout above, the source VHDX is 127GB, and during operating system installation, Windows created a 350MB System Reserved partition.

In a typical GPT layout, you’d have a 300MB recovery environment tools partition, a 100MB ESP, a 128MB Microsoft reserved partition, and the remainder for the system. There may be other utility partitions such as for push button reset as found on an OEM type of installation. This means that the OS partition size is slightly smaller than on an MBR same size disk using the Windows defaults. https://technet.microsoft.com/en-us/library/hh824839.aspx has a lot more information on this.

For this exercise, I’m going to assume there is ample space on the source image. To make this even more apparent, I’m going to create the new VHDX as a 30GB disk. I’m also going loopback mount it, find the disk number it’s mounted at, and get a few free drive letters I can work with. You should of course do appropriate sizing! From the elevated PowerShell prompt, run the following:

 

# Create a new VHDX
New-VHD .\migrated.vhdx –dynamic –size 30gb

# Mount the new VHDX
Mount-DiskImage c:\working\migrated.vhdx

# And find the mounted VHDX
$Target = Get-DiskImage c:\working\migrated.vhdx

# And find the disk number of the mounted VHDX on the parent partition
($Target | Get-Disk).Number

# And get some available drive letters
ls function:[d-z]: -n | ?{ !(test-path $_) } | select -last 3

clip_image011

From the output of the above commands, in my case the drive number is 2, and the three drive letters are X:, Y: and Z:. (Note you can just use explorer to get spare drive letters, and there’s a few ways of finding the drive number of the new VHDX.)

Now it’s time to create a diskpart script to partition the new VHDX. It is REALLY IMPORTANT to make sure you insert the right disk number, otherwise you risk losing all data on another disk on your system. For that reason, I’m not even going to put a number in the script below in case someone comes along and runs it. Here’s my script using drive letters X:, Y: and Z:. Save it as dp.txt

select disk INSERT_YOUR_DISK_NUMBER_HERE
clean
convert gpt

create partition primary size=300
format quick fs=ntfs label="Windows RE tools"
assign letter=
X set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"
gpt attributes=0x8000000000000001

create partition efi size=100
format quick fs=fat32 label=System
assign letter=Y
create partition msr size=128
create partition primary
format quick fs=ntfs label=Windows
assign letter=Z

list part

I expressly have not created a recovery image partition here. Please see the recommended configuration here: https://technet.microsoft.com/en-us/library/hh825686.aspx.

Next, partition your disk (again heed warning about the potential for data loss) by running the following from an elevated command prompt:

diskpart /s dp.txt

If all is good, you should see output similar to the following from the command prompt, and the drive letters showing up in Windows File Explorer.

clip_image013

clip_image015

At this point, we’re going to put or ‘apply’ the captured image from the source VHDX into the Windows partition on the new VHDX, drive letter Z: in my walkthrough. Run the following from an elevated command prompt:

dism /apply-image /imagefile:captured.wim /applydir:z:\ /index:1

clip_image017

This will take some time depending on the size of the capture image and your storage subsystem performance.

Now we need to configure the ESP (EFI System Partition) on the target VHDX. Run the following from an elevated command prompt, where Z: is the target Windows partition and Y: is the target ESP. To be safe, it would be prudent to running bcdboot from the source operating system, not the one on the parent partition, just in case of incompatibilities.

Z:\Windows\System32\bcdboot Z:\Windows /s Y: /f UEFI

clip_image019

At this point, let’s do a quick recap of where we are. We have

  • Created a new VHDX
  • Made it GPT and partitioned it.
  • Put a copy of the original Windows installation onto it
  • Created/configured a new BCD store to make the new Windows installation boot

A couple more things to do before we can see if the new VHDX actually works in a generation 2 VM. First we need to perform a workaround for a bug in Windows to avoid the drive letter for the ESP on the new VHDX persisting after an unmount. (If you don’t do this, the drive letter will persist and you’ll have to reboot your parent partition to get rid of it). Then we can unmount it. From an elevated PowerShell prompt run the following. Change NNNN to refer to the disk number of the new VHDX. PartitionNumber is 3 as that is where the ESP is based on our diskpart script from earlier. Y is the temporary drive letter (again in the diskpart script) we used for the ESP.

Remove-PartitionAccessPath –DiskNumber NNNN –PartitionNumber 3 –AccessPath Y:\
Dismount-DiskImage c:\working\migated.vhdx

clip_image021

Some of you may be asking why I even assigned a drive letter X: above as I didn’t touch the recovery partition. It’s more for completeness, and when I was also playing around with the recovery environment. You don’t need that drive letter to follow along.

As we no longer need from the source VHDX, we can dismount it by running the following from our elevated PowerShell prompt:

dismount-vhd .\migrateme.vhdx

Now we’ll create a new generation 2 VM using a similar configuration to the source VM, but attaching our migrated VHDX. I won’t walk you through that (as you all know how to do it…), but here it is:

clip_image023

Let’s fire it up and see what happens!

…The good news is that it boots.
…You see the spinny dots under the Hyper-V UEFI firmware logo
…The screen goes blank
…More spinny dots
…”Getting devices ready”

(Can you feel the tension building? J)

…”Getting ready”
…”Hi”
…“While we’re getting things ready”
…“Check out the new way to use Windows”
…”After your PC is Ready”

and so on until

clip_image025

SUCCESS!!! We can see the desktop background is from our original virtual machine. Let’s take a look in Windows Explorer and diskmgmt.msc to see:

clip_image026

We have had the nice side effect of having resized our Windows partition as well. Unfortunately Disk Management doesn’t show all partitions, so let’s open diskpart from an elevated command prompt to take a closer look.

clip_image028

Remember that the above process did not create a recovery partition for PBR. You should adjust the procedures above following ADK guidance if that is how you need the target virtual machine configured.

Remember also that we didn’t enable the recovery tools. We can see that it is configured by attempting to boot into RE.

clip_image030

Re-enabling is fairly straightforward. Here we enable RE, show it’s enabled and set the system to boot next time to RE.

reagentc /enable
reagentc /info
reagentc /boottore

clip_image032

And a reboot shows that the recovery environment is working.

clip_image034

What remains at this point is for you to mirror the virtual machine configuration such as advanced networking, processors, storage and so on from the source generation 1 virtual machine to the target generation 2 virtual machine.

So that’s it. Although somewhat involved, that is what it takes to convert a workload from generation 1 to generation 2. You may, of course, decide that a clean installation may be simpler.

The last thing I wanted to touch on was the inevitable question that some of you will no-doubt ask when manually walking through these steps on your own VM. The one of “I forgot to disable RE before migration – how do I fix it up?”. Below are my rough notes more than anything else, especially as sources of information on this are a little thin on the ground that I could find. But hopefully it will give you some good pointers on what to look. It certainly is possible to re-create the right entries manually. I’ve done it, but this isn’t a walkthrough as such. There is some tight coupling between the RE configuration held on the file-system and in BCD. Note that this method is UNSUPPORTED – the supported way is to go back to the start, run reagent /disable before starting the migration, and /enable once the migration is complete.

The first thing to know is when RE is configured, winre.wim is moved to the recovery tools partition. As we didn’t migrate that, it won’t be present in the Windows partition on the target. You can grab that from the source VHDX loopback mounted. It will be present under \Recovery\{GUID} or \Recovery\WindowsRE, generally in the system partition of a PCAT system. It will be in a hidden directory, and may be marked as a system file. Further, the partition is hidden so you will need to assign a drive letter. Let’s assume you have the source VHDX in my example loopback mounted to drives H: and I: this time (it was G: and H: during the earlier part of this walkthrough). The Windows installation is on drive I:, the System Reserved partition is on drive H:

Here’s how you would backtrack from a mounted VHDX of a generation 1 virtual machine which still has RE enabled. From the output of reagentc, you can determine the status (enabled), the location, and the BCD GUID.

clip_image036

From the location, we can use diskpart (even though we happen to already know it’s on drive H: ) to find the drive letter where the WinRE WIM file will be located.

clip_image038

And if we examine the H: drive, we can see WinRE.WIM is present, just in a hidden folder.

clip_image040

So that’s where you would grab winre.wim from. Put a copy (removing the system and hidden attribute using attrib –s –h) in \windows\system32\recovery on the new windows installation.

For completeness, go back to the screenshot of reagentc output which also lists the GUID for the BCD entry which we can examine using bcdedit pointing to the BCD database. There is an OSLoader entry present, which you can see is for PCAT due to the use of winload.exe, not winload.efi:

clip_image042

And if you look carefully where I’ve circled, the entry refers to another GUID. By default the first 8 digits are +1 in hex, so here you see the 8th digit “F” for the GUID entry with the 8th digit “E”. (This isn’t a requirement if you were to manually rebuild the BCD entries)

If you look at that other device entry, you can see it’s a ramdisk device.

clip_image044

And finally, the other piece of the puzzle is in the UTF-8 encoded file \windows\system32\recovery\reagent.xml.

clip_image046

I’ve circled a few things. One is the path to WinRE.wim. Next is some ID starting 313 which I’ll come to in a second. Offset is a partition byte offset, and if you go back to the diskpart screenshot above, you can see it’s where the recovery tools partition starts on the disk. And also the InstallState indicating it is on. What’s that ID? And why does WinreLocation have a null GUID in it? The answer is simple, it’s the disk ID of the disk on an MBR disk. On a GPT disk, the ID would be zero, and the GUID populated instead. (You can find disk IDs from diskpart, select disk n, detail disk.)

So now we know what we need to fix up in the migrated VHDX. Unfortunately reagentc is a little “temperamental” and needs a little coaxing (in other words, it tends to just fail without much information as to why). What you could do is manually recreate each of the BCD entries by hand, and recraft reagent.xml in notepad carefully to match. That (trust me having walked through it) is painful.

Bearing in mind that the new VHDX will not have any recovery BCD entries (OSLoader or Device), we will let reagentc do the work for us instead. We’ll do this inside the migrated VM. Keep a copy of reagent.xml, just in case, then with notepad (it’s not that friendly though as you will see), carefully edit reagent.xml to change the three circled values to look like the following:

clip_image048

Now you can simply use reagentc to enable WinRE (make sure you did copy winre.wim to the \windows\system32\recovery folder first). I’ll follow the same path to follow through what changes have been made. Reagent.xml now has a populated WinreBCD and WinreLocation, and InstallState is 1.

clip_image050

From the output above, confirming the GUID of the disk (starting daf2…) containing a hidden utility partition for recovery.

clip_image052

And the partition can be determine from the offset (135266304). I’m going to give the partition a drive letter temporarily (R:). You can remove this latter using the diskpart “remove” command.

clip_image054

Confirming the right files are in the \recovery\{GUID} path

clip_image056

And now examining the BCD store entries.

clip_image058

Notice again the “+1”, and that the path is to an EFI application for the loader.

So that’s pretty much how to convert from a generation 1 to a generation 2 virtual machine in circumstances where it is possible. For a much easier option, you might want to wait just a little longer :)  In the next part, part 9, I’ll talk about booting from ISO media.

Cheers,
John.