Jump to content
Welcome to our new Citrix community!

Tech Brief: User Layer Management Utility - Converting VHD to VHDX

  • Contributed By: Rob Zylowski Special Thanks To: Steve Beals


The User Layer Repair Utility is a tool created by Rob Zylowski. It enables administrators to select an OS layer, list all the app layers created with that layer, and match the layers to their associated repair JSON file.

VHDX Conversion Feature

The new feature to convert VHDs to VHDX and works similarly to the other features of the utility. The user interface is shown in the following image.


After opening the utility, you click the Convert tab. Then, if you have not run it already, click Get OS Layers (1), and the utility queries the App Layering appliance to retrieve the defined OS layers. Then, choose the desired OS Layer (2), and the utility enumerates the user layers for that OS layer. This is only required if you want to choose certain User Layers to convert. You can select one or more User Layers and choose to convert the selected User Layers (4). You can also run a script to convert all user layers (5) in the User Layer path defined in the utility settings. The Convert All script is available from the user interface, but it is more likely that you run it using a scheduled task. This is discussed later in the article. The actual conversions are performed using PowerShell scripts.

The Conversion Status column is updated when conversions are complete. Possible Status entries are:

  • SkippedExists
  • SkippedInUse
  • SkippedTime
  • Success
  • Failure

Converting All User Layers

If using this option, you schedule the PowerShell script called ConvertULAll.ps1, detailed in the PowerShell Scripts section, to run regularly. The defined path for User Layers is used to find and convert all the User Layers. The script has been written to support multithreading and can run up to eight jobs in parallel. It is recommended that you set the number of simultaneous jobs to match the number of CPUs in the machine running the script and configure that VM to have at least 4 CPUs. When the script runs, it first checks to make sure the VHD is not in use, then it checks to see if there is already a VHDX for the User Layer. If neither is true, the script compresses and converts the VHD. The script also has time limits. So, you can tell it to stop starting new conversions after it runs for 10 hours. This way, the conversion only runs during off-peak hours. This checking process allows the script to be run repeatedly until there are no more VHD files to convert. The script also has full logging so the administrators can determine all the outcomes for each script run and each converted file.

Utility Setup

To use this new functionality, there are some requirements for preparing the machine the conversion process runs on and settings in the utility.

The conversion process relies on the features of Hyper-V and the Hyper-V PowerShell modules. These are used rather than qemu-img because they allow better multithreading when performing mass conversions. However, this does mean that to use this feature, the Hyper-V role, and Hyper-V PowerShell Modules must be installed on the machine running the scripts. If this machine is a virtual machine, then the virtual machine must support virtualization. In VMware, this is called Enable Hardware Assisted Virtualization to the guest OS in Hyper-V, this is ExposeVirtualizationExtensions. To install Hyper-V on Windows 10, you need to add several features as seen in the following image.


This installs Hyper-V and the Hyper-V PowerShell Modules. It requires a reboot. Once you complete these steps, the new script work.

First, when you download the script zip file, unblock it before you unzip it. Then, all the files in the zip are unblocked. Also, the convert functionality does not work properly if there are any spaces in the path to the utility folders. Therefore, it's best to create a folder to house the utility of the root of the disk you install it on.

After unzipping the script, a reg script (HTARunAS.reg) is included in the download, configuring the registry to allow you to run hta files As Administrator. It is required to run the utility that way. If you double-click this reg file, it causes the registry to change. If you don’t want to use this, you can run the hta from an administrative command prompt.

To open the utility, right-click the hta file and run as administrator. Then, use the setup tab to configure the settings in the utility.


The relevant entries for the convert feature are A, B, D, and H. Most are self-explanatory for D or 4 above you can enter a share path where the user layers you want to convert are. The utility looks in all subfolders of the path specified. You must also set all the settings in section H, which is listed as number 8. The options are defined as follows:

DelAfterConvert: This setting determines if the conversion script will remove the old VHD file after creating a VHDX file. During testing, leave the old file in case the conversion fails. Be careful with this setting, as these files are large, and you are duplicating them if you choose not to delete them.

CompressBeforeConvert: This setting defines if the VHD is compressed before it is converted. Compression is recommended because it can significantly reduce conversion times.

VHDXSubFormat: This defines the type of VHDX file created. Dynamic uses the least space, while fixed performs better.

SimultaneousConversions: This setting has no effect When VHD files are selected and converted within the UI. If using the Convert All User Layers script, this determines how many simultaneous conversions run. This can be no more than the number of CPUs on the computer running the script.

MaxConversionHours: Using the Convert All User Layers script determines how many hours the script can run. Save the required settings, and the utility is able to be used.

The PowerShell Scripts

There are two PowerShell scripts developed for the convert feature named ConvertUL.ps1 and ConvertULAll.ps1


This script is used to convert a single VHD file to VHDX. It takes the following parameters:

$EncodedPathToVHD: The full path to the VHD file, including the “.vhd” encoded as a Base64 string.

$DelAfterConvert: a switch that tells the script to remove a VHD after successful conversion to VHDX.

$CompressBeforeConvert: a true or false parameter to tell the script whether to compress the VHD before converting it. Unless the VHDs have been pre-compressed, this is highly recommended to speed up the conversion process.

$VHDXSubformat: The format can be set to dynamic or fixed. Fixed gives better performance, but it creates full-sized VHDX files and take a long time to convert. Dynamic is recommended.


All of these parameters are set in the utility setup and never need to be adjusted in the script.

The process the ConverUL script goes through is as follows:

  1. Import the HyperV Module

  2. Create Log Folders if necessary.

  3. Create a Log File for this conversion based on the User Layer Path

  4. Check to see if the file is locked

    a. If its locked, log, return status, and exit the script

  5. Check to see if there is already a VHDX file for this User Layer

    a. If there is a log, return status and exit the script

  6. If enabled, compress the User Layer VHD

    a. MOUNT-VHD -Path "$PathToVHD" -ReadOnly

    b. OPTIMIZE-VHD -Path "$PathToVHD" -Mode Full

    c. DISMOUNT-VHD -Path "$PathToVHD"

  7. Convert VHD

    a. Convert-VHD -path "$PathToVHD" -destinationpath "$PathToVHDX" -VHDType $VHDXSubformat <-DeleteSource>

  8. Check to see if the VHDX was created

  9. Report and Log Status


This script finds all VHD files under the share path defined for User Layers in the utility settings and then converts them using the ConvertUL.ps1 script. Be careful with this path if you share a file with other uses. For example, if the User Layer share is on the same path as your Elastic Layer share, go to the Users folder when defining the settings path. Otherwise, the utility will also convert all the elastic layers.

This script takes a single parameter: how many simultaneous jobs to run. If no parameter is passed, it defaults to 4. The script uses runspaces to manage the jobs and reports batch statistics at the end of the run. The script also has a time limit and will not start any new jobs after reaching the time limit. At a high level, the script uses this logic:

  1. Get all VHDs recursively under the User Layer Share path defined in the utility settings.

  2. Create a DataTable to keep track of the disk conversions

    a. Add VHD Path, Encrypted VHD Path, and so forth to the DataTable

  3. For each row in the DataTable, convert the VHDs using the defined number of simultaneous jobs

  4. Whenever a job is completed, check the time limit. If over the time limit, do not add new jobs

  5. When all the jobs are completed

    a. Go Through each Line in the DataTable

    b. Calculate batch statistics and add disks into Batch Arrays

    c. Log any errors during processing

    d. Log Batch Statistics

    e. Log Total run time

Scheduled Task

To create scheduled tasks to run the conversion scripts, you need to be logged into the machine where the script will be run. Create the scheduled task as follows:

General Tab

Change the user to the user that runs the script. Since the script connects over the network to the shares with user layers, you must use a user here that has administrative permissions to the share and is an administrator on the system running the script. You also have to add the user’s password.

  • Select to run whether the user is logged in or not.
  • Select Run with Highest Privilege.
  • Configure for Windows 10/11

Triggers Tab

  • Define the task schedule to run when desired.


  • Start a Program
  • Program/Script : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
  • Add Arguments: C:\ConTest\Powershell\convertulall.ps1
  • Start in: C:\ConTest\Powershell
  • Assuming c:\contest is the folder where you installed the utility


  • Stop the task if it runs longer than you want it to. This should be, an hour more than you set the script to run for in its settings.


Logs are important when running the scripts using a Scheduled Task. The script creates several logs. There is a master log for the ConvertULAll script called ConvertAllJobs with a date time stamp added to the name. This lists any errors in calling the conversion scripts and the batch results. A log file is also created for each VHD that gets converted which is stored in the Logs\VHDS folder under the name of the VHD being converted.

Example ConvertAllJobs Log


The log for the ConvertAllJobs script is in the logs folder, and it says when each VHD file was started and, at the end, show the batch statistics for the run.

Example for a VHD (zdc2-unidesk-Users-ZYLOWSKI_testuser1-110000_Win10-testuser1.txt)


The VHD-specific logs are in the Logs\VHDS folder. This file shows any errors during the compression or conversion process and success or failure.

To download the new version of the script, go to the end of the original blog on the User Layer Repair utility.

User Feedback

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Create New...