Share via


Hyper-V Script: Compact VHD

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

Comments

  • Anonymous
    June 12, 2008
    What is the purpose of compacting the VHDs though Ben?

  • Anonymous
    June 24, 2008
    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?

  • Anonymous
    June 26, 2008
    I have just compacted a VHD Size before compaction: 9,524,588,032 Size After  compaction: 9,355,077,632

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

  • Anonymous
    July 07, 2010
    A nice addition would be a before/after report as an option.

  • Anonymous
    May 19, 2011
    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?

  • Anonymous
    February 17, 2012
    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.

  • Anonymous
    September 04, 2012
    Modification of the above Powershell script to compact VHD's in bulk without any user intervention $strComputers = @( "F:VMStoreCS1SRXCS1SRX.vhd", "F:VMStoreCS1SRVCS1SRV.vhd", "F:VMStoreCS1SQLCS1SQL.vhd", ) foreach($files in $strComputers) { $ImageManagementService = gwmi Msvm_ImageManagementService -namespace "rootvirtualization" -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}   } }

  • Anonymous
    January 07, 2014
    The comment has been removed

  • Anonymous
    April 09, 2014
    What the heck does "Specify the name of the virtual had disk to compact" even mean? Specifically "virtual had disk". And Windows 2012 R2 and non R2's WMI namespace is located at "rootvisualizationv2" if you need that info.