Hyper-V Script: Compact VHD

Hyper-V Script: Compact VHD

  • Comments 10

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 6 and 4 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}

      }

    }

  • The powershell script in Windows Server 2012 R2 not work, generates the following error:

    gwmi : Invalid class "Msvm_ImageManagementService"

    At C:\Users\cluster\Desktop\Compact_Dt082210.ps1:7 char:27

    + $ImageManagementService = gwmi Msvm_ImageManagementService -namespace "root\virt ...

    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidType: (:) [Get-WmiObject], ManagementException

       + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

    You cannot call a method on a null-valued expression.

    At C:\Users\cluster\Desktop\Compact_Dt082210.ps1:10 char:1

    + $result = $ImageManagementService.CompactVirtualHardDisk($VHDName)

    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

       + FullyQualifiedErrorId : InvokeMethodOnNull

    The virtual hard disk has not been compacted.  Error value:

    Thank you very much.

  • 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 "\root\visualization\v2" if you need that info.

Page 1 of 1 (10 items)