????

Your IP : 3.144.153.204


Current Path : C:/Windows/System32/WindowsPowerShell/v1.0/Modules/VMDirectStorage/
Upload File :
Current File : C:/Windows/System32/WindowsPowerShell/v1.0/Modules/VMDirectStorage/VMDirectStorage.psm1

<###################################################
 #                                                 #
 #  Copyright (c) Microsoft. All rights reserved.  #
 #                                                 #
 ##################################################>

Import-Module Hyper-V
Import-Module Storage

$VirtualizationNamespace = 'root\virtualization\v2'

# Only SCSI is supported at this time
[flagsattribute()]
Enum ControllerType
{
    SCSI = 1
}

class VMDirectVirtualDisk
{
    [String]
    $VirtualDiskUniqueId

    [String]
    $VirtualDiskFriendlyName

    [System.UInt32]
    $ControllerLocation

    [System.UInt32]
    $ControllerNumber

    [ControllerType]
    $ControllerType

    [String]
    $Id

    [Guid]
    $VMId

    [String]
    $VMName

    [CimSession]
    $CimSession

    [String]
    $ComputerName
}

function CreateErrorRecord
{
    Param
    (
        [String]
        $ErrorId,

        [String]
        $ErrorMessage,

        [System.Management.Automation.ErrorCategory]
        $ErrorCategory,

        [Exception]
        $Exception,

        [Object]
        $TargetObject
    )

    if ($Exception -eq $null)
    {
        $Exception = New-Object System.Management.Automation.RuntimeException $ErrorMessage
    }

    $errorRecord = New-Object System.Management.Automation.ErrorRecord @($Exception, $ErrorId, $ErrorCategory, $TargetObject)

    return $errorRecord
}

function Get-VMDirectVirtualDisk
{
    Param
    (
        #### --------------------------------- Parameter Sets --------------------------------- ####

        [System.String[]]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        [ValidateNotNullOrEmpty()]
        $VMName,

        [Microsoft.HyperV.PowerShell.VirtualMachine[]]
        [Parameter(
            ParameterSetName  = 'ByVM',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        [ValidateNotNullOrEmpty()]
        $VM,

        [Microsoft.HyperV.PowerShell.VMDriveController[]]
        [Parameter(
            ParameterSetName  = 'ByVMDriveController',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        [ValidateNotNullOrEmpty()]
        $VMDriveController,

        #### ----------------------------- Common Method Parameters ----------------------------- ####

        [CimSession[]]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false)]
        $CimSession,

        [ControllerType]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false,
            Position          = 1)]
        [Parameter(
            ParameterSetName  = 'ByVM',
            Mandatory         = $false,
            Position          = 1)]
        $ControllerType,

        [System.Int32]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false,
            Position          = 2)]
        [Parameter(
            ParameterSetName  = 'ByVM',
            Mandatory         = $false,
            Position          = 2)]
        $ControllerNumber,

        [System.Int32]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false,
            Position          = 3)]
        [Parameter(
            ParameterSetName  = 'ByVM',
            Mandatory         = $false,
            Position          = 3)]
        [Parameter(
            ParameterSetName  = 'ByVMDriveController',
            Mandatory         = $false)]
        $ControllerLocation
    )

    Process
    {
        # Get the instance controllers
        $vms         = @()
        $controllers = @()

        switch ($psCmdlet.ParameterSetName)
        {
            "ByVMName"
            {
                if (!$PSBoundParameters.ContainsKey('CimSession'))
                {
                    $CimSession = New-CimSession -ErrorAction Stop
                }

                $vms = Get-VM -Name $VMName `
                              -CimSession $CimSession `
                              -ErrorAction Stop
            }

            "ByVM"
            {
                $vms = $VM
            }

            "ByVMDriveController"
            {
                $controllers = $VMDriveController
            }
        }

        if ($controllers.Count -eq 0)
        {
            if ($PSBoundParameters.ContainsKey('ControllerNumber'))
            {
                $controllers = Get-VMScsiController -VM $vms `
                                                    -ControllerNumber $ControllerNumber `
                                                    -ErrorAction Stop
            }
            else
            {
                $controllers = Get-VMScsiController -VM $vms `
                                                    -ErrorAction Stop
            }
        }

        foreach ($controller in $controllers)
        {
            # Get the current CIM session
            $currentCimSession = $controller.CimSession

            # Cache virtual disks in the current CIM session for later use
            $virtualDisks = Get-VirtualDisk -CimSession $currentCimSession `
                                            -ErrorAction Stop

            # No virtual disks were found in the current CIM session
            if ($null -eq $virtualDisks -or
                $virtualDisks.Count -eq 0)
            {
                continue
            }

            # Get the current VM
            $currentVm = Get-VM -Id $controller.VMId `
                                -CimSession $currentCimSession `
                                -ErrorAction Stop

            # Get the associated controller resource
            $computerSystem = Get-CimInstance -ClassName 'Msvm_ComputerSystem' `
                                              -CimSession $currentCimSession `
                                              -Namespace $VirtualizationNamespace `
                                              -Filter "Name='$($currentVm.Id)'" `
                                              -ErrorAction Stop

            if ($null -eq $computerSystem)
            {
                $error = CreateErrorRecord -ErrorId "ObjectNotFound" `
                                           -ErrorMessage "No virtual machines found with id equal to '$($currentVm.Id)'." `
                                           -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) `
                                           -Exception $null `
                                           -TargetObject $null

                $psCmdlet.WriteError($error)
                return
            }

            $virtualSystemSettingData = Get-CimAssociatedInstance -InputObject $computerSystem `
                                                                  -CimSession $currentCimSession `
                                                                  -ResultClassName 'Msvm_VirtualSystemSettingData' `
                                                                  -ErrorAction Stop

            $virtualSystemSettingData = $virtualSystemSettingData | Where-Object -FilterScript `
                                        {
                                            $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized'
                                        }

            $controllerResource = Get-CimAssociatedInstance -InputObject $virtualSystemSettingData `
                                                            -CimSession $currentCimSession `
                                                            -ResultClassName 'Msvm_ResourceAllocationSettingData' `
                                                            -ErrorAction Stop

            $controllerResource = $controllerResource | Where-Object -FilterScript `
                                  {
                                      $_.ResourceType -eq 6 -and
                                      $_.InstanceID   -eq $controller.Id
                                  }

            # Get the associated luns
            $lunSettingDataEntries = Get-CimAssociatedInstance -InputObject $controllerResource `
                                                               -CimSession $currentCimSession `
                                                               -ResultClassName 'Msvm_VirtualLogicalUnitSettingData' `
                                                               -ErrorAction Stop

            # Filter luns by controller location
            if ($PSBoundParameters.ContainsKey('ControllerLocation'))
            {
                $lunSettingDataEntries = $lunSettingDataEntries | Where-Object -FilterScript `
                                         {
                                             $_.AddressOnParent -eq $ControllerLocation
                                         }
            }

            # Enumerate virtual disks with an associated lun
            foreach ($virtualDisk in $virtualDisks)
            {
                # Ignore luns not associated with a virtual disk
                $virtualDiskId = $virtualDisk.ObjectId.Split('{}')[7]

                $lunSettingData = $lunSettingDataEntries | Where-Object -FilterScript `
                {
                    $_.Connection -like "*$virtualDiskId*"
                }

                if ($null -eq $lunSettingData)
                {
                    continue
                }

                $vmDirectVirtualDisk = [VMDirectVirtualDisk]::new()

                $vmDirectVirtualDisk.VirtualDiskUniqueId     = $virtualDisk.UniqueId
                $vmDirectVirtualDisk.VirtualDiskFriendlyName = $virtualDisk.FriendlyName
                $vmDirectVirtualDisk.ControllerLocation      = $lunSettingData.AddressOnParent
                $vmDirectVirtualDisk.ControllerNumber        = $controller.ControllerNumber
                $vmDirectVirtualDisk.ControllerType          = [ControllerType]::SCSI
                $vmDirectVirtualDisk.Id                      = $lunSettingData.InstanceID
                $vmDirectVirtualDisk.VMId                    = $currentVm.VMId
                $vmDirectVirtualDisk.VMName                  = $currentVm.Name
                $vmDirectVirtualDisk.CimSession              = $currentCimSession
                $vmDirectVirtualDisk.ComputerName            = $currentVm.ComputerName

                $vmDirectVirtualDisk
            }
        }
    }
}

function Add-VMDirectVirtualDisk
{
    Param
    (
        #### --------------------------------- Parameter Sets --------------------------------- ####

        [System.String[]]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        [ValidateNotNullOrEmpty()]
        $VMName,

        [Microsoft.HyperV.PowerShell.VirtualMachine[]]
        [Parameter(
            ParameterSetName  = 'ByVM',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        [ValidateNotNullOrEmpty()]
        $VM,

        [Microsoft.HyperV.PowerShell.VMDriveController]
        [Parameter(
            ParameterSetName  = 'ByVMDriveController',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        [ValidateNotNullOrEmpty()]
        $VMDriveController,

        #### ----------------------------- Common Method Parameters ----------------------------- ####

        [CimSession[]]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false)]
        $CimSession,

        [ControllerType]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false,
            Position          = 1)]
        [Parameter(
            ParameterSetName  = 'ByVM',
            Mandatory         = $false,
            Position          = 1)]
        $ControllerType,

        [System.Int32]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false,
            Position          = 2)]
        [Parameter(
            ParameterSetName  = 'ByVM',
            Mandatory         = $false,
            Position          = 2)]
        $ControllerNumber,

        [System.Int32]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false,
            Position          = 3)]
        [Parameter(
            ParameterSetName  = 'ByVM',
            Mandatory         = $false,
            Position          = 3)]
        [Parameter(
            ParameterSetName  = 'ByVMDriveController',
            Mandatory         = $false)]
        $ControllerLocation,

        # Analog to Path parameter in Add-VMHardDiskDrive
        [String]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false)]
        [Parameter(
            ParameterSetName  = 'ByVM',
            Mandatory         = $false)]
        [Parameter(
            ParameterSetName  = 'ByVMDriveController',
            Mandatory         = $false)]
        [ValidateNotNullOrEmpty()]
        $VirtualDiskUniqueId
    )

    Process
    {
        # Get the instance VM(s)
        $vms = @()

        switch ($psCmdlet.ParameterSetName)
        {
            "ByVMName"
            {
                if (!$PSBoundParameters.ContainsKey('CimSession'))
                {
                    $CimSession = New-CimSession -ErrorAction Stop
                }

                $vms = Get-VM -Name $VMName `
                              -CimSession $CimSession `
                              -ErrorAction Stop
            }

            "ByVM"
            {
                $vms = $VM
            }

            "ByVMDriveController"
            {
                $vms = Get-VM -Name $VMDriveController.VMName `
                              -CimSession $VMDriveController.CimSession `
                              -ErrorAction Stop
            }
        }

        foreach ($currentVm in $vms)
        {
            # Get the current CIM session
            $currentCimSession = $currentVm.CimSession

            # Get the Virtual System Management Service
            $vmms = Get-CimInstance -ClassName 'Msvm_VirtualSystemManagementService' `
                                    -CimSession $currentCimSession `
                                    -Namespace $VirtualizationNamespace `
                                    -ErrorAction Stop

            if ($null -eq $vmms)
            {
                $error = CreateErrorRecord -ErrorId "ObjectNotFound" `
                                           -ErrorMessage "Could not find Virtual System Management Service." `
                                           -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) `
                                           -Exception $null `
                                           -TargetObject $null

                $psCmdlet.WriteError($error)
                return
            }

            $computerSystem = Get-CimInstance -ClassName 'Msvm_ComputerSystem' `
                                              -CimSession $currentCimSession `
                                              -Namespace $VirtualizationNamespace `
                                              -Filter "Name='$($currentVm.Id)'" `
                                              -ErrorAction Stop

            if ($null -eq $computerSystem)
            {
                $error = CreateErrorRecord -ErrorId "ObjectNotFound" `
                                           -ErrorMessage "No virtual machines found with id equal to '$($currentVm.Id)'." `
                                           -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) `
                                           -Exception $null `
                                           -TargetObject $null

                $psCmdlet.WriteError($error)
                return
            }

            $virtualSystemSettingData = Get-CimAssociatedInstance -InputObject $computerSystem ` `
                                                                  -CimSession $currentCimSession `
                                                                  -ResultClassName 'Msvm_VirtualSystemSettingData' `
                                                                  -ErrorAction Stop

            $virtualSystemSettingData = $virtualSystemSettingData | Where-Object -FilterScript `
                                        {
                                            $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized'
                                        }

            # Get associated controller resources
            $controllerResources = Get-CimAssociatedInstance -InputObject $virtualSystemSettingData `
                                                             -CimSession $currentCimSession `
                                                             -ResultClassName 'Msvm_ResourceAllocationSettingData' `
                                                             -ErrorAction Stop

            $controllerResources = $controllerResources | Where-Object -FilterScript `
                                   {
                                       $_.ResourceType -eq 6
                                   }

            # Cache VM controllers for later use
            $controllers = Get-VMScsiController -VM $currentVm `
                                                -ErrorAction Stop

            $controller = $null

            # Get the controller resource associated with the specified controller
            if ($PSBoundParameters.ContainsKey('VMDriveController'))
            {
                $controller = $VMDriveController
            }

            # Get the controller resource associated with the specified controller number
            if ($PSBoundParameters.ContainsKey('ControllerNumber'))
            {
                $controller = $controllers | Where-Object -FilterScript `
                {
                    $_.ControllerNumber -eq $ControllerNumber
                }

                if ($null -eq $controller)
                {
                    $controllerResources = $null
                }
            }

            if ($null -ne $controller)
            {
                $controllerResources = $controllerResources | Where-Object -FilterScript `
                                       {
                                           $_.InstanceID -eq $controller.Id
                                       }
            }

            # Hyper-V enforces a max of 64 locations per controller
            $minlocation = 0
            $maxlocation = 63

            if ($PSBoundParameters.ContainsKey('ControllerLocation')) {

                $minlocation = $ControllerLocation
                $maxLocation = $ControllerLocation
            }

            $found = $false

            foreach ($controllerResource in $controllerResources)
            {
                # Get the associated controller
                $controller = $controllers | Where-Object -FilterScript `
                {
                    $_.Id -eq $controllerResource.InstanceID
                }

                # Get the VM hdds associated with the current controller
                $vmHdds = $controller | Get-VMHardDiskDrive -ErrorAction Stop

                # Get the luns associated with the current controller
                $lunSettingDataEntries = Get-CimAssociatedInstance -InputObject $controllerResource `
                                                                   -CimSession $currentCimSession `
                                                                   -ResultClassName 'Msvm_VirtualLogicalUnitSettingData' `
                                                                   -ErrorAction Stop

                # Find the next available location on the current controller
                for ($location = $minlocation; $location -le $maxLocation; $location++)
                {
                    $vmHdd = $vmHdds | Where-Object -FilterScript `
                    {
                        $_.ControllerLocation -eq $location
                    }

                    $lunSettingData = $lunSettingDataEntries | Where-Object -FilterScript `
                    {
                        $_.AddressOnParent -eq $location
                    }

                    if ($null -eq $vmHdd -and
                        $null -eq $lunSettingData)
                    {
                        $found = $true
                        break
                    }
                }

                # No available locations on the current controller
                if (-not $found)
                {
                    continue
                }

                # Attach the virtual disk
                $virtualDisk = Get-VirtualDisk -UniqueId $VirtualDiskUniqueId `
                                               -CimSession $currentCimSession `
                                               -ErrorAction Stop

                $poolId        = $virtualDisk.ObjectId.Split('{}')[5]
                $virtualDiskId = $virtualDisk.ObjectId.Split('{}')[7]

                $lunAllocationCapabilities = Get-CimInstance -ClassName 'Msvm_AllocationCapabilities' `
                                                             -CimSession $currentCimSession `
                                                             -Namespace $VirtualizationNamespace `
                                                             -Filter "ResourceType=32768 and ResourceSubType='Microsoft:Hyper-V:Storage Logical Unit'" `
                                                             -ErrorAction Stop

                $lunSettingsDefineCapabilities = Get-CimInstance -ClassName 'Msvm_SettingsDefineCapabilities' `
                                                                 -CimSession $currentCimSession `
                                                                 -Namespace $VirtualizationNamespace `
                                                                 -ErrorAction Stop

                $lunSettingsDefineCapabilities = $lunSettingsDefineCapabilities | Where-Object -FilterScript `
                {
                    $_.GroupComponent.InstanceID -eq $lunAllocationCapabilities.InstanceID -and
                    $_.ValueRange                -eq 0
                }

                $lunSettingData = Get-CimInstance -ClassName Msvm_VirtualLogicalUnitSettingData `
                                                  -CimSession $currentCimSession `
                                                  -Namespace $VirtualizationNamespace `
                                                  -ErrorAction Stop

                $lunSettingData = $lunSettingData | Where-Object -FilterScript `
                {
                    $_.InstanceID -eq $lunSettingsDefineCapabilities.PartComponent.InstanceID
                }

                $lunSettingData.Parent               = "\\$($currentVm.ComputerName)\$($VirtualizationNamespace):Msvm_ResourceAllocationSettingData.InstanceID=`"$($controllerResource.InstanceID.Replace('\', '\\'))`""
                $lunSettingData.AddressOnParent      = $location
                $lunSettingData.Connection           = "{$poolId}{$virtualDiskId}"
                $lunSettingData.StorageSubsystemType = 'space'

                $cimSerializer = [Microsoft.Management.Infrastructure.Serialization.CimSerializer]::Create()

                $embeddedInstanceBuffer = $cimSerializer.Serialize($lunSettingData,
                                                                   [Microsoft.Management.Infrastructure.Serialization.InstanceSerializationOptions]::None)

                $embeddedInstance = [System.Text.Encoding]::Unicode.GetString($embeddedInstanceBuffer)

                $result = Invoke-CimMethod -InputObject $vmms `
                                           -MethodName 'AddResourceSettings' `
                                           -CimSession $currentCimSession `
                                           -Arguments @{ 'AffectedConfiguration' = $virtualSystemSettingData; 'ResourceSettings' = @($embeddedInstance) } `
                                           -ErrorAction Stop

                if ($result.ReturnValue -ne 0 -and
                    $result.ReturnValue -ne 4096)
                {
                    $error = CreateErrorRecord -ErrorId "InvalidOperation" `
                                               -ErrorMessage "Operation failed with error code $($result.ReturnValue)" `
                                               -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidOperation) `
                                               -Exception $null `
                                               -TargetObject $null

                    $psCmdlet.WriteError($error)
                    return
                }

                # Wait for the job to complete
                if ($result.ReturnValue -eq 4096)
                {
                    $job = $null

                    while ($true)
                    {
                        $job = Get-CimInstance -ClassName Msvm_ConcreteJob `
                                               -CimSession $currentCimSession `
                                               -Namespace $VirtualizationNamespace `
                                               -Filter "InstanceID='$($result.Job.InstanceID)'" `
                                               -ErrorAction Stop

                        if ($null -eq $job -or
                            $job.JobState -gt 4)
                        {
                            break
                        }

                        Start-Sleep -Seconds 1
                    }

                    if ($null -ne $job -and
                        $job.ErrorCode -ne 0)
                    {
                        $error = CreateErrorRecord -ErrorId "InvalidOperation" `
                                                   -ErrorMessage $job.ErrorDescription `
                                                   -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidOperation) `
                                                   -Exception $null `
                                                   -TargetObject $null

                        $psCmdlet.WriteError($error)
                        return
                    }
                }

                break
            }

            if (-not $found)
            {
                $error = CreateErrorRecord -ErrorId "ObjectNotFound" `
                                           -ErrorMessage "The operation could not be completed because no available locations were found on the disk controller." `
                                           -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) `
                                           -Exception $null `
                                           -TargetObject $null

                $psCmdlet.WriteError($error)
                return
            }
        }
    }
}

function Remove-VMDirectVirtualDisk
{
    Param
    (
        #### --------------------------------- Parameter Sets --------------------------------- ####

        [System.String]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        [ValidateNotNullOrEmpty()]
        $VMName,

        # Analog to VMHardDiskDrive parameter in Remove-VMHardDiskDrive
        [VMDirectVirtualDisk[]]
        [Parameter(
            ParameterSetName  = 'ByVirtualDisk',
            ValueFromPipeline = $true,
            Mandatory         = $true,
            Position          = 0)]
        $VirtualDisk,

        #### ----------------------------- Common Method Parameters ----------------------------- ####

        [CimSession[]]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $false)]
        $CimSession,

        [ControllerType]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $true,
            Position          = 1)]
        $ControllerType,

        [System.Int32]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $true,
            Position          = 2)]
        $ControllerNumber,

        [System.Int32]
        [Parameter(
            ParameterSetName  = 'ByVMName',
            Mandatory         = $true,
            Position          = 3)]
        $ControllerLocation
    )

    Process
    {
        # Get the VM Direct virtual disks to be removed
        $vmDirectVirtualDisks = @()

        switch ($psCmdlet.ParameterSetName)
        {
            "ByVMName"
            {
                if (!$PSBoundParameters.ContainsKey('CimSession'))
                {
                    $CimSession = New-CimSession -ErrorAction Stop
                }

                $vmDirectVirtualDisks = Get-VMDirectVirtualDisk -VMName $VMName `
                                                                -CimSession $CimSession `
                                                                -ControllerType $ControllerType `
                                                                -ControllerNumber $ControllerNumber `
                                                                -ControllerLocation $ControllerLocation `
                                                                -ErrorAction Stop
            }

            "ByVirtualDisk"
            {
                $vmDirectVirtualDisks = $VirtualDisk
            }
        }

        # Remove the VM Direct virtual disks
        foreach ($vmDirectVirtualDisk in $vmDirectVirtualDisks)
        {
            # Get the current CIM session
            $currentCimSession = $vmDirectVirtualDisk.CimSession

            # Get the Virtual System Management Service
            $vmms = Get-CimInstance -ClassName 'Msvm_VirtualSystemManagementService' `
                                    -CimSession $currentCimSession `
                                    -Namespace $VirtualizationNamespace `
                                    -ErrorAction Stop

            if ($null -eq $vmms)
            {
                $error = CreateErrorRecord -ErrorId "ObjectNotFound" `
                                           -ErrorMessage "Could not find Virtual System Management Service." `
                                           -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound) `
                                           -Exception $null `
                                           -TargetObject $null

                $psCmdlet.WriteError($error)
                return
            }

            # Get the associated VM
            $currentVm = Get-VM -Id $vmDirectVirtualDisk.VMId `
                                -CimSession $currentCimSession `
                                -ErrorAction Stop

            $computerSystem = Get-CimInstance -ClassName 'Msvm_ComputerSystem' `
                                              -CimSession $currentCimSession `
                                              -Namespace $VirtualizationNamespace `
                                              -Filter "Name='$($currentVm.Id)'" `
                                              -ErrorAction Stop

            $virtualSystemSettingData = Get-CimAssociatedInstance -InputObject $computerSystem ` `
                                                                  -CimSession $currentCimSession `
                                                                  -ResultClassName 'Msvm_VirtualSystemSettingData' `
                                                                  -ErrorAction Stop

            $virtualSystemSettingData = $virtualSystemSettingData | Where-Object -FilterScript `
                                        {
                                            $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized'
                                        }

            # Get the associated controller
            $controller = Get-VMScsiController -VM $currentVm `
                                               -ControllerNumber $vmDirectVirtualDisk.ControllerNumber `
                                               -ErrorAction Stop

            $controllerResource = Get-CimAssociatedInstance -InputObject $virtualSystemSettingData `
                                                            -CimSession $currentCimSession `
                                                            -ResultClassName 'Msvm_ResourceAllocationSettingData' `
                                                            -ErrorAction Stop

            $controllerResource = $controllerResource | Where-Object -FilterScript `
                                  {
                                      $_.ResourceType -eq 6 -and
                                      $_.InstanceID   -eq $controller.Id
                                  }

            # Get the associated virtual disk
            $msftVirtualDisk = Get-VirtualDisk -UniqueId $vmDirectVirtualDisk.VirtualDiskUniqueId `
                                               -CimSession $currentCimSession `
                                               -ErrorAction Stop

            $poolId        = $msftVirtualDisk.ObjectId.Split('{}')[5]
            $virtualDiskId = $msftVirtualDisk.ObjectId.Split('{}')[7]

            $virtualDiskDescriptor = "{$poolId}{$virtualDiskId}"

            $lunSettingData = Get-CimAssociatedInstance -InputObject $controllerResource `
                                                        -CimSession $currentCimSession `
                                                        -ResultClassName 'Msvm_VirtualLogicalUnitSettingData' `
                                                        -ErrorAction Stop

            $lunSettingData = $lunSettingData | Where-Object -FilterScript `
            {
                $_.Connection -eq $virtualDiskDescriptor
            }

            # Remove the virtual disk from the VM
            $result = Invoke-CimMethod -InputObject $vmms `
                                       -CimSession $currentCimSession `
                                       -MethodName 'RemoveResourceSettings' `
                                       -Arguments @{ 'ResourceSettings' = [CimInstance[]]$lunSettingData } `
                                       -ErrorAction Stop

            if ($result.ReturnValue -ne 0 -and
                $result.ReturnValue -ne 4096)
            {
                $error = CreateErrorRecord -ErrorId "InvalidOperation" `
                                           -ErrorMessage "Operation failed with error code $($result.ReturnValue)" `
                                           -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidOperation) `
                                           -Exception $null `
                                           -TargetObject $null

                $psCmdlet.WriteError($error)
                return
            }

            # Wait for the job to complete
            if ($result.ReturnValue -eq 4096)
            {
                $job = $null

                while ($true)
                {
                    $job = Get-CimInstance -ClassName Msvm_ConcreteJob `
                                           -CimSession $currentCimSession `
                                           -Namespace $VirtualizationNamespace `
                                           -Filter "InstanceID='$($result.Job.InstanceID)'" `
                                           -ErrorAction Stop

                    if ($null -eq $job -or
                        $job.JobState -gt 4)
                    {
                        break
                    }

                    Start-Sleep -s 1
                }

                if ($null -ne $job -and
                    $job.ErrorCode -ne 0)
                {
                    $error = CreateErrorRecord -ErrorId "InvalidOperation" `
                                               -ErrorMessage $job.ErrorDescription `
                                               -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidOperation) `
                                               -Exception $null `
                                               -TargetObject $null

                    $psCmdlet.WriteError($error)
                    return
                }
            }
        }
    }
}