Hyper-V Script: Compact VHD

Hyper-V Script: Compact VHD

  • Comments 8

Continuing on with my walk through the storage WMI API - here are some scripts for compacting VHDs with Hyper-V.  Note that Hyper-V allows you to compact both differencing and dynamically expanding virtual hard disks.

PowerShell:

# Prompt for the Hyper-V Server to use
$HyperVServer = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
 
# Get name for VHD
$VHDName = Read-Host "Specify the name of the virtual had disk to compact"
 
# Get the Msvm_ImageManagementService object
$ImageManagementService = gwmi Msvm_ImageManagementService -namespace "root\virtualization" -computername $HyperVServer
 
# Compact the VHD
$result = $ImageManagementService.CompactVirtualHardDisk($VHDName)
 
#Return success if the return value is "0"
if ($Result.ReturnValue -eq 0)
   {write-host "The virtual hard disk has been compacted."} 
 
#If the return value is not "0" or "4096" then the operation failed
ElseIf ($Result.ReturnValue -ne 4096)
   {write-host "The virtual hard disk has not been compacted.  Error value:" $Result.ReturnValue}
 
  Else
   {#Get the job object
    $job=[WMI]$Result.job
 
    #Provide updates if the jobstate is "3" (starting) or "4" (running)
    while ($job.JobState -eq 3 -or $job.JobState -eq 4)
      {write-host "Compacting. "$job.PercentComplete "% complete"
       start-sleep 1
 
       #Refresh the job object
       $job=[WMI]$Result.job}
 
     #A jobstate of "7" means success
    if ($job.JobState -eq 7)
       {write-host "The virtual hard disk has been compacted."}
      Else
       {write-host "The virtual hard disk has not been compacted."
        write-host "ErrorCode:" $job.ErrorCode
        write-host "ErrorDescription" $job.ErrorDescription}
   }

VBScript:

Option Explicit
 
Dim HyperVServer
Dim VHDName
Dim WMIService
Dim Msvm_ImageManagementService
Dim Result
Dim Job
Dim InParam
Dim OutParam
 
'Prompt for the Hyper-V Server to use
HyperVServer = InputBox("Specify the Hyper-V Server to create the virtual machine on:")
 
'Get name for VHD
VHDName = InputBox("Specify the name of the virtual had disk to compact:")
 
'Get an instance of the WMI Service in the virtualization namespace.
Set WMIService = GetObject("winmgmts:\\" & HyperVServer & "\root\virtualization")
 
'Get the Msvm_ImageManagementService object
Set Msvm_ImageManagementService = WMIService.ExecQuery("SELECT * FROM Msvm_ImageManagementService").ItemIndex(0)
 
'Setup the input parameter list
Set InParam = Msvm_ImageManagementService.Methods_("CompactVirtualHardDisk").InParameters.SpawnInstance_()
InParam.Path = VHDName 
 
'Execute the method and store the results in OutParam
Set OutParam = Msvm_ImageManagementService.ExecMethod_("CompactVirtualHardDisk", InParam) 
 
'Check to see if the job completed synchronously
if (OutParam.ReturnValue = 0) then
   Wscript.Echo "The virtual hard disk has been compacted."
elseif (OutParam.ReturnValue <> 4096) then
   Wscript.Echo "The virtual hard disk has not been compacted."
else   
 
   'Get the job object
   set Job = WMIService.Get(OutParam.Job)
 
    'Wait for the job to complete (3 == starting, 4 == running)
   while (Job.JobState = 3) or (Job.JobState = 4)
      Wscript.Echo "Compacting. " & Job.PercentComplete & "% complete"
      WScript.Sleep(1000)
 
      'Refresh the job object
      set Job = WMIService.Get(OutParam.Job)
   Wend
 
   'Provide details if the job fails (7 == complete)
   if (Job.JobState <> 7) then
      Wscript.Echo "The virtual hard disk has not been compacted."
      Wscript.Echo "ErrorCode:" & Job.ErrorCode
      Wscript.Echo "ErrorDescription:" & Job.ErrorDescription
   else
      Wscript.Echo "The virtual hard disk has been compacted."
   end If
end if
Cheers,
Ben
Leave a Comment
  • Please add 7 and 1 and type the answer here:
  • Post
  • What is the purpose of compacting the VHDs though Ben?

  • Thanks for all the great tips, they've been exceptionally helpful.

    I have a server that is not hyper-v capable, but I would like to install the hyper-v tools to mount/defrag/compact vhds.  Is there a way to do this?

  • I have just compacted a VHD

    Size before compaction: 9,524,588,032

    Size After  compaction: 9,355,077,632

  • Do I need to run pre-compactor tool (precompact.exe in VirtualServer) before compacting VHDs in Hyper-V?

  • A nice addition would be a before/after report as an option.

  • There seems to be some issues here.  The powershell script won't run at all, and the VBS script is vague.  I can't tell if it actually ran or not.  When running the script it immediately says the drive is compacted but there is no difference at all.  How am I supposed to know if it really worked or not?

  • Specify the name of the virtual had disk to compact? Had disk? Are you by any chance from Boston?

    I assume that the Virtual Server has to be shut down?

    I just compared Usage stats from File Server Resource Manager to .VHD sizes. I was quite surprised at the difference, on a dynamic disk.

  • Modification of the above Powershell script to compact VHD's in bulk without any user intervention

    $strComputers = @(

    "F:\VMStore\CS1SRX\CS1SRX.vhd",

    "F:\VMStore\CS1SRV\CS1SRV.vhd",

    "F:\VMStore\CS1SQL\CS1SQL.vhd",

    )

    foreach($files in $strComputers)

    {

    $ImageManagementService = gwmi Msvm_ImageManagementService -namespace "root\virtualization" -computername machinename

    $result = $ImageManagementService.CompactVirtualHardDisk($files)

    if ($Result.ReturnValue -eq 0)

      {write-host "The virtual hard disk has been compacted."}

    #If the return value is not "0" or "4096" then the operation failed

    ElseIf ($Result.ReturnValue -ne 4096)

      {write-host "The virtual hard disk has not been compacted.  Error value:" $Result.ReturnValue}

     Else

      {#Get the job object

       $job=[WMI]$Result.job

       #Provide updates if the jobstate is "3" (starting) or "4" (running)

       while ($job.JobState -eq 3 -or $job.JobState -eq 4)

         {write-host "Compacting. "$job.PercentComplete "% complete"

          start-sleep 1

          #Refresh the job object

          $job=[WMI]$Result.job}

        #A jobstate of "7" means success

       if ($job.JobState -eq 7)

          {write-host "The virtual hard disk has been compacted."}

         Else

          {write-host "The virtual hard disk has not been compacted."

           write-host "ErrorCode:" $job.ErrorCode

           write-host "ErrorDescription" $job.ErrorDescription}

      }

    }

Page 1 of 1 (8 items)