powershellrobocopyvhd

How to robocopy VHDs while skipping those that are in use?


I am trying to robocopy a set of VHDs, while skipping over those that are in use.

To that end, I am trying to create a list of all VHDs which are not in use. If the VHD is not in use, I will be able to run Get-VHD and check that the .Attached property is false. If the VHD is in use, I get the following error:

Get-VHD Getting the mounted storage instance for the path <VHD-Path> failed.
The operation cannot be performed while the object is in use.
CategoryInfo: ResourceBusy: (:) [Get-VHD], VirtualizationException
FullyQualifiedErrorID: ObjectInUse,Microsoft.Vhd.PowerShell.Cmdlets.GetVHD

My plan is to use a try-catch to identify which VHDs are in use, create a list of their file names, then pass it to the robocopy /xf option. To that end, the following code should output the names of all in-use VHDs to console:

$VHDLocation = "\\server\share"
$VHDs = Get-Children -Path $VHDLocation -Include "*.vhd" -Recurse

$VHDs | ForEach-Object {
try { Get-VHD ($VHDLocation + "\" + $_)).Attached }
catch { Write-Output $_ }}

However, when I run it, Powershell outputs "False" for VHDs that are not in use, and the "object in use" error for VHDs that are in use. It seems like the try-catch is being ignored in favour of just running the Get-VHD command.

Is there something wrong with the above code, or am I off the mark with how to accomplish this task altogether?


Solution

  • Untested, but I think your code is missing -ErrorAction Stop in the try block. Without that, Get-VHD calls that succeed will output the value of the Attached property, which is either $true or $false.
    Also, once inside a catch block, the $_ automatic variable no longer represents the item from the ForEach-Object loop, but rather the exception being thrown.

    Try:

    $VHDLocation = "\\server\share"
    $VHDs = Get-Children -Path $VHDLocation -Include "*.vhd" -Recurse
    
    # try and get an array of unattached VHD full file names
    $unAttached = foreach($vhd in $VHDs) {
        try { 
            # ErrorAction Stop ensures exceptions are being handled in the catch block
            $disk = $vhd | Get-VHD -ErrorAction Stop
            # if you get here, the Get-VHD succeeded, output if Attached is False
            if (!($disk.Attached)) { $vhd.FullName }
        }
        catch {
            # exception is thrown, so VHD must be in use; output this VHD object
            # inside a catch block, the '$_' automatic variable represents the exception
            $vhd.FullName
        }
    }
    

    Hope that helps