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
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
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
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:workfile.txt'
DestinationPath = 'C:workfile.txt'
CreateFullPath = $True
FileSource = 'Host'
Force = $True
Verbose = $True
}
My intention is to copy C:workfile.txt to the same path on CHI-DC01 and Win10Preview virtual machines.
Copy-VMFile @paramHash
It appears to be successful. I can double-check with PowerShell remoting.
invoke-command { get-item c:workfile.txt} -computer chi-dc01,chi-win10
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.
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.
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.
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.
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.
But usually this is temporary. I can always re-verify.
Get-VM -ComputerName chi-hvr2 | where {$_.state -eq 'running'} | Get-VMIntegrationService -Name Guest*
$paramHash = @{
SourcePath = 'c:workfile.txt'
DestinationPath = 'c:workfile.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:
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.