Hyper-V How To: Shrink a VHD File

Our first guest post (https://blogs.technet.com/tonyso/archive/2008/10/07/your-hyper-v-blog-post-here.aspx) comes from Jeremy Hagan. Thanks Jeremy!

Background:

One problem I have had when trying to do my first round of P2Vs for my Hyper-V implementation that I didn’t notice in testing is that you can’t size the VHD of the VM smaller than the corresponding physical disk of the original machine.  Since it is best practice to have one LUN per VM and it is also best practice to have fixed VHDs, this can cause a lot of wasted SAN space.  In my planning for the P2V process I monitored the target servers for 1 month so that I could get an idea of the CPU and memory use, but also the data growth.  I decided to plot the percentage of data growth, extrapolate this for 3 years and then add a 15% premium on top of this then rounded up to the nearest 5 GB.  This ended up being the SAN LUN size for each VM.  The problem I then had was that I didn’t have enough SAN space available to P2V my machines.

What I decided I needed to do was to P2V each machine to a sufficiently large scratch LUN, then shrink down the VHDs to be the size I wanted then migrate the VM off the scratch LUN to the desired LUN.  Easy right?  Well maybe not.  After much searching I have managed to come up with a recipe for doing this.  See the example below.  In this case it was a Windows XP machine with a 40 GB hard disk, but only 9.5 GB of data.  If you want to play with this I recommend making a copy of the VHD file as a backup rather than taking a snapshot.  Snapshots will interfere with final step using VHDResizer to create the final fixed VHD file.

Steps (assumes a single disk with a single partition):

  1. Do your P2V as normal to a scratch disk that is big enough to hold all of the data from your physical machine and choose Dynamic for your disk types.
  2. Bring up your physical machine, allowing any chkdsk that wants to run to complete successfully.  Boot into the operating system and defrag your C: drive:
    • Defrag C: -f –v
  3. When the defrag has finished, use the precompact.exe tool (which was part of a Virtual Server R2 Service Pack 2) to write out all of the free space to zeros.  Failure to complete these last two steps has resulted in the following steps from failing.  I downloaded precompact.exe from here.
  4. Now use Hyper-V manager to Compact the disk.
  5. Connect the machine to bootable ISO Linux distro that includes NTFSRESIZE and FDISK.  I was playing around with free GHOST alternatives at the time and just happen to be using this one.
  6. Boot to linux and login as root, then run:
    • Ntfsresize /dev/had1 –i
      This will report to you the smallest size to which you can shrink your NTFS MFT, like thisntfsresize1  
  7. Decide how small to resize your NTFS MFT to.  In this example the minimum is 9504 Mb.  I chose 9700 Mb so that the OS would have enough free space to boot cleanly.
    • Ntfsresize /dev/hda1 –s 9700M
      This will relocate any data needed then shrink the MFT.  ntfsresize2
  8. After this we need to shrink the partition, but the linux FDISK complains about the NTFS dirty bit, so we will allow our machine to boot to the OS and complete its chkdsk.  Detach the ISO and boot the OS allowing it to come all the way to the login prompt, do not skip the chkdsk.  For the sake of illustration I have logged in and taken a screenshot of Disk Manager showing the 9.6 GB NTFS, but a 37 GB Primary Partition,.diskmanager1 
  9. Now boot back into the Linux distro and login as root.  Now we will delete the current partition and create a new one around the data we have there:
    • Run fdisk /dev/had
    • Press p to print the current partition infooriginalpartition
    • Press d to delete the current partition
    • Press n to create a new partition:
      • Press p to select primary
      • Press 1 to create the first primary partition
      • Set the starting cylinder to be the same starting cylinder as the old partition (important!).  This is usually 1
      • Select the amount of space to use for the partition.  I chose only a little bigger than my NTFS size due to the vagaries of developers saying that 1 MB is 1000 KB or 1024 KB.  I put +10G
      • Press t to change the partition type and then select 7 for NTFS
      • Press a to make the partition bootable
      • Press p and review that the partition is correct
      • Press w to write the changes
        newpartition 
    • Boot to the OS again to check that everything is working.  For the purposes of illustration I logged in and took a screenshot diskmanager2 showing a 10GB partition and 27 GB of unallocated space. Shut down the VM.
  10. Now we are ready to use a nifty little utility called VHDResizer to make a new, fixed-size VHD file, preferably in the same folder as the original dynamic one, which you will delete when you are done.  VHDResizer is available here.
    • Run VHDResizer
    • Open the Dynamic VHD that was created by the P2V process
    • Enter a path and filename of your new VHD file
    • Choose Fixed
    • Enter the size in GB
    • Click Resize
      vhdresizer
  11. Now all we need to do is extend the partition and file system to take up the full disk.  I won’t spell out how to do it, but you can either boot to WinPE and use:
    • DISKPART:
      • SELECT VOLUME 0 or 1
      • DISKPART EXTEND
      • DISKPART EXTEND FILESYSTEM
    • Linux:
      • Use FDISK to delete the current partition and create a new one as above, but select the default for the end cylinder.
      • Run ntfsresize /dev/hda with no other arguments and it will extend to the maximum
  12. Finished

Now if you think that is laborious and annoying, you’d be right.  Hopefully Microsoft will come up with a better way to do this in the future.  Now before I get a million comments about this, I did try to just use WinPE and DISKPART SHRINK, but no matter how many times I defragmented, precompacted and compacted I couldn’t get the partition to shrink down to less than about 24 GB.