How to Copy Files with PowerShell Remoting

Copying files between computers has long been a common system administration task. In the Windows world this has usually been a no-brainer. The underlying protocols are mature and simply work. And while SMB 3.0 is a valuable improvement, I thought it might be interesting to copy files through a PowerShell remoting session.

With PowerShell remoting you have a secure, encrypted connection between two computers. It can be further protected by using SSL. Personally, I find the big benefit is that communication is done through a single port, which makes the entire process very firewall friendly. So why not use this single port connection to copy files?

Copy Files Over PowerShell Remoting
Copying files between computers is a very common task for system administrators, but PowerShell can help make that job much easier. (Image Credit: Jeff Hicks)

Using PowerShell Remoting to Copy Files

Let’s walk through the process, and maybe you’ll learn a new thing or two about PowerShell along the way. First, let’s consider a simple text file.

The $content variable holds the content of Computerdata.xml. We can use this variable with Invoke-Command and pass it to a remote computer.

I have parameterized the scriptblock to accept values for file content and a file name. Values are passed with the –ArgumentList parameter. This takes the value of $content and sends it to the remote computer where it is saved to a new file. All of this is accomplished over a PSSession.

If you are using PowerShell 3.0 or later, you can simplify this a bit and take advantage of $using.

Now, I can reference the variable $content, which exists on my computer in the remote PowerShell session. I could have even defined the path as a local variable and passed that with $using as well. This technique works fine with text files, but it will fail to properly copy anything else like a zip file. For that, we need to turn to the .NET Framework and use some methods from System.IO.File.

In this example, I am reading the content of the exe file by reading bytes. In the remote session, I can write all the bytes to the specified file. This scriptblock also gets the final file and writes the directory listing to the pipeline.

But you know, there’s no reason to have two separate techniques as reading and writing bytes will work for text files just as well. Now that I have the foundation of command, it is time to turn this into a more feature-rich tool. Allow me to introduce Copy-FiletoRemote.

Before I explain how this works let me mention a few caveats. First, due to restrictions with PowerShell remoting, you can’t copy any single file larger than 10MB. If you do, this command will throw an exception and the file won’t be copied. The other potential gotcha involves the PowerShell v5 preview. As I’m writing this there is a known issue with $using if one computer running v5 and the other is not. If both computers are running the v5 preview, there is no problem and my function works just fine. But if you are running PowerShell 4 and try to copy files to a computer running the v5 preview, you will get an error. I’m trusting that eventually this will get sorted out.

The function includes comment-based help.

Copy Files Over PowerShell Remoting

The syntax is simple enough. Specify a local file and a folder on a remote computer. The folder must already exist. My command will copy the file to the remote folder over a PSSession. This version doesn’t handle anything like recursive copying. It simply allows you to copy C:\MyFiles\Data.xml to C:\Some\Remote\Path\Data.xml on another computer. Actually, you can copy the same file or sets of files to multiple computers simultaneously because everything is done with Invoke-Command over a PSSession! Remoting also supports alternate credentials so I threw that in as well.

I assumed you would want to run a command like this:

A command like this would copy all XML files from the local C:\Files folder to the folder C:\XML on all the computers in the $MyServers variable.

My function uses a Begin, Process, and End scriptblock. Code in the Begin script block runs once before any pipeline input is processed. In this section of the script, I create the PSSessions to the specified computers.

Next, I verify that the target folder exists on each computer. If it doesn’t, then I remove that session, which in essence closes the connection.

The variable $myRemoteSessions includes all of the sessions, but now some of them will be closed. The easiest way I found to remove them is to filter them out and re-define the variable.

In the End script block, after all the files have been copied, these sessions are removed.

The Process script block includes code to handle each file. Because the path might be piped in, typed directly, or use a relative reference, I need to make sure I get a clean filesystem path, which is where Convert-Path comes into play.

Next I define some local variables and split up the file path to get the file name and construct the destination name.

These variables are used within my scriptblock, which is executed using Invoke-Command.

Because I wanted to support –WhatIf and –Passthru, I had to find a way to pass those settings to the remote script block.

The –ArgumentList parameter passes the local values to the remote computer.

Because I’ve setup SupportsShouldProcess, I can define my own WhatIf handling because calling a .NET static method has no concept of –WhatIF.

Now, I can run a command like this:

Defining WhatIf handling in Windows PowerShell . (Image Credit: Jeff Hicks)
Defining WhatIf handling in Windows PowerShell . (Image Credit: Jeff Hicks)

If that looks good, I can rerun the command without –Whatif. Note that I won’t see any results unless I use –Passthru.

Rerunning the command without WhatIf. (Image Credit: Jeff Hicks)
Rerunning the command without WhatIf. (Image Credit: Jeff Hicks)

I could just as easily have copied the files to 10 computers.

What do you think? Is something like this useful? What features do you think a future version should include? As with any PowerShell scripts you find on the Internet, please be sure to review and test in a non-production environment.

Related Topics:

  • PowerShell

    Don't have a login but want to join the conversation? Sign up for a Petri Account