Content/Deployment/Module References/xHyper-VBackup/xHyper-VBackup.cmdlets.psm1

# ProcessWMIJob is a generic function that waits for WMI job's to
# complete and returns appropriate success/failure cases.
# This function was originally written and documented on
# Taylor Brown's blog at:
# http://blogs.msdn.com/b/taylorb/archive/2008/06/18/hyper-v-wmi-rich-error-messages-for-non-zero-returnvalue-no-more-32773-32768-32700.aspx
filter ProcessWMIJob 
{ 
    param 
    ( 
        [WMI]$WmiClass = $null,
        [string]$MethodName = $null
    )
    $errorCode = 0
    $returnObject = $_

    if ($_.ReturnValue -eq 4096) 
    { 
        $Job = [WMI]$_.Job 
        $returnObject = $Job

        while ($Job.JobState -eq 4) 
        { 
            Write-Progress -Activity $Job.Caption -Status ($Job.JobStatus + " - " + $Job.PercentComplete + "%") -PercentComplete $Job.PercentComplete
            Start-Sleep -seconds 1
            $Job.PSBase.Get()
        } 
        if ($Job.JobState -ne 7) 
        { 
            if ($Job.ErrorDescription -ne "")
            {
                Write-Error $Job.ErrorDescription 
                Throw $Job.ErrorDescription 
            }
            else
            {
                $errorCode = $Job.ErrorCode
            }
        } 
        Write-Progress -Activity $Job.Caption -Status $Job.JobStatus -PercentComplete 100 -Completed:$true
        
    }
    elseif($_.ReturnValue -ne 0)
    {
        $errorCode = $_.ReturnValue
    }
    
    if ($errorCode -ne 0) 
    { 
        Write-Error "Hyper-V WMI Job Failed!" 
        if ($WmiClass -and $MethodName)
        {
            $psWmiClass = [WmiClass]("\\" + $WmiClass.__SERVER + "\" + $WmiClass.__NAMESPACE + ":" + $WmiClass.__CLASS)
            $psWmiClass.PSBase.Options.UseAmendedQualifiers = $TRUE
            $MethodQualifierValues = ($psWmiClass.PSBase.Methods[$MethodName].Qualifiers)["Values"]
            $indexOfError = [System.Array]::IndexOf(($psWmiClass.PSBase.Methods[$MethodName].Qualifiers)["ValueMap"].Value, [string]$errorCode)
            if (($indexOfError -ne "-1") -and $MethodQualifierValues)
            {
                Throw "ReturnCode: ", $errorCode, " ErrorMessage: '", $MethodQualifierValues.Value[$indexOfError], "' - when calling $MethodName"
            }
            else
            {
                Throw "ReturnCode: ", $errorCode, " ErrorMessage: 'MessageNotFound' - when calling $MethodName"
            }
        }
        else
        {
            Throw "ReturnCode: ", $errorCode, "When calling $MethodName - for rich error messages provide classpath and method name."
        }
    } 
    return $returnObject
}


function Convert-VmBackupCheckpoint
{
    Param(
      [Parameter(Mandatory=$True)]
      [System.Management.ManagementObject]$BackupCheckpoint = $null
    )

    # Retrieve an instance of the snapshot management service
    $Msvm_VirtualSystemSnapshotService = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_VirtualSystemSnapshotService

    # Convert the snapshot to a reference point, this function returns a job object.
    $job = $Msvm_VirtualSystemSnapshotService.ConvertToReferencePoint($BackupSnapshot)

    # Wait for the job to complete.
    ($job | ProcessWMIJob -WmiClass $Msvm_VirtualSystemSnapshotService -MethodName "ConvertToReferencePoint") | Out-Null

    # The new reference point object is related to the job, GetReleated
    # always returns an array in this case there is only one member
    $refPoint = (([WMI]$job.Job).GetRelated("Msvm_VirtualSystemReferencePoint") | % {$_})

    # Return the reference point object
    return $refPoint
}

function Export-VMBackupCheckpoint
{
    Param(
      [Parameter(Mandatory=$True)]
      [string]$VmName = [String]::Empty,

      [Parameter(Mandatory=$True)]
      [string]$DestinationPath = [String]::Empty,

      [Parameter(Mandatory=$True)]
      [System.Management.ManagementObject]$BackupCheckpoint = $null,

      [System.Management.ManagementObject]$ReferencePoint = $null,

      [bool]$noWait = $false
    )

    # Retrieve an instance of the virtual machine management service
    $Msvm_VirtualSystemManagementService = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_VirtualSystemManagementService

    # Retrieve an instance of the virtual machine computer system that will be snapshoted
    $Msvm_ComputerSystem = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName='$vmName'"

    # Retrieve an instance of the Export Setting Data Class (this is used to inform the export operation)
    # GetReleated always returns an array in this case there is only one member
    $Msvm_VirtualSystemExportSettingData = ($Msvm_ComputerSystem.GetRelated("Msvm_VirtualSystemExportSettingData","Msvm_SystemExportSettingData",$null,$null, $null, $null, $false, $null) | % {$_})

    # Specify the export options
        # CopySnapshotConfiguration
        # 0: ExportAllSnapshots - All snapshots will be exported with the VM.
        # 1: ExportNoSnapshots - No snapshots will be exported with the VM.
        # 2: ExportOneSnapshot - The snapshot identified by the SnapshotVirtualSystem property will be exported with the VM.
        # 3: ExportOneSnapshotUseVmId - The snapshot identified by the SnapshotVirtualSystem property will be exported with the VM. Using the VMs ID.
        $Msvm_VirtualSystemExportSettingData.CopySnapshotConfiguration = 3

        # CopyVmRuntimeInformation
        # Indicates whether the VM runtime information will be copied when the VM is exported. (i.e. saved state)
        $Msvm_VirtualSystemExportSettingData.CopyVmRuntimeInformation = $false

        # CopyVmStorage
        # Indicates whether the VM storage will be copied when the VM is exported. (i.e. VHDs/VHDx files)
        $Msvm_VirtualSystemExportSettingData.CopyVmStorage = $true

        # CreateVmExportSubdirectory
        # Indicates whether a subdirectory with the name of the VM will be created when the VM is exported.
        $Msvm_VirtualSystemExportSettingData.CreateVmExportSubdirectory = $false

        # SnapshotVirtualSystem
        # Path to a Msvm_VirtualSystemSettingData instance that represents the snapshot to be exported with the VM.
        $Msvm_VirtualSystemExportSettingData.SnapshotVirtualSystem = $BackupSnapshot

        # DifferentialBase
        # Base for differential export. This is either path to a Msvm_VirtualSystemReferencePoint instance that
        # represents the reference point or path to a Msvm_VirtualSystemSettingData instance that
        # represents the snapshot to be used as a base for differential export. If the CopySnapshotConfiguration
        # property is not set to 3(ExportOneSnapshotUseVmId), this property is ignored."
        $Msvm_VirtualSystemExportSettingData.DifferentialBase = $ReferenceSnapshot

        # StorageConfiguration
        # Indicates what should be the VHD path in the exported configuration.
        # 0: StorageConfigurationCurrent - The exported configuration would point to the current VHD.
        # 1: StorageConfigurationBaseVhd - The exported configuration would point to the base VHD.
        $Msvm_VirtualSystemExportSettingData.StorageConfiguration = 1

    #Export the virtual machine snapshot, this method returns a job object.
    $job = $Msvm_VirtualSystemManagementService.ExportSystemDefinition($Msvm_ComputerSystem, $DestinationPath, $Msvm_VirtualSystemExportSettingData.GetText(1))

    if (!$noWait)
    {
        ($job | ProcessWMIJob -WmiClass $Msvm_VirtualSystemManagementService -MethodName "ExportSystemDefinition") | Out-Null
    }
}

function Get-VmBackupCheckpoints
{
    Param(
      [Parameter(Mandatory=$True)]
      [string]$VmName = [String]::Empty
    )

    # Retrieve an instance of the virtual machine computer system that contains recovery checkpoints
    $Msvm_ComputerSystem = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName='$VmName'"

    # Retrieve all snapshot associations for the virtual machine
    $allSnapshotAssociations = $Msvm_ComputerSystem.GetRelationships("Msvm_SnapshotOfVirtualSystem")

    # Enumerate across all of the instances and add all recovery snapshots to an array
    $virtualSystemSnapshots = @()
    $enum = $allSnapshotAssociations.GetEnumerator()
    $enum.Reset()
    while($enum.MoveNext())
    {
        if (([WMI] $enum.Current.Dependent).VirtualSystemType -eq "Microsoft:Hyper-V:Snapshot:Recovery")
        {
            $virtualSystemSnapshots += ([WMI] $enum.Current.Dependent)
        }
    }

    # Return the array of recovery snapshots
    $virtualSystemSnapshots
}

function Get-VmReferencePoints
{
    Param(
      [Parameter(Mandatory=$True)]
      [string]$VmName = [String]::Empty
    )

    # Retrieve an instance of the virtual machine computer system that contains reference points
    $Msvm_ComputerSystem = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName='$VmName'"

    # Retrieve all refrence associations of the virtual machine
    $allrefPoints = $Msvm_ComputerSystem.GetRelationships("Msvm_ReferencePointOfVirtualSystem")

    # Enumerate across all of the instances and add all recovery points to an array
    $virtualSystemRefPoint = @()
    $enum = $allrefPoints.GetEnumerator()
    $enum.Reset()
    while($enum.MoveNext())
    {
        $virtualSystemRefPoint += ([WMI] $enum.Current.Dependent)
    }

    # Return the array of recovery points
    $virtualSystemRefPoint
}

function New-VmBackupCheckpoint
{
    Param(
      [Parameter(Mandatory=$True)]
      [string]$VmName = [String]::Empty,
      [ValidateSet('ApplicationConsistent','CrashConsistent')]
      [string]$ConsistencyLevel = "Application Consistent"
    )

    # Retrieve an instance of the virtual machine management service
    $Msvm_VirtualSystemManagementService = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_VirtualSystemManagementService

    # Retrieve an instance of the virtual machine snapshot service
    $Msvm_VirtualSystemSnapshotService = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_VirtualSystemSnapshotService

    # Retrieve an instance of the virtual machine computer system that will be snapshotted
    $Msvm_ComputerSystem = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName='$vmName'"

    # Create an instance of the Msvm_VirtualSystemSnapshotSettingData, this class provides options on how the checkpoint will be created.
    $Msvm_VirtualSystemSnapshotSettingData = ([WMIClass]"\\.\root\virtualization\v2:Msvm_VirtualSystemSnapshotSettingData").CreateInstance()

    # Identify the consistency level for the snapshot.
    # 1: Application Consistent
    # 2: Crash Consistent
    switch ($ConsistencyLevel)
    {
        "ApplicationConsistent" {
        $Msvm_VirtualSystemSnapshotSettingData.ConsistencyLevel = 1
        }

        "CrashConsistent" {
        $Msvm_VirtualSystemSnapshotSettingData.ConsistencyLevel = 2
        }

        default {
        throw "Unexpected Consistancy Level Specified"
        }
    }

    # Specify the behavior for disks that cannot be snapshotted (i.e. pass-through, virtual fibre channel)
    $Msvm_VirtualSystemSnapshotSettingData.IgnoreNonSnapshottableDisks = $true

    # Create the virtual machine snapshot, this method returns a job object.
    $job = $Msvm_VirtualSystemSnapshotService.CreateSnapshot(
        $Msvm_ComputerSystem,
        $Msvm_VirtualSystemSnapshotSettingData.GetText(2),
        32768)

    # Waits for the job to complete and processes any errors.
    ($job | ProcessWMIJob -WmiClass $Msvm_VirtualSystemSnapshotService -MethodName "CreateSnapshot") | Out-Null

    # Retrieves the snapshot object resulting from the snapshot.
    $snapshot = (([WMI]$job.Job).GetRelated("Msvm_VirtualSystemSettingData") | % {$_})

    # Returns the snapshot instance
    return $snapshot
}

function Remove-VmReferencePoint
{
    Param(
      [Parameter(Mandatory=$True)]
      [System.Management.ManagementObject]$ReferencePoint = $null
    )


    # Retrieve an instance of the virtual machine refrence point service
    $Msvm_VirtualSystemReferencePointService = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_VirtualSystemReferencePointService

    # Removes the virtual machine reference, this method returns a job object.
    $job = $Msvm_VirtualSystemReferencePointService.DestroyReferencePoint($ReferenceSnapshot)

    # Waits for the job to complete and processes any errors.
    ($job | ProcessWMIJob -WmiClass $Msvm_VirtualSystemReferencePointService -MethodName "DestroyReferencePoint") | Out-Null
}