Copy file to remote servers (Powershell)


To totally unlock this section you need to Log-in

To copy files to remote servers we can use the Copy-Item cmdlet in PowerShell. This cmdlet will copy files and folders to a remote server using the following syntax:

Copy-Item "source" -Destination "\\server\C$"

Please note that the user you are running the commands under must also exist and work on the servers you are copying the file(s) to. All the examples shown are performed in a test domain environment using a Domain Admin account but as long as your user has sufficient rights the commands should work fine.

Let's use an example to demonstrate how Copy-Item works: let's say we want to copy an .exe file from server test01 to server testapp01. To do so, we will run:

Copy-Item "C:\Software\example.exe -Destination \\testapp01\c$\temp

As you can see from the above, we have defined the full path of the example.exe file and the destination it is going to, in this case \\testapp01\c$\temp.

To copy to another server you could use the same command and change the destination accordingly but that wouldn’t good enough for the Japinator. I prefer a slightly more sophisticated solution – we can use the Get-Content cmdlet or gc for short to get the contents of a text file called servers.txt that contains a list of the servers you want to copy to. This text file simply contains a list of servers, each on a new line.

We can pass each line of the contents of servers.txt as variables to the Copy-Item cmdlet to perform the copies to those servers. The script below will achieve this:

# This file contains the list of servers you want to copy files/folders to
$computers = gc "C:\scripts\servers.txt"

# This is the file/folder(s) you want to copy to the servers in the $computer variable
$source = "C:\Software\example.exe"

# The destination location you want the file/folder(s) to be copied to
$destination = "C$\temp\"

#The command below pulls all the variables above and performs the file copy
foreach ($computer in $computers)
{ if (Test-Path -Path \\$computer\$destination)
{ Copy-Item $source -Destination "\\$computer\$destination" -Recurse -Verbose}
}

The file copies successfully to all servers in C:\scripts\servers.txt successfully.

To make it a little but more sophisticated we will add a check to confirm that the remote servers’ path exists using the Test-Path cmdlet before performing the copy. If the path does not exist it will state that it “is not reachable or does not exist”:

# This file contains the list of servers you want to copy files/folders to
$computers = gc "C:\scripts\servers.txt"

# This is the file/folder(s) you want to copy to the servers in the $computer variable
$source = "C:\Software\EMC\Networker\NWVSS.exe"

# The destination location you want the file/folder(s) to be copied to
$destination = "C$\temp\"

foreach ($computer in $computers) {
if ((Test-Path -Path \\$computer\$destination)) {
Copy-Item $source -Destination \\$computer\$destination -Recurse
} else {
"\\$computer\$destination is not reachable or does not exist"
}
}

Another approach to the above code example is the following (without using external text files to define destination servers):

# The following array contains the destination servers
$computers = @("SRV1","SRV2","SRV3")

# This is the file/folder(s) you want to copy to the servers in the $computer variable
$source = "C:\Software\EMC\Networker\NWVSS.exe"

# The destination location you want the file/folder(s) to be copied to
$destination = "C$\temp\"

foreach ($computer in $computers) {
if ((Test-Path -Path \\$computer\$destination)) {
Copy-Item $source -Destination \\$computer\$destination -Recurse
} else {
"\\$computer\$destination is not reachable or does not exist"
}
}

Copy single file to multiple servers

Another approach to copy a single file on multiple systems is by using Invoke-Command that will handle all the parallel processing. You can modify the -ThrottleLimit parameter on Invoke-Command to change how many copies it does at the same time. The default is 32.

The following is an example code to use this approach (the source-server is the server from which the file will be copied on destination servers):

$servers = Get-Content 'C:\servers.txt'

Invoke-Command -ComputerName $servers -ScriptBlock {
Copy-Item -Path '\\source-server\500MB.zip' -Destination "C:\" -Verbose
}

In this case, the source path needs a reachable UNC where the 500MB.zip file, in this example, is located. The destination is local since you are running the command from the destination server.

Copy multiple files to multiple servers

Now let's say that we need to copy multiple files to multiple computers. A very simple approach to achieve this is the following:

$a = Get-Content "C:\computerlist.txt"
foreach ($i in $a)
{$files= get-content "C:\fileslist.txt"
foreach ($file in $files)
{Copy-Item $file -Destination "\\$i\C$\admin\" -Force}
}

Here is a sample of what the computerlist.txt will look like:

hostname1.example.com
hostname2.example.com
hostname3.example.com

And here is a sample of what the fileslist.txt will look like:

\\fileserver\share\IT\file1.ps1
\\fileserver\share\IT\file2.txt
\\fileserver\share\IT\file3.bat

Now, let's say we want not only copy multiple files to multiple servers, but also keep it more efficient by first comparing these files to check if it is needed to copy them (in case a previous version of these files were already copied in the past) on the destination endpoint.

$a = Get-Content "C:\computerlist.txt"

foreach ($i in $a)
{ $files = get-content "C:\fileslist.txt"

foreach ($file in $files)
{

If (Test-Path -Path "\\$i\C$\admin\$file") {
If ((Get-FileHash $file).hash -ne (Get-FileHash "\\$i\C$\admin\$file").hash) {
Copy-Item $file -Destination "\\$i\C$\admin\" -Force
}
} Else { Copy-Item $file -Destination "\\$i\C$\admin\" -Force }

}
}

The Get-FileHash code is rather efficient because Windows PowerShell is pretty fast when it comes to getting the file hash. Plus this operation simply obtains the file hashes, and compares the two hashes.

Credentials Security

A small consideration about the access process to each server during the copy of multiple files: if we are executing the Powershell script with an administrative user (such as a Domain or Enterprise Admin) interactively there should not be so much "issues" with this approach. But, if we need to specify a specific user, preferably a non-administrative one on both the sender and destination servers (we could think to a simple normal user with specific privileges only for the folders involved on servers and not with an access level so powerful to each system involved), and also "schedule" this copy activity a path to follow could be (not recommended, but usable) the following:

...
foreach ($computer in $computers) {
net use $computer /USER:domain\username password
...

An alternative to storing the "password" in clear text, as above, is by using the Get-Credential or Read-host -AsSecureString approaches to create first an encrypted password file and then use it for a scheduled Powershell script, but this will be exposed on a next article.

Summary
Article Name
Copy file to remote servers (Powershell)
Description
A common activity, on many IT environments, is to move/copy files from a system to another, usually in a scheduled mode. Let's see how to do it with Powershell on Windows systems.
Author
Publisher Name
Heelpbook.net