The problem:
A client requires that we upload extracted data from our system to their box.com platform, rather than our normal SFTP utility. I have box.com credentials, and am aware they require FTPS not SFTP, and require passive mode. I've cribbed a fragment from ThomasMaurer's Powershell FTP Upload and Download script. Powershell version on my server is 4.0
Code fragment is:
#config
$Username = "username#host.com"
$Password = "redactedpassword"
$LocalFile = "C:\path\to\my\file.csv"
$RemoteFile = "ftp://ftp.box.com:990/file.csv"
#Create FTPWebRequest
$FTPRequest = [System.Net.FtpWebRequest]::Create($RemoteFile)
$FTPRequest = [System.Net.FtpWebRequest]$FTPRequest
$FTPRequest.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$FTPRequest.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
$FTPRequest.UseBinary = $true
$FTPRequest.UsePassive = $true
#read file for upload
$FileContent = gc -en byte $LocalFile
$FTPRequest.ContentLength = $FileContent.Length
#get stream request by bytes
$run = $FTPRequest.GetRequestStream()
$run.Write($FileContent,0,$FileContent.Length)
#cleanup
$run.Close()
$run.Dispose()
The error(s):
Exception calling "GetRequestStream" with "0" argument(s): "System error." At C:\path\to\my\powershellscript.ps1:28 char:1
+ $Run = $FTPRequest.GetRequestStream()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: () [], MethodInvocationException
+ FullyQualifiedErrorId: WebException
I also get downstream errors on calling the $FileContent.Length property and $run.close and $run.dispose().
Has anyone successfully automated to box (specifically) or to a passive implicit-ssl using only PowerShell 4.0 commands, and do you have a solid pattern I could reuse? Many thanks
I'm uploading files with a derived version of System.Net.WebClient, which supports FTP over TLS. This can easily be achieved by embedding C# code in PowerShell:
$typeDefinition = #"
using System;
using System.Net;
public class FtpClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
FtpWebRequest ftpWebRequest = base.GetWebRequest(address) as FtpWebRequest;
ftpWebRequest.EnableSsl = true;
return ftpWebRequest;
}
}
"#
Add-Type -TypeDefinition $typeDefinition
$ftpClient = New-Object FtpClient
$ftpClient.UploadFile("ftp://your-ftp-server/yourfile.name", "STOR", "C:\YourLocalFile.name")
The answer by #h0r41i0 solves the problem by using WebClient. But as the WebClient internally uses (Ftp)WebRequest, it cannot be the solution on its own.
I'll assume that the "System error" occurs because either OP is trying to connect to a secure port (990) with an insecure connection.
Or because the file is too large and the OP code tries to read it whole to memory:
$FileContent = gc -en byte $LocalFile
In either case, there's no reason to give up on FtpWebRequest. Just use a secure connection (FtpWebRequest.EnableSsl). And an efficient way to feed the data from the file to the FTP stream, for example Stream.CopyTo:
$request = [Net.WebRequest]::Create("ftp://ftp.example.com/remote/path/file.zip")
$request.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$request.EnableSsl = $True
$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()
$fileStream.CopyTo($ftpStream)
$ftpStream.Dispose()
$fileStream.Dispose()
For other options, see Upload files with FTP using PowerShell.
Though note that .NET framework does not support implicit TLS (what is typical use of 990). Only explicit TLS. But support for the explicit TLS is more common ayway. See Does .NET FtpWebRequest Support both Implicit (FTPS) and explicit (FTPES)?
Probably too late to be useful to original questioner, but I found this other answer did the trick for me: Cyril Gupta's answer to Upload files with ftp using powershell
Here is my revised edition, including URL encoding (since the box.com usernames are email addresses which include the "at sign"):
## https://stackoverflow.com/a/2485696/537243
## User comment complains can't turn off passive mode,
## but that is exactly what we want here!
[Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
# config
$Username = "foo#bar.com"
$Password = "s3cr3tpAssw0rd"
$Servername = "ftp.box.com"
# This is what we need URI it to look like:
# ftp://foo%40bar.com:s3cr3tpAssw0rd#ftp.box.com/
$baseURI = "ftp://$([System.Web.HttpUtility]::UrlEncode($Username)):$([System.Web.HttpUtility]::UrlEncode($Password))#$($Servername)"
$LocalFile = "C:\tmp\to_upload\data.csv"
$RemoteFile = "date.csv"
$ftpURI = "$($baseURI)/$($RemoteFile)"
Write-output "ftp uri: $($ftpURI)";
$webclient = New-Object -TypeName System.Net.WebClient;
$ftpURI = New-Object -TypeName System.Uri -ArgumentList $ftpURI; #"convert" it
$webclient.UploadFile($ftpURI, $LocalFile);
Write-output "Uploaded $($LocalFile) ... "; # of course since we didn't use try/catch or other error dectection this is a bit presuming.
Also should note this example uses plain FTP, not FTPS or SFTP.
Related
I am trying to append data to a CSV file on an FTP server using PowerShell.
Currently my script works fine, however it is overwriting the data.
Here is the main bit of my code:
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$UserInfo = [pscustomobject]#{
UserName = $env:UserName
ComputerName = $env:computername
}
$contents =
(($UserInfo | ConvertTo-Csv -NoTypeInformation) -join [Environment]::NewLine) +
[Environment]::NewLine
$webclient.UploadString(($ftp + "/Installed.csv"), $contents)
Ideally, I'd like to do this without any native plugins or libraries. The only other thing I can think of is using something like get-content to get the current data from the file, and then the new data would be
getcontent myfile.csv + the new data from $UserInfo
Basically I am installing a VPN through group policy and would like to log who successfully installed the client. We don't have any software distribution platforms unfortunately so we're trying to use GP and FTP to perform this task.
Any suggestions are appreciated. Thank you in advance.
It's not possible with WebClient. But it can be done with FtpWebRequest and WebRequestMethods.Ftp.AppendFile.
$request = [System.Net.WebRequest]::Create("ftp://example.com/remote/path/file.txt")
$request.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::AppendFile
$ftpStream = $request.GetRequestStream()
$writer = New-Object System.IO.StreamWriter($ftpStream)
$writer.Write($contents)
$writer.Close()
$ftpStream.Dispose()
I am working on a FTP automation script that will upload certain files from a network share to a specific location on a FTP server. I found the below, but am unable to edit it to navigate to the desired destination directory.
#ftp server
$ftp = "ftp://SERVER/OtherUser/"
$user = "MyUser"
$pass = "MyPass"
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
#list every sql server trace file
foreach($item in (dir $Dir "*.trc")){
"Uploading $item..."
$uri = New-Object System.Uri($ftp+$item.Name)
$webclient.UploadFile($uri, $item.FullName)
}
I have credentials to the FTP server but am defaulted to /home/MyUser/ and I need to direct to /home/OtherUser/. I do have permissions to browse to and upload to that directory, but I can't figure out how to, essentially, cd to that location.
Here is the current error received:
Exception calling "UploadFile" with "2" argument(s): "The remote server returned an erro
r: (550) File unavailable (e.g., file not found, no access)."
At line:26 char:26
+ $webclient.UploadFile <<<< ($uri, $item.FullName)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
You need to use the FtpWebRequest type. The WebClient is used for HTTP traffic.
I have written and tested a parameterized function that will upload a file to a FTP server, called Send-FtpFile. I used the sample C# code from MSDN to translate this into PowerShell code, and it works quite well.
function Send-FtpFile {
[CmdletBinding()]
param (
[ValidateScript({ Test-Path -Path $_; })]
[string] $Path
, [string] $Destination
, [string] $Username
, [string] $Password
)
$Credential = New-Object -TypeName System.Net.NetworkCredential -ArgumentList $Username,$Password;
# Create the FTP request and upload the file
$FtpRequest = [System.Net.FtpWebRequest][System.Net.WebRequest]::Create($Destination);
$FtpRequest.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile;
$FtpRequest.Credentials = $Credential;
# Get the request stream, and write the file bytes to the stream
$RequestStream = $FtpRequest.GetRequestStream();
Get-Content -Path $Path -Encoding Byte | % { $RequestStream.WriteByte($_); };
$RequestStream.Close();
# Get the FTP response
[System.Net.FtpWebResponse]$FtpRequest.GetResponse();
}
Send-FtpFile -Path 'C:\Users\Trevor\Downloads\asdf.jpg' `
-Destination 'ftp://google.com/folder/asdf.jpg' `
-Username MyUsername -Password MyPassword;
I have searched and read a lot about downloading SSL FTP files in PowerShell... But how do I upload them? I have a filename I need to upload each day and I get errors that I am not logged in.
I'm currently uploading to other FTP sites like this:
$Filename = "myfile.txt"
$FullPath = "\\server\share\$Filename"
$ftp = "ftp://user:pass#ftp.domain.com/$Filename"
$ftpWebClient = New-Object System.Net.WebClient
$ULuri= New-Object System.URI($ftp)
$ftpWebClient.UploadFile($ULuri, $FullPath)
Do I need to create a whole new block of code for the SSL FTP upload, or do I just need to make minor adjustments to this code?
Thanks!
I use the WinSCP DLL to do this stuff. Check out some examples here:
http://winscp.net/eng/docs/library#powershell
Here's some of my sample code.
[Reflection.Assembly]::LoadFrom("D:\WinSCP.dll") | Out-Null
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
$sessionOptions.HostName = "192.168.1.120"
$sessionOptions.UserName = "user"
$sessionOptions.Password = "pass"
$session = New-Object WinSCP.Session
$session.Open($sessionOptions)
#upload stuff here, check the link for detail on how, and use powershell to populate your file list!
$session.Dispose()
As you are already using the WebClient class from the .NET framework in your script, I suggest you create a derived version from WebClient that supports FTP over TLS, by embedding C# code in PowerShell:
$typeDefinition = #"
using System;
using System.Net;
public class FtpClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
FtpWebRequest ftpWebRequest = base.GetWebRequest(address) as FtpWebRequest;
ftpWebRequest.EnableSsl = true;
return ftpWebRequest;
}
}
"#
Add-Type -TypeDefinition $typeDefinition
$ftpClient = New-Object FtpClient
$ftpClient.UploadFile("ftp://your-ftp-server/yourfile.name", "C:\YourLocalFile.name")
I'm using a Powershell script to automate sending a .txt to an FTP site. When I execute it in powershell, nothing happens. The root\prompt just appears...no messages that it was successful. How do I tell if it worked? Here is my script in case it helps.
$localfile = "D:\Export\TESTING.txt"
$remotefile = "/TESTING.txt"
$ftphost = "ftp://ftp.site.com"
$URI = $ftphost + $remotefile
$username="USERNAME"
$password="1234"
function Get-FTPFile
($URI,$localfile,$username,$password){
$credentials=New-Object System.Net.NetworkCredential
($username,$password)
$ftp=[System.Net.FtpWebRequest]::Create($URI)
$ftp.Credentials=$credentials
$ftp.UseBinary=1
$ftp.KeepAlive=0
$response=$ftp.GetResponse()
$responseStream = $response.GetResponseStream()
$file = New-Object
IO.FileStream ($localfile,[IO.FileMode]::Create)
[byte[]]$buffer = New-Object byte[] 1024
$read = 0
do{
$read=$responseStream.Read($buffer,0,1024)
$file.Write($buffer,0,$read)
}
while ($read -ne 0)$file.close()
}
Use WebRequestMethods.Ftp.GetFileSize following completion of the upload to confirm that the uploaded file size matches the local file size.
You could also use a try/catch block to check for exceptions during your read/write operations. A lack of exceptions would give you some confidence that the upload was successful (i.e. no news is good news).
All I need is get email in PowerShell Script and see at its topic - with pop3 or imap, doesnt matter.
I tried to find solution, but all I found is either 3rd party .net assebmlies, or MS Exchange direct work. Both are not appropriate.
How to use SMTP and send email - its absolutely clear, but how to receive? Isn't there any standard assemblies similar to System.Net.Mail?
Here is a code I have been using on c#. I have Imported the dll to powershell and used it to retrieve different parts of a message. The dll I used is Imapx2 which is an open source. I understand that you don't want to use a third party .NET assemblies but this might help other people trying to reach to this content.
### Import the dll
[Reflection.Assembly]::LoadFile(“YourDirectory\imapx.dll”)
### Create a client object
$client = New-Object ImapX.ImapClient
###set the fetching mode to retrieve the part of message you want to retrieve,
###the less the better
$client.Behavior.MessageFetchMode = "Full"
$client.Host = "imap.gmail.com"
$client.Port = 993
$client.UseSsl = $true
$client.Connect()
$user = "User"
$password = "Password"
$client.Login($user,$password)
$messages = $client.Folders.Inbox.Search("ALL", $client.Behavior.MessageFetchMode, 1000)
foreach($m in $messages){
$m.Subject
foreach($r in $m.Attachments){
$r | Out-File "Directory"
}
}
I hope this was helpful
I used the suggestion of Falah Abu Hassan and it worked very well for my requirements for receiving mails via IMAP!
How to get the IMAPX.DLL
The Github Repository for imapx is found here:
https://github.com/azanov/imapx
Unfortunably you have to compile it yourself with "Visual Studio" to get the imapx.dll.
Creation of an sample Powershell Script
The Script and the DLL should be placed side and can integrated with this:
$path = Split-path $script:MyInvocation.MyCommand.Path
[Reflection.Assembly]::LoadFile(“$path\imapx.dll”)
The following example script, inspired by the answer from Falah Abu Hassan worked very well for me:
$path = Split-path $script:MyInvocation.MyCommand.Path
[Reflection.Assembly]::LoadFile(“$path\imapx.dll”)
### Create a client object
$client = New-Object ImapX.ImapClient
$client.Behavior.MessageFetchMode = "Full"
$client.Host = "Servername"
$client.Port = 993
$client.UseSsl = $true
$client.IsDebug = $true
$client.ValidateServerCertificate = $true
$client.Connect()
$user = "login#domain"
$pass = 'password'
$client.Login($user, $pass)
$messages = $client.Folders.Inbox.Search("ALL", $client.Behavior.MessageFetchMode, 100)
write-host "Count found: $($messages.count)"
foreach($m in $messages){
write-host "Processing Subject: $($m.Subject)"
}