Copy file remotely with PowerShell - powershell

I am writing a PowerShell script that I want to run from Server A.
I want to connect to Server B and copy a file to Server A as a backup.
If that can't be done then I would like to connect to Server B from Server A and copy a file to another directory in Server B.
I see the Copy-Item command, but I don't see how to give it a computer name.
I would have thought I could do something like
Copy-Item -ComputerName ServerB -Path C:\Programs\temp\test.txt -Destination (not sure how it would know to use ServerB or ServerA)
How can I do this?

From PowerShell version 5 onwards (included in Windows Server 2016, downloadable as part of WMF 5 for earlier versions), this is possible with remoting. The benefit of this is that it works even if, for whatever reason, you can't access shares.
For this to work, the local session where copying is initiated must have PowerShell 5 or higher installed. The remote session does not need to have PowerShell 5 installed -- it works with PowerShell versions as low as 2, and Windows Server versions as low as 2008 R2.[1]
From server A, create a session to server B:
$b = New-PSSession B
And then, still from A:
Copy-Item -FromSession $b C:\Programs\temp\test.txt -Destination C:\Programs\temp\test.txt
Copying items to B is done with -ToSession. Note that local paths are used in both cases; you have to keep track of what server you're on.
[1]: when copying from or to a remote server that only has PowerShell 2, beware of this bug in PowerShell 5.1, which at the time of writing means recursive file copying doesn't work with -ToSession, an apparently copying doesn't work at all with -FromSession.

Simply use the administrative shares to copy files between systems.
It's much easier this way.
Copy-Item -Path \\serverb\c$\programs\temp\test.txt -Destination \\servera\c$\programs\temp\test.txt;
By using UNC paths instead of local filesystem paths, you help to
ensure that your script is executable from any client system with
access to those UNC paths. If you use local filesystem paths, then you
are cornering yourself into running the script on a specific computer.

Use net use or New-PSDrive to create a new drive:
New-PsDrive: create a new PsDrive only visible in PowerShell environment:
New-PSDrive -Name Y -PSProvider filesystem -Root \\ServerName\Share
Copy-Item BigFile Y:\BigFileCopy
Net use: create a new drive visible in all parts of the OS.
Net use y: \\ServerName\Share
Copy-Item BigFile Y:\BigFileCopy

Just in case that the remote file needs your credential to get accessed, you can generate a System.Net.WebClient object using cmdlet New-Object to "Copy File Remotely", like so
$Source = "\\192.168.x.x\somefile.txt"
$Dest = "C:\Users\user\somefile.txt"
$Username = "username"
$Password = "password"
$WebClient = New-Object System.Net.WebClient
$WebClient.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
$WebClient.DownloadFile($Source, $Dest)
Or if you need to upload a file, you can use UploadFile:
$Dest = "\\192.168.x.x\somefile.txt"
$Source = "C:\Users\user\somefile.txt"
$WebClient.UploadFile($Dest, $Source)

None of the above answers worked for me. I kept getting this error:
Copy-Item : Access is denied
+ CategoryInfo : PermissionDenied: (\\192.168.1.100\Shared\test.txt:String) [Copy-Item], UnauthorizedAccessException>
+ FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.CopyItemCommand
So this did it for me:
netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=yes
Then from my host my machine in the Run box I just did this:
\\{IP address of nanoserver}\C$

Related

Copy a file to a remote computer

I have made two instances on sky-high; cl1 and srv1. I am trying to copy a folder from cl1 to srv1. I can use the command
Enter-PSSession -Credential $cred IP_ADD_SRV1
from cl1 to get into srv1. I have been looking at the help site for copy-item and found this command called Copy a file to a remote computer. Is this right? The command is
$Session = New-PSSession -ComputerName "Server01" -Credential "Contoso\User01"
Copy-Item "D:\Folder001\test.log" -Destination "C:\Folder001_Copy\" -ToSession $Session
My questions are:
Is the ComputerName just the name I called them on my Microsoft Remote Desktop?
And what do I put as the credential?
My problem is that the path for the two folders I want to copy are almost the same. Someone told me I need to use the UNC path. Do I need to use this
both at the copy-item and destination? I am new to this, but does
this look right for the UNC path: \\cl1\C$\Users\Admin\Test. ?
You can copy a file or folder from a pc to a remote machine in several ways.
A 'Normal' copy (not using a Session object)
if the pc you are logged into is called cl1 and the file is on that computer (source), you need to specify the Destination in UNC format:
Copy-Item -Path 'C:\SourceFolder\TheFileToCopy.txt' -Destination '\\srv1\c$\DestinationFolder'
If however the file is on the remote machine and you need to copy that TO the machine you're logged into, then the Source should be in UNC format:
Copy-Item -Path '\\srv1\c$\TheFileToCopy.txt' -Destination 'C:\DestinationFolder'
Using the Session object
if the pc you are logged into is called cl1 and the file is on that computer (source) and you have established a session using $session = New-PSSession –ComputerName srv1 to the remote machine, then you need to specify both the Path and Destination parameters as LOCAL paths:
Copy-Item -Path 'C:\SourceFolder\TheFileToCopy.txt' -Destination 'C:\DestinationFolder' -ToSession $session
A Credential object contains user name and (encrypted) password to use to authenticate to the remote machine. Use the Get-Credential cmdlet for that
It seems you want to copy a directory from a source on computer Cl1 to a path on the remote server srv1.
From your comments, I see that the source is C:\Users\Admin\Test (that is the LOCAL path of the computer you are logged in to, i.e. Cl1) and that the destination would be C:\Users\Admin\Backup on the REMOTE machine.
That is why you need to use the UNC format for the destination path, C:\Users\Admin\Backup --> \\srv1\C$\Users\Admin\Backup.
Using the servers name needs DNS to be set up properly, so you can also use the IP address of that server instead of its name. Suppose that the server has IP 10.212.141.129, the UNC path for the destination would then become \\10.212.141.129\C$\Users\Admin\Backup.
However.. You are targetting the so-called Administrative Share (C$), and for that you need to have permissions. Also you are targetting a user folder for user Admin (which is user Admin on the remote machine, and that is not the same one as the Administrator on your computer.
Therefore, it is quite possible you do not have access permissions on the target folder.
You can give yourself permissions (if you know the correct credentials of course) by adding parameter -Credential $cred to the Copy-Item cmdlet. Such a credentials object is easily obtained by using
$cred = Get-Credential -Message "Please enter Domain Admin credentials"
For Copy-Item to be able to copy something to somewhere, you must make sure the destination path exists.
Try to navigate in File Explorer to that remote path using the same UNC naming convention.
If for instance the path \\srv1\C$\Users\Admin exists, but there is no folder Backup, (and you have permissions to do so), create that folder, either from within Explorer, or in PowerShell:
if (-not (Test-Path -LiteralPath '\\srv1\C$\Users\Admin\Backup' -PathType Container)) {
$null = New-Item -Path '\\srv1\C$\Users\Admin\Backup' -ItemType Directory
}
Next, you should be able to copy all files and subfolders from the source directory to that destination using
Copy-Item -Path 'C:\Users\Admin\Test' -Destination '\\srv1\C$\Users\Admin\Backup' -Recurse # -Credential $cred # can go here
# local source on cl1 ^^^^ ^^^^ to remote destination on srv1
Of course, you can also use the Session method I've described earlier., where in that case you should use local pathnames (C:\whatever) and don't need UNC paths, because the $session object takes care of that for you.
It could be that on the destination server, there is a share set-up for you that resides somewhere else. For instance a folder X:\Students\Course1\Output and that path has been shared as StudentMaterial$.
If this might be the case (ask your teacher) you can set the destination as \\srv1\StudentMaterial$ and you do not need to go all the way via the Administrative Share.
Hope this explains some more

using invoke-command to create files on a remote server

I am new to powershell and all sorts of scripting and have been landed with the following task.
I need to create a file on a remote server based on the filename picked up on the local server using the invoke-command.
WinRM is configured and running on the remote server.
What i need to happen is the following
On Server1 a trigger file is placed folder. Powershell on Server1 passes the filename onto powershell on Server2. Powershell on Server2 then creates a file based on the name.
My heads been melted trolling through forms looking for inspiration, any help would be greatly appreciated
Many thanks
Paul
I think if you're new to scripting, something that will add a lot of extra complexity is storing and handling credentials for Invoke-Command. It would be easier if you could make a shared folder on Server2 and just have one PowerShell script writing to that.
Either way, a fairly simple approach is a scheduled task on Server1 which runs a PowerShell script, with its own service user account, every 5 minutes.
Script does something like:
# Check the folder where the trigger file is
# assumes there will only ever be 1 file there, or nothing there.
$triggerFile = Get-ChildItem -LiteralPath "c:\triggerfile\folder\path"
# if there was something found
if ($triggerFile)
{
# do whatever your calculation is for the new filename "based on"
# the trigger filename, and store the result. Here, just cutting
# off the first character as an example.
$newFileName = $triggerFile.Name.Substring(1)
# if you can avoid Invoke-Command, directly make the new file on Server2
New-Item -ItemType File -Path '\\server2\share\' -Name $newFileName
# end here
# if you can't avoid Invoke-Command, you need to have
# pre-saved credentials, e.g. https://www.jaapbrasser.com/quickly-and-securely-storing-your-credentials-powershell/
$Credential = Import-CliXml -LiteralPath "${env:\userprofile}\server2-creds.xml"
# and you need a script to run on Server2 to make the file
# and it needs to reference the new filename from *this* side ("$using:")
$scriptBlock = {
New-Item -ItemType File -Path 'c:\destination' -Name $using:newFileName
}
# and then invoke the scriptblock on server2 with the credentials
Invoke-Command -Computername 'Server2' -Credential $Credential $scriptBlock
# end here
# either way, remove the original trigger file afterwards, ready for next run
Remove-Item -LiteralPath $triggerFile -Force
}
(Untested)

Powershell - Copying File to Remote Host and Executing Install exe using WMI

EDITED: Here is my code now. The install file does copy to the remote host. However, the WMI portion does not install the .exe file, and no errors are returned. Perhaps this is a syntax error with WMI? Is there a way to just run the installer silently with PsExec? Thanks again for all the help sorry for the confusion:
#declare params
param (
[string]$finalCountdownPath = "",
[string]$slashes = "\\",
[string]$pathOnRemoteHost = "c:\temp\",
[string]$targetJavaComputer = "",
[string]$compname = "",
[string]$tempPathTarget = "\C$\temp\"
)
# user enters target host/computer
$targetJavaComputer = Read-Host "Enter the name of the computer on which you wish to install Java:"
[string]$compname = $slashes + $targetJavaComputer
[string]$finalCountdownPath = $compname + $tempPathTarget
#[string]$tempPathTarget2 =
#[string]$finalCountdownPath2 = $compname + $
# say copy install media to remote host
echo "Copying install file and running installer silently please wait..."
# create temp dir if does not exist, if exist copy install media
# if does not exist create dir, copy dummy file, copy install media
# either case will execute install of .exe via WMII
#[string]$finalCountdownPath = $compname + $tempPathTarget;
if ((Test-Path -Path $finalCountdownPath) )
{
copy c:\hdatools\java\jre-7u60-windows-i586.exe $finalCountdownPath
([WMICLASS]"\\$targetJavaComputer\ROOT\CIMV2:win32_process").Create("cmd.exe /c c:\temp\java\jre-7u60-windows-i586.exe /s /v`" /qn")
}
else {
New-Item -Path $finalCountdownPath -type directory -Force
copy c:\hdatools\dummy.txt $finalCountdownPath
copy "c:\hdatools\java\jre-7u60-windows-i586.exe" $finalCountdownPath
([WMICLASS]"\\$targetJavaComputer\ROOT\CIMV2:win32_process").Create("cmd.exe /c c:\temp\java\jre-7u60-windows-i586.exe /s /v`" /qn")
}
I was trying to get $Job = Invoke-Command -Session $Session -Scriptblock $Script to allow me to copy files on a different server, because I needed to off load it from the server it was running from. I was using the PowerShell Copy-Item to do it. But the running PowerShell script waits until the file is done copying to return.
I want it to take as little resources as possible on the server that the powershell is running to spawn off the process on another server to copy the file. I tried to user various other schemes out there, but they didn't work or the way I needed them to work. (Seemed kind of kludgey or too complex to me.) Maybe some of them could have worked? But I found a solution that I like that works best for me, which is pretty easy. (Except for some of the back end configuration that may be needed if it is is not already setup.)
Background:
I am running a SQLServer Job which invokes Powershell to run a script which backups databases, copies backup files, and deletes older backup files, with parameters passed into it. Our server is configured to allow PowerShell to run and under the pre-setup User account with SQL Server Admin and dbo privileges in an Active Directory account to allow it to see various places on our Network as well.
But we don't want it to take the resources away from the main server. The PowerShell script that was to be run would backup the database Log file and then use the another server to asynchronously copy the file itself and not make the SQL Server Job/PowerShell wait for it. We wanted it to happen right after the backup.
Here is my new way, using WMI, using Windows Integrate Security:
$ComputerName = "kithhelpdesk"
([Wmiclass]'Win32_Process').GetMethodParameters('Create')
Invoke-WmiMethod -ComputerName RemoteServerToRunOn -Path win32_process -Name create -ArgumentList 'powershell.exe -Command "Copy-Item -Path \\YourShareSource\SQLBackup\YourDatabase_2018-08-07_11-45.log.bak -Destination \\YourShareDestination\YourDatabase_2018-08-07_11-45.log.bak"'
Here is my new way using passed in Credentials, and building arg list variable:
$Username = "YouDomain\YourDomainUser"
$Password = "P#ssw0rd27"
$ComputerName = "RemoteServerToRunOn"
$FromFile = "\\YourShareSource\SQLBackup\YourDatabase_2018-08-07_11-45.log.bak"
$ToFile = "\\YourShareDestination\SQLBackup\YourDatabase_2018-08-07_11-45.log.bak"
$ArgumentList = 'powershell.exe -Command "Copy-Item -Path ' + $FromFile + ' -Destination ' + $ToFile + '"'
$SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $Username, $SecurePassWord
([Wmiclass]'Win32_Process').GetMethodParameters('Create')
Invoke-WmiMethod -ComputerName $ComputerName -Path win32_process -Name create -ArgumentList $ArgumentList -Credential $Cred
We think that this above one is the preferred one to use.
You can also run a specific powershell that will do what you want it to do (even passing in parameters to it):
Invoke-WmiMethod -ComputerName RemoteServerToRunOn -Path win32_process -Name create -ArgumentList 'powershell.exe -file "C:\PS\Test1.ps1"'
This example could be changed to pass in parameters to the Test1.ps1 PowerShell script to make it more flexible and reusable. And you may also want to pass in a Credential like we used in a previous example above.
Help configuring WMI:
I got the main gist of this working from: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/invoke-wmimethod?view=powershell-5.1
But it may have also needed WMI configuration using:
https://helpcenter.gsx.com/hc/en-us/articles/202447926-How-to-Configure-Windows-Remote-PowerShell-Access-for-Non-Privileged-User-Accounts?flash_digest=bec1f6a29327161f08e1f2db77e64856b433cb5a
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/enable-psremoting?view=powershell-5.1
Powershell New-PSSession Access Denied - Administrator Account
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/invoke-wmimethod?view=powershell-5.1 (I used to get how to call Invoke-WmiMethod).
https://learn.microsoft.com/en-us/powershell/scripting/core-powershell/console/powershell.exe-command-line-help?view=powershell-6 (I used to get syntax of command line)
I didn't use this one, but could have: How to execute a command in a remote computer?
I don't know for sure if all of the steps in the web articles above are needed, I suspect not. But I thought I was going to be using the Invoke-Command PowerShell statement to copy the files on a remote server, but left my changes from the articles above that I did intact mostly I believe.
You will need a dedicated User setup in Active Directory, and to configure the user accounts that SQL Server and SQL Server Agent are running under to give the main calling PowerShell the privileges needed to access the network and other things to, and can be used to run the PowerShell on the remote server as well. And you may need to configure SQLServer to allow SQL Server Jobs or Stored Procedures to be able to call PowerShell scripts like I did. But this is outside the scope of this post. You Google other places on the internet to show you how to do that.

NET USE only works once in powershell

My requirement is to connect to a remote computer, copy files with the extension of .TRA to a different remote computer.
Now my difficulty arises in that the source machine aren't on the domain so I need to connect with alternate credentials.
My first sticking point is trying to use New-PSDrive. It doesn't appear to allow me to pass stored credentials. I have tried various versions of this:
$cred = Get-Credential
New-PSDrive -NAME Z: -PSprovider Filesystem -root \\Computer\Exchange -credentials $cred
and I get an error
The provider does not support the use of credentials. Perform the operation again without specifying credentials.
This appears to be a bug (see halr9000's comment here : Connecting to a network folder with username/password in Powershell)
My alternative was to use NET USE, which works fine but only once.
In the full script below I map to 8 different machines. The first of these works. After that any attempt to use NET USE (or New-Object WScript.NETWORK, mentioned as a work around on the Technet article linked above) fails. Or rather it doesn't fail.
Its creates a mapped drive, which I can see if I type NET USE, or if I look in explorer (and its browsable). But I cannot connect to that drive in Powershell. Its like it doesn't exist, Test-Path Z: returns False. Right up until I run NET USE Z: /D which successfully deletes the drive.
Can someone please help me with one or other of my issues please? Don't make me do this in DOS!
Full script :
$int = 0
$arrStagePC = "BOX42", "BOX43", "BOX44", "BOX45", "BOX46", "BOX47", "BOX48", "BOX49"
$arrData = "0848","5144","5292","5383","2158","2646","0061","2331"
Foreach($strComp in $arrStagePC)
{
$arr = $arrData[$int]
$comp = $strComp
net use z: \\$arr\Exchange /user:admin password
Copy-Item z:\*$date.tra \\$comp\Export$
net use z: /d
$int++
}
EDIT
Have a look at #Christians accepted answer below. I didn't actually need to Get-Content that folder, I just needed to copy data from the source so the bit of the script doing the business on there now looks like :
Foreach($strComp in $arrStagePC)
{
$arr = $arrData[$int]
$comp = $strComp
net use z: \\$arr\Exchange /user:admin password
Copy-Item FILESYSTEM::Z:\*.TRA \\$comp\Export$
net use z: /d
$int++
}
Using NET USE try access in this way:
Set-Location FILESYSTEM::Z:
Had same issue. Mended mine by adding
*New-PSDrive z FileSystem z:*
after the NET USE. Seems like NET USE /DELETE disturbs the PS housekeeping.
Try this, maybe:
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive("Z:", "\\$arr\Exchange", $false, "admin", "password")

Powershell doesn't have access to a network share

I use powershell to check if the ports are opened on my computers. I got 8 windows 2008 R2 machines and I run the following script :
$localhost = get-content env:computername
foreach($port in get-content "\\computer1\txtfiles\ports.txt")
{
foreach ($hostname in get-content "\\compiuter1\txtfiles\servers.txt")
{
try {
$sock = new-object System.Net.Sockets.Socket -ArgumentList $([System.Net.Sockets.AddressFamily]::InterNetwork),$([System.Net.Sockets.SocketType]::Stream),$([System.Net.Sockets.ProtocolType]::Tcp)
$sock.Connect($hostname,$Port)
$output = $localhost+","+$hostname+","+$port+","+$sock.Connected
$output
$sock.Close()
}
catch {
$output = $localhost+","+$hostname+","+$port+","+$sock.Connected
$output
}
}
}
And I run this script on the 8 computer from computer1 using :
Invoke-Command -ComputerName computer1,computer2 -FilePath F:\scripts\port-test.ps1
On the first computer (computer1- the machine that I execute the script from ) I got an output but on the computer2 I got :
Cannot find path '\\computer1\txtfiles' because it does not exist.
+ CategoryInfo : ObjectNotFound: (\\computer1\txt
files:String) [Set-Location], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand
Why isn't Powershell seeing network share? How can I fix it?
Sounds like the double hop issue - http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx - basically you are remoting to one machine and then trying to access another machine. your kerberos token is seen as invalid as there's a machine in between the original and the destination.
What OS are you using (the source and the destination OS is relevant for CredSSP)? If it is Windows 2008 or Windows 7 all the way through and the issue is double hop you may be able to us CredSSP to avoid it - http://www.ravichaganti.com/blog/?p=1230
If it's not a problem with access control, then consider a similar problem I faced when copying files over servers with this error:
Cannot find path '\\computer1\d$\path' because it does not exist.
It works after adding Microsoft.PowerShell.Core\FileSystem:: in front of file name:
copy-item "Microsoft.PowerShell.Core\FileSystem::\\computer1\d$\path\installer.msi" "Microsoft.PowerShell.Core\FileSystem::\\computer2\d$\path\installer.msi"
Edit:
I was able to reproduce this and it can be the double-hop issue. I solved it as per the instructions here:
http://blogs.msdn.com/b/clustering/archive/2009/06/25/9803001.aspx
( or the link that Matt had given)
Make sure computer2 and other computers are able to see that share. If the other machines are not able to see the share in the first place, Powershell cannot do anything.
For a simple check do:
Invoke-Command -computer computer2 -script {dir \\computer1\txtfiles}