How to Copy Files between Hyper-V Host and Guests with PowerShell

Copying files to Hyper-V virtual machines wouldn’t seem like a big deal. In most situations, the virtual machine is no different than a physical machine on your network. You could copy files to a virtual machine using traditional methods like you would any other machine, but sometimes that isn’t possible. Fortunately, there is an alternative. If you are running the latest version of Hyper-V on either Windows Server 2012 R2 or Windows 8.1, which implies that you’re running PowerShell 4.0, then you have access to a new cmdlet in the Hyper-V module called Copy-VMFile.

I’m going to demonstrate how to use this cmdlet, but be sure to take time to read the help.

help Copy-VMFile -ShowWindow

Copy-VMFile help in Windows PowerShell. (Image Credit: Jeff Hicks)
Copy-VMFile help in Windows PowerShell. (Image Credit: Jeff Hicks)

 
I’m first going to demonstrate from my Windows 8.1 client that’s running Hyper-V. The Guest Services feature of VM Integration Services must be enabled on any guest virtual machines in order to use this cmdlet. Here’s how you can verify if that feature is enabled:

Get-VMIntegrationService -name Guest* -VMName chi-dc01,chi-dc02,win10preview

Verifying that the Guest Services feature is enabled in Windows PowerShell. (Image Credit: Jeff Hicks)
Verifying that the Guest Services feature is enabled in Windows PowerShell. (Image Credit: Jeff Hicks)

 
The service on CHI-DC01 is all set. It is enabled on CHI-DC02, but notice that the status indicates no contact. That particular virtual machine needs to be updated so that I can install the latest VM Integration Services. Until I get a connection for Guest Services, I won’t be able to use Copy-VMFile. The last virtual machine is running a Windows 10 preview, and as you can see, Guest Services are not enabled. Let’s fix that.

Enable-VMIntegrationService -name Guest* -VMName win10preview -Passthru

Enabling Guest Services in Windows PowerShell. (Image Credit: Jeff Hicks)
Enabling Guest Services in Windows PowerShell. (Image Credit: Jeff Hicks)

 
That looks pretty good. Time to copy a file.
When you copy a file, you must specify the source and destination. The source location is relative to the Hyper-V host, which in my situation is my local host running Windows 8.1. The destination is the path on the virtual machine. I especially like that the cmdlet has a parameter, CreateFullPath, that will create all folders that are part of the destination path if they don’t already exist. If you think the file might already exist be sure to use –Force, otherwise you will get an error. Naturally, you must specify the virtual machine names.

A bit later I’ll show you how to pipe a virtual machine object to this cmdlet. Finally, you have to include the –FileSource parameter. Right now this can only take a value of Host. I’m assuming at some point in the future we might be able to copy files from a UNC or the local host. But for now, use Host. Here’s a hashtable of Copy-VMFile parameters:

$paramHash = @{
 Name = 'chi-dc01','win10preview'
 SourcePath = 'C:\work\file.txt'
 DestinationPath = 'C:\work\file.txt'
 CreateFullPath = $True
 FileSource = 'Host'
 Force = $True
 Verbose = $True
}

My intention is to copy C:\work\file.txt to the same path on CHI-DC01 and Win10Preview virtual machines.

Copy-VMFile @paramHash

Copying files using Copy-VMFile in Windows PowerShell. (Image Credit: Jeff Hicks)
Copying files using Copy-VMFile in Windows PowerShell. (Image Credit: Jeff Hicks)

 
It appears to be successful. I can double-check with PowerShell remoting.

invoke-command { get-item c:\work\file.txt} -computer chi-dc01,chi-win10

Verifying that the copy file operation completed successfully. (Image Credit: Jeff Hicks)
Verifying that the copy file operation completed successfully. (Image Credit: Jeff Hicks)

 
As you can see, my Windows 10 virtual machine isn’t set up to play nicely on the network, so traditional file copy methods would probably have failed. But I can see from the VM console that the file was indeed copied.
The file was successfully copied to the correct location. (Image Credit: Jeff Hicks)
The file was successfully copied to the correct location. (Image Credit: Jeff Hicks)

 
What about copying multiple files or a folder? Sadly, the source and destination parameter don’t accept arrays or wildcards. If you want to copy multiple files, then you need to process each one. I’m going to go ahead and re-use my existing hashtable of parameter values.

dir c:\files | foreach {
 $destination = Join-path -Path "C:\files" -ChildPath $_.name
 write-host "copying $($_.fullname) to $destination" -foreground Yellow
 $paramHash.SourcePath = $_.fullname
 $paramHash.DestinationPath = $destination
 Copy-VMFile @paramHash
}

My goal is to copy all the files under C:\Files to CHI-DC01 and my Windows 10 virtual machines. I’m taking each file and updating parameters in the hashtable.

Prepping my hashtable for multiple files. (Image Credit: Jeff Hicks)
Prepping my hashtable for multiple files. (Image Credit: Jeff Hicks)

 
Success!
Now that you understand the mechanics, let’s switch to a more typical scenario where you want to do this for a Hyper-V host and virtual machines, but from your client desktop. The tricky part is that whatever files you want to copy must reside on the Hyper-V host. You can use whatever method you want to get the files there. I have a file under C:\work on my Hyper-V host, CHI-HVr2.
012015 2057 CopyFilesbe8
I want to copy this version of the file to all running virtual machines. That means I need to make sure Guest Services is enabled.

get-vm -ComputerName chi-hvr2| where {$_.state -eq 'running' -AND -Not ((Get-VMIntegrationService -Name Guest* -vm $_).Enabled)}

These virtual machines fail that test.

Verifying that Guest Services is enabled. (Image Credit: Jeff Hicks)
Verifying that Guest Services is enabled. (Image Credit: Jeff Hicks)

 
To enable these virtual machines, I can add on the Enable-VMIntegrationService cmdlet to my previous expression.

get-vm -ComputerName chi-hvr2|
where {$_.state -eq 'running' -AND -Not ((Get-VMIntegrationService -Name Guest* -vm $_).Enabled)} |
Enable-VMIntegrationService -Name Guest* -Passthru

It looks like there is a problem.
012015 2057 CopyFilesbe10
But usually this is temporary. I can always re-verify.

Get-VM -ComputerName chi-hvr2 | where {$_.state -eq 'running'} | Get-VMIntegrationService -Name Guest*

012015 2057 CopyFilesbe11

$paramHash = @{
 SourcePath = 'c:\work\file.txt'
 DestinationPath = 'c:\work\file.txt'
 Force = $True
 CreateFullPath = $True
 FileSource = 'Host'
 Verbose = $True
}
Get-VM -ComputerName chi-hvr2 |
where {$_.state -eq 'running'} |
Copy-VMFile @paramHash

Here’s the command in action:

Copying a file to a guest. (Image Credit: Jeff Hicks)
Copying a file to a guest. (Image Credit: Jeff Hicks)

 
If you are copying a lot of files or copying to many virtual machines, you also have an option to run Copy-VMFile as a job. Again, be sure to read cmdlet help.
And that’s all there is to it. You can copy files between the Hyper-V host and its virtual machine guests regardless of the state of the guest Windows operating system.