It's easy to copy files with PowerShell Copy-Item via the command line. Once you specify the source and destination location, it just happens. Unfortunately, many administrators don't think about how this process occurs until it doesn't work.

Whether or not you think about this, all TCP network communication (such as SMB file copies) use network ports to make the bits transfer. For a file copy process to get a file from point A to point B, a port needs to be open all the way to the destination node. In the case of an SMB file copy, that port is 445. This is a common port that's usually open internally, except in some high-security situations or across a DMZ.

PowerShell Copy-Item

If you're in a high-security environment or need to transfer files from an internal network to a DMZ that might have various port restrictions, how can you ensure that your scripts are able to copy files to nodes all the time? One way to do so is to use PowerShell v5's Copy-Item cmdlet with the new –ToSession parameter.

This parameter was introduced with Windows Management Framework (WMF) v5 with the Copy-Item cmdlet. It provides a way to transfer files over the same link that you might use today to execute commands remotely on computers with cmdlets like Invoke-Command.

This process has a few different advantages, but included in the biggest benefits are the TCP ports used: 5985 (HTTP) and 5986 (HTTPS). These standard ports are typically open to manage remote nodes, sometimes even to a DMZ environment. By using Copy-Item –ToSession, an administrator can ensure files will always be copied to a remote computer regardless of whether or not SMB is blocked.

When you're using PowerShell Copy-Item via the traditional SMB method, you need to specify the Path and Destination parameters. If you'd like to copy a file called file1.txt inside of C:\Folder to a remote computer SERVER1 on its C:\, you could do this:

Copy-Item –Path C:\Folder1\file1.txt –Destination '\\SERVER1\c$'

Notice that you're using the UNC path of \\SERVER1\c$ here. This will be important in a minute.

PowerShell Remoting Sessions

But what if SMB is blocked for some reason or you're using Invoke-Command to run commands on SERVER1 anyway? You can leverage PowerShell remoting sessions to transfer the file over WinRM instead of SMB. In order to do this, you must establish a new remoting session and then pass the file over that session.

First, you should create a new PowerShell remoting session. To do this, you can use the New-PSSession cmdlet and assign the session to the $session variable.

$session = New-PSSession –ComputerName SERVER1

This will use Kerberos authentication to establish a new PowerShell remoting session, which is the most common method to use when in an Active Directory environment.

Next, you need to specify the ToSession parameter and a local path on the remote computer for the Destination parameter.

Copy-Item –Path C:\Folder1\file1.txt –Destination 'C:\' –ToSession $session

Notice that you're now using C:\ for the destination rather than a UNC path. This command will accomplish the exact same thing as your previous one, but it will use the session to encapsulate the file and transfer it via WinRM.

Don't forget to remove the session when you're done by using Remove-PSSession.

$session | Remove-PSSession

If you don't intend to reuse the session for anything else, you could also create the session and tear it down (all at the same time).

Copy-Item –Path C:\Folder1\file1.txt –Destination 'C:\' –ToSession (New-PSSession –ComputerName SERVER1)

That's all there is to it! The next time you find yourself in an environment where PowerShell remoting is allowed but SMB is restricted, or you're already using a remoting session for something else, you can pass the session to Copy-Item to get your file transferred easily from point A to point B.