Simple, annoying task

Those kind I love. Why? Cause I can automate them :smile:

Here’s the story - a VM on a Hyper-V host with a bunch of disks, that are no longer needed. Task:

The hardest part was to map disk number from within OS to disk attached in VM configuration. It seems that Location from VM configuration maps to LUN number in Location within the Windows OS. That’s great!

Disk

Disclaimer

THIS IS A SCRIPT, NOT A FUNCTION

Bear in mind this is an interactive script that is supposed to be run with caution! Re-check your variables before removing. :smile: This is a dirty soltuion to get the task done!

Consider yourself warned!

Let’s get to work

$Credential = Get-Credential

$VMConnectionProps = @{
    ComputerName = 'TestVM' #VMName
}
$HostConnectionProps =@{
    ComputerName = 'HyperVHost1' #HyperV Host Name
}

if ($Credential) {
    $VMConnectionProps.Credential = $Credential
    $HostConnectionProps.Credential = $Credential
}
$disksToDisable =  Invoke-Command @VMConnectionProps -ScriptBlock {
    Get-Disk | where-object {$null -ne $PSItem.Number } 
} | Out-GridView -PassThru
if ($disksToDisable) {
    Invoke-Command @VMConnectionProps -ScriptBlock {
        foreach ($disk in $USING:disksToDisable) {
            Write-Host "Processing with disk - [$($disk.FriendlyName)] to disable."
            Set-Disk -InputObject $disk -IsOffline $true
        }
    }
}
$VMDisks = Invoke-Command @HostConnectionProps -ScriptBlock {
    Get-VM -Name $USING:VMConnectionProps.ComputerName | Select-Object -ExpandProperty HardDrives
}
$ToDelete= foreach ($diskToDisable in $disksToDisable) {
    $VMDisks | Where-Object {$PSItem.ControllerLocation -eq $diskToDisable.Number}
}
foreach ($disk in $ToDelete) {
    Write-Host "Processing VM [$($VMConnectionProp.ComputerName)] on Host [$($HostConnectionProps.ComputerName)] - Removing disk [$($disk.ControllerLocation)] with Path [$($disk.Path)]"
    Invoke-Command @HostConnectionProps -ScriptBlock {
        $removeVMHardDiskDriveSplat = @{
            ControllerType     = $USING:disk.ControllerType
            ControllerNumber   = $USING:disk.ControllerNumber
            VMName             = $USING:VMConnectionProps.ComputerName
            ControllerLocation = $USING:disk.controllerLocation
        }
        Remove-VMHardDiskDrive @removeVMHardDiskDriveSplat
    }
}
Invoke-Command @HostConnectionProps -ScriptBlock {
    foreach ($disk in $USING:ToDelete) {
        Remove-Item -Path $disk.Path 
    }
}

Full script

Here’s the full script

$Credential = Get-Credential

$VMConnectionProps = @{
    ComputerName = 'TestVM' #VMName
}
$HostConnectionProps =@{
    ComputerName = 'HyperVHost1' #HyperV Host Name
}

if ($Credential) {
    $VMConnectionProps.Credential = $Credential
    $HostConnectionProps.Credential = $Credential
}

#Get specifc disks from VM
$disksToDisable =  Invoke-Command @VMConnectionProps -ScriptBlock {
    Get-Disk | where-object {$null -ne $PSItem.Number } 
} | Out-GridView -PassThru

#Set disk to offline
if ($disksToDisable) {
    Invoke-Command @VMConnectionProps -ScriptBlock {
        foreach ($disk in $USING:disksToDisable) {
            Write-Host "Processing with disk - [$($disk.FriendlyName)] to disable."
            Set-Disk -InputObject $disk -IsOffline $true
        }
    }
}

#Get disks attached to VM
$VMDisks = Invoke-Command @HostConnectionProps -ScriptBlock {
    Get-VM -Name $USING:VMConnectionProps.ComputerName | Select-Object -ExpandProperty HardDrives
}

#get vhdx to disable and delete
$ToDelete= foreach ($diskToDisable in $disksToDisable) {
    $VMDisks | Where-Object {$PSItem.ControllerLocation -eq $diskToDisable.Number}
}

#detach disk from VM
foreach ($disk in $ToDelete) {
    Write-Host "Processing VM [$($VMConnectionProp.ComputerName)] on Host [$($HostConnectionProps.ComputerName)] - Removing disk [$($disk.ControllerLocation)] with Path [$($disk.Path)]"
    Invoke-Command @HostConnectionProps -ScriptBlock {
        $removeVMHardDiskDriveSplat = @{
            ControllerType     = $USING:disk.ControllerType
            ControllerNumber   = $USING:disk.ControllerNumber
            VMName             = $USING:VMConnectionProps.ComputerName
            ControllerLocation = $USING:disk.controllerLocation
        }
        Remove-VMHardDiskDrive @removeVMHardDiskDriveSplat
    }
}
#remove vhdx from Hyper-V
Invoke-Command @HostConnectionProps -ScriptBlock {
    foreach ($disk in $USING:ToDelete) {
        Remove-Item -Path $disk.Path 
    }
}