How to convert Multiple VHDs to VHDX with PowerShell

Table of contents

When working with Hyper-V, you may run into a scenario where you need to make a mass conversion of VHD files. Maybe you’re migrating from Hyper-V 2008R2 to Hyper-V 2012 R2 and want to convert all the VHDs to VHDX format; or maybe you want to change a few of your VHDs over to dynamic disk. Using PowerShell to do this can make this process easier, allowing for IT pros to simply type in a command and wait for the changes to complete.

Converting Multiple VHDs to VHDX

Step 1: First, to convert multiple VHD files into VHDX, all VMs that are using those VHDs must be powered off. This is because the disk is taken offline when the conversion process is started. To get started, open up a PowerShell console on the Hyper-V host. In this example we will use the directory C:ClusterStorageVolume1VHDs for our VHD storage and we will convert all VHDs in that folder to VHDX.

1 - VHDDirectories

Step 2: I powered all VMs off that are using these VHDs and am now ready to convert all my VHDs to VHDX. The Convert-VHD cmdlet simply copies the file specified and creates a new virtual hard disk file with whatever format is specified. Converting just a single VHD file would use the following syntax:

Convert-VHD -Path "c:clusterstoragevolume1VHDsdc01.vhd"  -destinationpath "c:clusterstoragevolume1VHDsdc01.vhdx"

But, since we want to convert all the VHD’s at once, we will need to change the syntax around. First we use the Get-ChildItem cmdlet and specify the directory including all .vhd files:

Get-ChildItem -Path "C:ClusterStorageVolume1VHDS*.vhd"

This will provide a list of all .vhd files in the directory. Running just this part of the command will give us the following information:

2 - List All VHDS

Step 3: Next we take the information we gathered so far and pipe it through to the ForEach-Object cmdlet. This cmdlet will take all the .vhd objects that we’ve gathered and perform whatever script is specified in the {} brackets for all objects. For more information on using the ForEach-Object cmdlet, refer to  Microsoft’s TechNet article:

Get-ChildItem C:ClusterStorageVolume1VHD*.vhd | ForEach-Object { }

So, essentially we will need to use the Convert-VHD cmdlet between the {} brackets to run the conversion against all of our collected .vhd files. But, just like when converting a single VHD in our example above, we need to include the path of the VHD files we want to convert as well as the destination of where we want to put the newly converted files.

However, since we are running the Convert-VHD cmdlet against multiple VHD files, we will use $_ as a placeholder to refer to the previously piped in object (which is the data obtained from the Get-ChildItem cmdlet in our example) followed by a period so we can access the property FULLNAME. So for the –path parameter we will specify the full path of each object by using $_.FULLNAME. The FULLNAME property will give us the directory path, filename, and extension format that we need in order to properly execute Convert-VHD. So the -path parameter in our example will simply be:

-path $_.fullname

For the –destinationpath parameter, since we are going to be converting them to VHDXs we need to specify the .vhdx extension instead. This makes it a little trickier than the -path parameter. To get around this we create a string with the normal directory path and add on the $_.BASENAME property to get only the file name of each object (for example “DC01” without the .vhd extension). Lastly we add on the “.vhdx” string to specify that we want the VHD file converted to VHDX. So the -destination parameter will look like this:

-destinationpath ("C:ClusterStorageVolume1VHDs"+ $_.basename + ".vhdx")

Step 4: When we put everything together we can convert all VHDS in our directory with one line of code:

Get-ChildItem C:ClusterStorageVolume1VHD*.vhd | ForEach-Object {Convert-VHD -path $_.fullname -destinationpath ("C:ClusterStorageVolume1VHDs"+ $_.basename + ".vhdx") }

A progress display will appear indicating the progress of each conversion:

3 - ConvertStatus

It may take a while depending on the size of the VHD files, but at the end your VHD files will now have copied versions in VHDX format.

4 - VHDX Directory

Configure your VMs to use the new VHDX files, power them on, and reap the benefits!

12- Configure VHD

Converting Fixed Disk to Dynamic

Just like in the previous example, using the Convert-VHD cmdlet requires the disk to be offline.

Step 1: So the first step to converting VHDs from fixed to dynamic is to power off the VMs. Then simply open up a PowerShell console on the Hyper-V host. In this example we will continue to use the directory C:ClusterStorageVolume1VHDs for our VHD storage and we will convert all VHDs in that folder to dynamic.

5 - VHDS New Directory

Step 2: Place the new VHDX files in a sub-folder named “Dynamic”. This is because unless you are converting from VHD to VHDX (or vice versa), the destination path cannot be the same as the source path unless the file name changes, which we’re not doing in this script. The conversion can be done with the following line of code. It is similar to the format used when converting to VHDX, however we use the –vhdtype parameter to specify the disk format. Also we do not need to add the .vhdx extension in the -destinationpath parameter because we are not changing file extensions:

Get-ChildItem "C:ClusterStorageVolume1VHDs*.vhdx" | ForEach-Object {Convert-VHD -path $_.fullname -vhdtype Dynamic -DestinationPath ("C:ClusterStorageVolume1VHDsDynamic" + $_.name) }

A progress display will appear in the console for the progress of each conversion.

6 - Convert Dynamic Status

Step 3: By inspecting the new disk, we can see that the new disk is now dynamic.

7 - Convert Dynamic

Converting Multiple VHDs from a CSV file

You might come across a scenario where you just need to convert a specific list of virtual disk files. You don’t need to move those files to their own folder. Instead, simply create a comma-separated values (CSV) file.  Then, feed that CSV into PowerShell to perform the conversion.  In this example we will convert only the VHDs of our remote desktop servers to dynamic.

8 - ConvertCSV Directory

Also note, there are multiple ways to make a CSV file, you could even create one with PowerShell using the ConvertTo-CSV cmdlet. However, for simplicity’s sake we will use Excel in this example.

Step 1: First, we create a CSV document in Excel called “DisksToConvert.csv”. We make two columns, one for the file path of the VHDX files we want to convert and the other for the directory that we want our copies to go to. In this example I saved the CSV file to the root of C.

9 - Excel CSV

One great thing about PowerShell is that it integrates very well with CSV files. What we did, by creating the CSV file is build a table with the information used by the two parameters in the Convert-VHD cmdlet (-path and -destinationpath).

Step 2: Using the Get-Member cmdlet to list the properties of our CSV file, you can see that the two columns we made in excel are now two properties that we can reference inside PowerShell.

10 - CSV Properties

Step 3: So using the following syntax, we tell PowerShell to convert only the VHDX files that are in the CSV file. Also we use the $_ placeholder again to refer to previous object piped in (data from the CSV file) followed by a period so that we can access the property or column of the csv file (for example $_.SOURCEPATH) :

Import-CSV C:DisksToConvert.csv | ForEach-Object {Convert-VHD -path $_.SourcePath -DestinationPath $_.destinationpath -VHDType Dynamic }

After the conversion job is complete, we can see our dynamic virtual hard disk files in the folder we specified. Success!

11 - CSV Dynamic Directory

 

Altaro Hyper-V Backup
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 "How to convert Multiple VHDs to VHDX with PowerShell"

  • Thank you, Luke!

    Nothing too complex or new but your post is very useful for beginner admins wanted to automate their job using PS. You can also provide samples to mass extending and/or shrinking VHD/VHDX disks.

    • Luke Orellana says:

      Thanks! Make sure to check for checkpoints before doing any mass disk extending or shrinking via PowerShell. It will not stop or warn you and, if there is an existing checkpoint, the job will break the VHD/VHDX disk.

  • Thank you, Luke!

    Nothing too complex or new but your post is very useful for beginner admins wanted to automate their job using PS. You can also provide samples to mass extending and/or shrinking VHD/VHDX disks.

  • Serp says:

    This is what I came up with to do the same thing…

    (Get-VM -Computer hyperv-node-01,hyperv-node-02,hyperv-node-03 | Where-Object -Property State -eq “off” ).HardDrives.Path | Where-Object -FilterScript { $_.EndsWith(“.vhdx”) -eq $false } | ForEach-Object { Convert-VHD -Path $_ -DestinationPath $_.Insert($_.Length, “x”) -VHDType Dynamic -AsJob }

    It goes through the servers and finds all the machines that are shut off, find the VHD paths for them that haven’t been converted to VHDX then converts them in background jobs. You can use Get-Job to monitor them. Don’t forget to use Remove-Job to clean up afterwards, or remove the -AsJob, and let the one-liner work through your VHDs synchronously.

    • Great one-liner! The beauty of PowerShell is that there is more than one way of doing things and the creativity is in the creator hands. Using jobs will definitely help if there are many VHDs to iterate through. Thank you for sharing this.

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.