Automate VHD Offline Defrag for Citrix Provisioning Server

This article, Automate VHD Offline Defrag for Citrix Provisioning Server, shows you how to defragment your VHD files with PowerShell.

Change Log
14.10.2017: new check in main script to verify the existence of the VHD(X) file.
15.10.2017: 1) fellow Citrix CTA Trentent Tye mentions two important caveats concerning VHD(X) files and block size; 2) this blog post now offers a second script which supports command line arguments (based on a suggestion from Citrix CTP Carl Webster). Thanks a lot for your input guys!
17.11.2017: major issue solved for Windows 2012 R2 and higher: set the Unique Disk ID back to the original one before unmounting the VHD(X) file. When mounting the VHD(X) file on the same machine where it was created, a disk collision occurs as soon as the VHD(X) file is taken online. As soon as the disk is taken online, a new Unique Disk ID is set, which makes the VHDX file unbootable in PVS.

Table of Contents

Introduction

Note: for the sake of easy reading, I use the abbreviation VHD in this article, but all information and code also applies 100% to VHDX files.

When deploying VHD files with Citrix Provisioning Server in combination with RAM Cache with Overflow to Disk, it is very important to defragment your virtual disks. In the article Size Matters: PVS RAM Cache Overflow Sizing, Citrix makes two very clear statements. The first one is:

“Defragmenting the vDisk resulted in write cache savings of up to 30% or more during testing.”

The second statement refers directly to offline defragmentation (as opposed to online defragmentation):

“Defragment the vDisk by mounting the .VHD on the PVS server and running a manual defragmentation on it. This allows for a more robust defragmentation as the OS is not loaded. An additional 15% reduction in the write cache size was seen with this approach over standard defragmentation.”

Defragment a stand-alone VHD file

Fellow Citrix CTA Trentent Tye mentions the following caveats:

Manually defragment a stand-alone VHD file

The manual process to defragment a stand-alone VHD file is as follows.

Open the Computer Management console. With a right-mouse click on Disk Management select Attach VHD.

Automate VHD Offline Defrag for Citrix Provisioning Server - Computer Management attach VHD

Locate and select your VHD file. You can use a local or network path.

Automate VHD Offline Defrag for Citrix Provisioning Server - Computer Management attach VHD browse window

After having attached the VHD, it will show up in the Computer Management console as an additional disk. Modern operating systems have at minimum two partitions; a partition called System Reserved which serves as the boot partition and the C: drive containing the Windows files and folders.

Automate VHD Offline Defrag for Citrix Provisioning Server - Computer Management VHD attached

The partition you want to defragment is the one with the Windows files and folders, in our case drive F:. With a right-mouse click on drive F:, select Properties. In the new windows that opens, go to the tab Tools and click the button Optimize.

Automate VHD Offline Defrag for Citrix Provisioning Server - Computer Management optimize drive

Another windows opens. Select the F: drive and click the button Optimize. The defragmentation process starts, which may take a while.

Automate VHD Offline Defrag for Citrix Provisioning Server - Computer Management defrag drive

The following paragraph describes how to automate this process using PowerShell .

Automatically defragment a stand-alone VHD file with PowerShell

In a fully automated installation process of your master target device, the automatic offline defragmentation of your VHD file should be included as well. This is certainly true if you are using software deployment technologies such as Microsoft SCCM or MDT.

One of the last tasks when creating your master target device is most likely the execution of the p2pvs.exe or imagingwizard.exe to capture your VHD image. This image capture tool is included in the Citrix Provisioning Server Target Device software.

After the VHD file has been created, you can continue, within the same PowerShell script, with the offline defragmentation of the newly created VHD file.

For this purpose, I have prepared a script, complete with detailed logging and error handling. You can run this script by itself, or you can copy the blocks of code you need to your own scripts.

Note: many thanks to Salim Hurjuk (@salimhurjuk) for his help testing this script!

The above script can be used as follows:

  1. Prepare a VHD file. Any VHD file from any operating system will do.
  2. Copy the VHD file to an installation directory, either on the local computer or on a network share (UNC path). For example: C:\Temp\MyVirtualHardDisk.vhdx or \\MyServer\MyShare\MyVirtualHardDisk.vhdx.
  3. Copy the above PowerShell script to a new PS1 file (e.g. VHDOfflineDefrag.ps1) and add this file to a directory on the server where you would like to execute it.
  4. Edit the PowerShell script and modify lines 78 to 81:
    • $BaseLogDir: this variable contains the path to your log directory.
    • $PackageName: this variable contains the package name (the name of the log directory and log file is based on the package name).
    • $VHDFileToDefrag: this variable contains the path and name of the VHD file that needs to be defragmented. Enter the path to the installation directory you created in one of the previous steps.
    • $DefragFile: this variable contains the executable name of the defragmentation program you would like to use. The default defragmentation tool included in Windows is defrag.exe. Another possible defragmentation tool is df.exe (= Defraggler) from CCLeaner (previously Piroform) (the same company as from CCleaner).
  5. Execute the PowerShell script:
    powershell.exe -file “C:\Temp\VHDOfflineDefrag.ps1”
Note: in case of an error you may have to execute the PowerShell script as follows:
powershell.exe -executionpolicy unrestricted -file “C:\Temp\VHDOfflineDefrag.ps1”

Let’s take a closer look at the contents of the script:

  • Up to line 120, the code mainly consists of variables, functions and logging.
  • Enter your custom variables in lines 78 to 81.
  • Lines 119 to 128 check if the VHD file specified in the variable $VHDFileToDefrag in line 78 exists. If not, an error is thrown.
  • In line 133, the currently mapped drives are retrieved. We will need this information later in the script, to compare the currently mapped drives with the new one(s) created after mounting the VHD file.
  • Lines 138 to 141 retrieve the currently available disks. We will need this information later in the script, to compare the currently available disks with the new one(s) created after mounting the VHD file.
  • Lines 143 to 151 retrieve the Unique Disk ID from the disk containing the boot partition. We need this ID later in the script. When mounting the VHD(X) file on the same machine where it was created, a disk collision occurs as soon as the VHD(X) file is taken online. As soon as the disk is taken online, a new Unique Disk ID is set, which makes the VHDX file unbootable in PVS. Before unmounting the VHD file the original Unique Disk ID needs to be set once again. If this action is not taken and the Unique Disk ID is overwritten, the boot process fails and results in error 0xc000000e:
    Automate VHD Offline Defrag for Citrix Provisioning Server - Boot error 0xc000000e Unique Disk ID
  • Lines 155 to 174 contain the code to create the file diskpart_mount.txt in the current user’s %Temp% folder. This file contains the commands required by DiskPart to mount the VHD file:
    • SELECT VDISK FILE=C:\Temp\MyVirtualHardDisk.vhdx
    • ATTACH VDISK
    • ONLINE DISK
  • Lines 178 to 199 contain the code to actually mount the VHD file, using diskpart.exe and the DiskPart configuration file diskpart_mount.txt. DiskPart is launched using the PowerShell cmdlet Start-Process.
  • In line 205, the currently mapped drives are retrieved (again), which will now include the drive(s) of the mapped VHD file.
  • Lines 209 to 253 contain the code to retrieve the drive letter that needs to be defragmented. In case of new operating systems such as Windows 10 and Windows Server 2016 (all versions and builds), the VHD file contains two partitions: a boot partition with the name System Reserved and the “C:” drive containing all Windows files and folders. The PowerShell script automatically checks if one or two new drives were added and determines which drive needs to be defragmented.
  • In line 258, the currently available disks are retrieved (again), which will now include the disk of the mapped VHD file.
  • Lines 262 to 292 contain the code to determine the disk number of the newly attached VHD file. The original Unique Disk ID is (re)set to this disk number (= VHD file).
  • Lines 296 to 309 is where the actual defragmentation of the drive is executed, using the PowerShell cmdlet Start-Process. The defragmentation tool used is the one defined in the variable $DefragFile in line 81.
  • Lines 313 to 335 contain the code to create the file diskpart_unmount.txt in the current user’s %Temp% folder. This file contains the commands required by DiskPart to unmount the VHD file:
    • SELECT DISK=3
    • UNIQUEID DISK ID=2A23B239
    • SELECT VDISK FILE=C:\Temp\MyVirtualHardDisk.vhdx
    • DETACH VDISK
  • In the last part of the script, lines 339 to 350, the Unique Disk ID is set and the VHD file is unmounted, using diskpart.exe and the DiskPart configuration file diskpart_unmount.txt.

Now you can copy the VHD file to your Provisioning Server store and create your vDisk.

Note: in case you would rather parse the parameters at the command line (instead of changing the values directly in the script), I have created a copy of the above script and added command line arguments.

The following arguments are available.

  • -VHDFile -> mandatory: enter the name of the VHD(X) file
  • -DefragFile -> optional (default value: “defrag.exe”)
  • -LogDir -> optional (default value: “C:\Logs”)
  • -PackageName -> optional (default value: “VHDOfflineDefrag”)

Examples:

  • powershell.exe -ExecutionPolicy ByPass -File “C:\Temp\VHD Offline Defrag.ps1” -VHDFile “C:\Temp\MyVirtualHardDisk.vhdx”
  • powershell.exe -ExecutionPolicy ByPass -File “C:\Temp\VHD Offline Defrag.ps1” -VHDFile “C:\Temp\MyVirtualHardDisk.vhdx” -Logdir “C:\MyLogDir” -Packagename “DefragMyVHDFile”
  • powershell.exe -ExecutionPolicy ByPass -File “C:\Temp\VHD Offline Defrag.ps1” -VHDFile “C:\Temp\MyVirtualHardDisk.vhdx” -DefragFile “C:\Temp\Defraggler.exe” -Logdir “C:\MyLogDir” -Packagename “DefragMyVHDFile”

You can download the script here.

Defragment a Provisioning Server vDisk

In case you already imported your VHD file to Provisioning Server, you cannot simply start an offline defragmentation. You need to make sure that the file is not in use. To put it differently, you have to make sure that no target devices are connected to the vDisk you want to defragment.

If you want to use the PowerShell script in the previous section, you will have to add a few additional lines to check for any open connections to the vDisk. You can use the Get-PvsDiskInfo cmdlet to check if your vDisk is locked. Please make sure to install the Provisioning Server PowerShell snap-in as described in the article Citrix Provisioning Server unattended installation beforehand. Here is an example of how to check if your vDisk is locked or not.

It is not important if the vDisk is set to Standard or Private mode. These modes are only relevant for interactive logons. In our case, we want the vDisk to be completely offline.

Provisioning Server mounting tool CVhdMount.exe

Both Provisioning Server as well as the Provisioning Server Target Device software includes it own mounting tool, the executable CVhdMount.exe in the directory C:\Program Files\Citrix\Provisioning Services.

Automate VHD Offline Defrag for Citrix Provisioning Server - CVhdMount.exe

This tool is used when mounting a vDisk using the Provisioning Server console.

Automate VHD Offline Defrag for Citrix Provisioning Server - Provisioning Server console mount vDisk

You can also use this tool on the command line. The parameters to mount the vDisk are: CVhdMount.exe -p 1 C:\PVSMyHomeStore\MyVirtualHardDisk.vhdxAutomate VHD Offline Defrag for Citrix Provisioning Server - CVhdMount.exe example

The -p switch should be greater than 0. As far as I can tell, this serial number is only required for the CVhdMount tool to keep track of the mounted vDisk or vDisks. This serial number is not to be confused with the serial number of the actual vDisk.

Automate VHD Offline Defrag for Citrix Provisioning Server - Get-PvsDiskInfo get serial number

In most cases, you can simply use number 1 as the serial number. Let me demonstrate. In the following screen shot, I try to mount two different vDisk with the same serial number (in both cases I use number 1). The second vDisk cannot be mounted due to a conflicting serial number. When I assign number 2 as a serial number, the second vDisk can be mounted without any problem.

Automate VHD Offline Defrag for Citrix Provisioning Server - Provisioning Server console mount multiple vDisks

To unmount the vDisk, use the following command: CVhdMount.exe -u 1. The switch -u requires the same serial number as the one you used when mounting the vDisk.

If you so choose, you could replace the diskpart.exe with the cVHDMount.exe in the PowerShell script presented in the first section of this article. Personally, I would stick to DiskPart.

17 thoughts on “Automate VHD Offline Defrag for Citrix Provisioning Server

  1. Great stuff Dennis! Some caveats I’ve experienced:

    1) if you create a VHD file with 32MB block size, ONLY cvhdmount.exe will mount it. All other formats can be done with diskpart.

    2) You can’t mount VHD files if they reside on 4K block storage, they will need to be moved to 512b block storage.

  2. Performing the “Manually defragment a stand-alone VHD file” procedure, the degragment status of the drive before starting the defragmentation is: OK (100% space efficiency).

  3. Pingback: EUC Weekly Digest – October 14, 2017 – Carl Stalhood

  4. Pingback: How to configure and run BIS-F in an SCCM task sequence - Dennis Span

  5. Pingback: How I run a Defraf a PVS Vdisk. » Citrixirc.com

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.