“Clipboard” disks in Hyper-V   Leave a comment


With Windows Server 2012 R2 we’ll be getting a complete RDP experience when connecting to a VM through Hyper-V Manager, Failover Cluster Manager or VMM which means that we’ll get an easy way to copy content to a VM that’s otherwise isolated from a network.

There are still a couple of reasons why you’d want an alternative though and I’d like to present mine.

I’m certain that I’m not the only one looking for a solution to this problem and I’m equally sure that I’m not the only one who thought that hot-adding and removing disks to the SCSI-controller in a VM and then mounting and unmounting the VHD/VHDX to the host is an interesting way to do it.

I’d also be very surprised if I’m the only one who scripted this to some degree but as I wanted to write some PowerShell I haven’t checked if anyone else has done this.

So, here’s my version of a script for managing what I call “clipboard” disks. I’m not that good with PowerShell so it’s rather crude and although I’ve added what I think is enough for a version 1.0 there’s still lots of room for improvement so feel free to provide feedback in the comments.

Download from SkyDrive: ClipboardDisk.zip

<#
.AUTHOR
 Martin Edelius, 2013-07-12. Find me at https://hypervaccordingtome.wordpress.com/

.SYNOPSIS
 Allows you to attach and remove a VHDX to a running VM as well as to the host the VHDX is located on in a controlled manner.

.DESCRIPTION
 The script requires you to create a base clipboard VHDX, formatted and ready to use, located in an appropriate place on the Hyper-V host.

 If the VM you select to connect a VHDX to doesn't have a unique clipboard disk the script will create one based on the base clipboard disk.

 The script toggles the clipboard disk between being attached to the VM and to the host until you manually exit the loop.

 The script also provides options for removing the clipboard disk from all VMs and also to unmount them all from the host.

 In order to locate the clipboard disk the script uses location 63 on SCSI-controller 0 of the VM which means that A) you need a SCSI-controller and B) that location has to be empty.
 
.EXAMPLE
 PS> ClipboardDisk.ps1
 
 This starts the script which from here on is interactive. The script doesn't accept any parameters.
  
.NOTES
 You must be running as a user that has permissions to attach a VHDX to a VM and also permissions to mount a VHDX to the host.

 Feel free to modify and distribute this script to your hearts content but if you do any major modifications I want you to post a comment to this post: http://wp.me/p1vNsA-2u with a link back to your modified script so that others may benefit from it.

 Finally; you're using this completely on your own risk, I take no responsibility for any damage this script may do. If something breaks, you own both parts.
#>

# Path to the base clipboard disk that is used for new, unique clipboard disks
# Do not forget the trailing '\'!
$BaseClipboardDiskPath = "C:\VHD\Clipboards\";

# Name of the base clipboard disk that is used for new, unique clipboard disks
$BaseClipboardDiskName = "Clipboard - base.vhdx";

## PLEASE DO NOT MODIFY BELOW THIS LINE UNLESS YOU KNOW WHAT YOU'RE DOING! ##

# Select action
Write-Host("Select option:`n1. Toggle clipboard disk for VM (default)`n2. Unmount all disks from host`n3. Disconnect clipboard disk from all VMs");
$Action = Read-Host("[Enter = default]");

# Set default behaviour
if($Action.Length -eq 0)
{
    $Action = 1;
}

switch($Action)
{
    1 # Toggle clipboard disk
    {
        # Loop through VMs and list their names and corresponding numbers
        $VmInfo = @{};

        $VMs = Get-VM;

        # Make sure that we actually have any VMs to list
        if($VMs.Length -eq 0)
        {
            Write-Host("`nCouldn't find any VMs, do you have correct permissions to list them?`n");
        }
        else
        {
            Write-Host(" ");   # Just to introduce a newline, for readability's sake.
            $VMidx = 1;
            foreach($VM in $VMs)
            {
                Write-Host($VMidx.ToString() + ": " + $VM.Name);
                $VmInfo.Add($VMidx++, $VM.Name);
            }

            # Grab the index of the VM we want to attach a clipboard disk to
            do
            {
                $VMidx = Read-Host("Choose VM (1 - " + $VmInfo.Count + ")");
            }
            while($VMidx.Length -le 0 -and $VMidx -le 0);

            # Grab the name of the VM from the info hashtable
            $VMname = $VmInfo.Get_Item([int]$VMidx);

            # Do we have a unique clipboard disk for this VM?
            $VMDiskPath = "$BaseClipboardDiskPath$VMname.VHDX";

            if(!(Test-Path $VMDiskPath))
            {
                # Create new clipboard disk
                Write-Host("`nCreating unique clipboard disk: $VMDiskPath");
                Copy-Item "$BaseClipboardDiskPath$BaseClipboardDiskName" -Destination $VMDiskPath;
            }
            else
            {
                Write-Host("`nUnique clipboard disk exists: $VMDiskPath");
            }

            # This runs in a loop that only exits when the user enters the letter 'q'
            do
            {
                # Grab all disks attached to this VM
                $VMHDAttached = $false;
                $VMHardDisks = Get-VM -Name $VMName | Get-VMHardDiskDrive

                # We need a way to identify the clipboard disk so it always attaches to SCSI-controller 0, position 63
                foreach($VMHardDisk in $VMHardDisks)
                {
                    if($VMHardDisk.ControllerType -eq "SCSI" -and $VMHardDisk.ControllerNumber -eq 0 -and $VMHardDisk.ControllerLocation -eq 63)
                    {
                        $VMHDAttached = $true;
                    }
                }

                # Did we find one at our designated clipboard position?
                if($VMHDAttached)
                {
                    # Disk is attached so lets remove it and then mount it to our host
                    Remove-VMHardDiskDrive -ControllerLocation 63 -ControllerNumber 0 -ControllerType SCSI -VMName $VMName;
                    Write-Host("`nDisk removed from VM!");
                    Mount-VHD $VMDiskPath;
                    Write-Host("Disk mounted to host!");
                }
                else
                {
                    Write-Host(" ");   # Need to introduce a newline, for readability's sake.
                    # Unmount disk from host and then attach to VM
                    if((Get-VHD -Path $VMDiskPath).Attached -eq $true)
                    {
                        # Only dismount if it's attached to the host to begin with. Duh.
                        Dismount-VHD $VMDiskPath;
                        Write-Host("Disk unmounted from host!");
                    }
                    Add-VMHardDiskDrive -VMName $VMName -ControllerLocation 63 -ControllerNumber 0 -ControllerType SCSI -Path $VMDiskPath
                    Write-Host("Disk attached to VM!");
                }

                $subAction = Read-Host("'Q' to quit loop, [Enter] to toggle");
            }
            while($subAction.ToUpper() -ne 'Q');
        }
    }

    2 # Unmount all VHDs from host
    {
        $VHDs = Get-VHD "$BaseClipboardDiskPath*.VHDX";

        foreach($VHD in $VHDs)
        {
            if($VHD.Attached -eq $true)
            {
                # Only dismount if it's attached to the host to begin with. Duh.
                Dismount-VHD $VHD.Path;
                Write-Host($VHD.Path +" unmounted from host!");
            }
        }

        Write-Host("`nDone!`n");
    }

    3 # Disconnect clipboard VHD from all VMs
    {
        $VMs = Get-VM;

        foreach($VM in $VMs)
        {
            $VMHardDisks = $VM| Get-VMHardDiskDrive;

            # We need a way to identify the clipboard disk so it always attaches to SCSI-controller 0, position 63
            foreach($VMHardDisk in $VMHardDisks)
            {
                if($VMHardDisk.ControllerType -eq "SCSI" -and $VMHardDisk.ControllerNumber -eq 0 -and $VMHardDisk.ControllerLocation -eq 63)
                {
                    Remove-VMHardDiskDrive $VMHardDisk;
                    Write-Host("`nDisk disconnected from VM " + $VM.Name +"!");
                }
            }
        }
        Write-Host("`nDone!`n");
    }
}
Advertisements

Posted 12 July, 2013 by martinnr5 in Technical, Tools

Tagged with , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: