I need to upload a file to ftp and following is what I am trying
# Config
$Username = "XXX"
$Password = "XXX"
$LocalFile = "E:\shell\File1.xml"
$RemoteFile = "ftp://XXX/Folder1/File1.xml"
# Create a FTPWebRequest
$FTPRequest = [System.Net.FtpWebRequest]::Create($RemoteFile)
$FTPRequest.Credentials = New-Object System.Net.NetworkCredential($Username,$Password)
$FTPRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$FTPRequest.UseBinary = $true
$FTPRequest.KeepAlive = $false
# Send the ftp request
$FTPResponse = $FTPRequest.GetResponse()
# Get a download stream from the server response
$ResponseStream = $FTPResponse.GetResponseStream()
# Create the target file on the local system and the download buffer
$LocalFileFile = New-Object IO.FileStream ($LocalFile,[IO.FileMode]::Create)
[byte[]]$ReadBuffer = New-Object byte[] 1024
# Loop through the download
do {
$ReadLength = $ResponseStream.Read($ReadBuffer,0,1024)
$LocalFileFile.Write($ReadBuffer,0,$ReadLength)
}
while ($ReadLength -ne 0)
Now I am getting an error stating
Exception calling "GetResponse" with "0" argument(s): "The remote server returned an error: (550) File unavailable (e.g., file not found, no access).
I have not created any file in ftp by default. I want teh script to create a new file from the local file contents.
What am I doing wrong ?
Related
I wrote a PowerShell script to download files using FTPto my local machine.
After the file is downloaded, I want to delete it from the FTP server. I wrote this code too. But unfortunately it's not working.
Can anyone help me to point out what is wrong with my code? Any clues will be helpful ...
Here is my code
function Delete-File($Source,$Target,$UserName,$Password)
{
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Source)
{
"ABCDEF File exists on ftp server."
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse()
"ABCDEF File deleted."
}
}
function Get-FTPFile ($Source,$Target,$UserName,$Password)
{
# Create a FTPWebRequest object to handle the connection to the ftp server
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
# set the request's network credentials for an authenticated connection
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($username,$password)
if(Test-Path $targetpath)
{
"ABCDEF File exists"
}
else
{
"ABCDEF File downloaded"
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$ftprequest.UseBinary = $true
$ftprequest.KeepAlive = $false
Delete-File $sourceuri $targetpath $user $pass
}
# send the ftp request to the server
$ftpresponse = $ftprequest.GetResponse()
# get a download stream from the server response
$responsestream = $ftpresponse.GetResponseStream()
# create the target file on the local system and the download buffer
$targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create)
[byte[]]$readbuffer = New-Object byte[] 1024
# loop through the download stream and send the data to the target
file
do{
$readlength = $responsestream.Read($readbuffer,0,1024)
$targetfile.Write($readbuffer,0,$readlength)
}
while ($readlength -ne 0)
$targetfile.close()
}
$sourceuri = "ftp://ftpxyz.com/vit/ABCDEF.XML"
$targetpath = "C:\Temp\M\NEWFOLDER\ABCDEF.XML"
$user = "*******"
$pass = "*******"
Get-FTPFile $sourceuri $targetpath $user $pass
Delete-File $sourceuri $targetpath $user $pass
Every time I execute this script, the only statement I get
ABCDEF file downloaded
or
ABCDEF file exists
I guess Delete-File is not executing at all... any type of clue will be helpful.
You cannot use Test-Path with an FTP URL. So your code for deleting the file will never execute.
Just remove the Test-Path condition and try to delete the file unconditionally. Then check for error and treat "file not exist" error as you like.
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($UserName, $Password)
try
{
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse() | Out-Null
Write-Host ("File {0} deleted." -f $Source)
}
catch
{
if ($_.Exception.InnerException.Response.StatusCode -eq 550)
{
Write-Host ("File {0} does not exist." -f $Source)
}
else
{
Write-Host $_.Exception.Message
}
}
Though as you try to delete the file only after you successfully download it, it's actually unlikely that the file won't exist.
So you may consider to give up on any specific error handling.
I ran your script locally to try it out and found a few issues. I refactored also a few things just to make it a bit more readable (at least in my opinion :) ).
Issues
Line 13. $Source parameter there is a ftp://... path. Test-Path will always return $false here and the delete request will never be executed.
In Get-FTPFile you were not referencing the input parameter of the function, instead the variables defined outside of it. I don't know if this was just a copy & paste bug or on purpose. In my opinion you should use the parameters you sent to the function. Lines 38, 39 and 50 at least in my code below.
Code
function Delete-File
{
param(
[string]$Source,
[string]$Target,
[string]$UserName,
[string]$Password
)
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Source)
{
"ABCDEF File exists on ftp server."
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse()
"ABCDEF File deleted."
}
}
function Get-FTPFile
{
param(
[string]$Source,
[string]$Target,
[string]$UserName,
[string]$Password
)
# Create a FTPWebRequest object to handle the connection to the ftp server
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
# set the request's network credentials for an authenticated connection
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Target)
{
"ABCDEF File exists"
}
else
{
"ABCDEF File downloaded"
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$ftprequest.UseBinary = $true
$ftprequest.KeepAlive = $false
Delete-File $Source $Target $UserName $Password
}
# send the ftp request to the server
$ftpresponse = $ftprequest.GetResponse()
# get a download stream from the server response
$responsestream = $ftpresponse.GetResponseStream()
# create the target file on the local system and the download buffer
$targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create)
[byte[]]$readbuffer = New-Object byte[] 1024
# loop through the download stream and send the data to the target
file
do{
$readlength = $responsestream.Read($readbuffer,0,1024)
$targetfile.Write($readbuffer,0,$readlength)
}
while ($readlength -ne 0)
$targetfile.close()
}
$sourceuri = "ftp://ftpxyz.com/vit/ABCDEF.XML"
$targetpath = "C:\Temp\M\NEWFOLDER\ABCDEF.XML"
$user = "*******"
$pass = "*******"
Get-FTPFile $sourceuri $targetpath $user $pass
#Delete-File $sourceuri $targetpath $user $pass
There are also ready made PowerShell cmdlets for talking to FTP/SFTP, no need to create everything from scratch, unless you are required to.
Anyway, for reference, check out e.g.
https://www.powershellgallery.com/packages/PSFTP
https://www.powershellgallery.com/packages/WinSCP
I am using WinSCP to download a file from SFTP and this is my code.
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = ConfigurationManager.AppSettings["SFTPDomain"],
UserName = ConfigurationManager.AppSettings["SFTPUser"],
Password = ConfigurationManager.AppSettings["SFTPPass"],
GiveUpSecurityAndAcceptAnySshHostKey = true,
PortNumber = 22
};
using (Session session = new Session())
{
//Attempts to connect to your SFtp site
session.Open(sessionOptions);
//Get SFtp File
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary; //The Transfer Mode - Automatic, Binary, or Ascii
transferOptions.FilePermissions = null; //Permissions applied to remote files;
transferOptions.PreserveTimestamp = false; //Set last write time of destination file
//to that of source file - basically change the timestamp to match destination and source files.
transferOptions.ResumeSupport.State = TransferResumeSupportState.Off;
//SFTP File Path
Sftpserver = ConfigurationManager.AppSettings["SFTPFileName"].ToString();
//Delete File if Exist
if (System.IO.File.Exists(FilePath))
{
System.IO.File.Delete(FilePath);
}
//the parameter list is: remote Path, Local Path with filename
TransferOperationResult transferOperationResult = session.GetFiles("p", FilePath, false, transferOptions);
//Throw on any error
transferOperationResult.Check();
}
How can I check the errors. Here they have defined the error codes but how can I implement in my code to check if the password is wrong or file does not exit.
The WinSCP .NET assembly API does not provide error code. Note that WinSCP supports range of protocols, including FTP, SFTP, SCP and WebDAV. So there's no single set of codes to check. Each protocol have different codes. In addition there are errors coming from SSH protocol (what would be a case of wrong password), which are different from SFTP/SCP errors set. Then you have a different set of WinAPI codes for errors when accessing local files.
Task: I need to loop thru all files on Sharepoint site and download them to local folder.
Script:
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$s = Get-SPSite “https://abc.abctools.consumer.abc.net/sites/rtc/report/SitePages/Forms/AllPages.aspx”
$files = $s.RootWeb.GetFolder("Shared Documents").Files
foreach ($file in $files) {
Write-host $file.Name
$b = $file.OpenBinary()
$fs = New-Object System.IO.FileStream(("C:\SP Document Library files\"+$file.Name), [System.IO.FileMode]::Create)
$bw = New-Object System.IO.BinaryWriter($fs)
$bw.Write($b)
$bw.Close()
}
Errors: I get when i try to run/execute above script.
1. "You cannot call a method on a null-valued expression."
New-Object: Exception calling ".ctor" with "2" agrument(s): "Could not find a part of the path 'C:\SP Document Library files\'
New-Object: Constructor not found. Cannot find an appropriate constructor for the type system.IO.BinaryWrite.
The term 'Get-SPSite' is not recognized as a cmdlet, function, operable program or script file. verify the term and try again.
Response on Error #2: I have created the folder & named "SP Document Library files" so that path is correct C:\SP Document Library files not sure why i see that msg.
Library files (.csv,.xls) exists in a folder.
Folder name : 2014-01-31.
1. What to do to in order resolve above error message(s).
2. I'm not sure if i need to use whole sharepoint url or part of it.Educate me on that.
Thanks!!
Try by giving ReadWrite FileAccess.
And you can get the root web directly if you know the Url instead of using SPSite.
Here's my script I use and has always worked
$siteUrl = '“https://abc.abctools.consumer.abc.net/sites/rtc”'
$listUrl = '“https://abc.abctools.consumer.abc.net/sites/rtc/Shared Documents”'
$folderPath = 'C:\\....'
$web = Get-SPWeb -Identity $siteUrl
$list = $web.GetList($listUrl)
$items = $list.Items
ForEach ($item in $items)
{
$binary = $item.File.OpenBinary();
$folderPathToSave = $folderPath + "\\" + $item.Name;
if ($binary -ne $null)
{
$stream = New-Object System.IO.FileStream($folderPathToSave,[System.IO.FileMode]::Create,[System.IO.FileAccess]::ReadWrite);
$writer = New-Object System.IO.BinaryWriter($stream);
$writer.Write($binary);
$writer.Close();
}
}
$web.Dispose()
The original post:
http://naimmurati.wordpress.com/2012/06/07/backup-documents-from-document-library-with-powershell-script/
I'm executing a process remotely via WMI (Win32_Process Create) but am unable to figure out how I can determine when the process has completed executing. When I first issue the command, there is an exit code (0 for success) but that just tells me the process has been successfully spawned.
Is there a way I can know when the process ends? Thanks!
Faced same issue and wrote a simple VMI wrapper:
var exitStatus = WmiOperations.Run("notepad.exe", wait:10);
Synopsis for Run is:
int Run(string command, // Required
string commandline = null, // (default=none)
string machine = null, // (default=local)
string domain = null, // (default=current user domain)
string username = null, // (default=current user login)
string password = null, // (default=current user password)
SecureString securePassword = null, // (default=current user password)
double wait = double.PositiveInfinity); // (default=wait til command ends);
Source code can be downloaded from here.
Give caesar his due, code is inspired from this one. Simply:
Refactored things to static class
Added more control on remoting parameters
Redesigned event watcher to suppress the unappealing CheckProcess test
Here is an example create on the top of .NET objects but written in Powershell, it's easy to translate it to C#
Clear-Host
# Authentication object
$ConOptions = New-Object System.Management.ConnectionOptions
$ConOptions.Username = "socite\administrateur"
$ConOptions.Password = "adm"
$ConOptions.EnablePrivileges = $true
$ConOptions.Impersonation = "Impersonate"
$ConOptions.Authentication = "Default"
$scope = New-Object System.Management.ManagementScope("\\192.168.183.220\root\cimV2", $ConOptions)
$ObjectGetOptions = New-Object System.Management.ObjectGetOptions($null, [System.TimeSpan]::MaxValue, $true)
# Equivalent to local :
# $proc = [wmiclass]"\\.\ROOT\CIMV2:Win32_Process"
$proc = New-Object System.Management.ManagementClass($scope, "\\192.168.183.220\ROOT\CIMV2:Win32_Process", $ObjectGetOptions)
# Now create the process remotly
$res = $proc.Create("cmd.exe")
# Now create an event to detect remote death
$timespan = New-Object System.TimeSpan(0, 0, 1)
$querryString = "SELECT * From WIN32_ProcessStopTrace WHERE ProcessID=$($res.ProcessID)"
$query = New-Object System.Management.WQLEventQuery ($querryString)
$watcher = New-Object System.Management.ManagementEventWatcher($scope, $query)
$b = $watcher.WaitForNextEvent()
$b
I am trying to create a Windows Application that will be able to run a variety of Powershell scripts.
I have a script which works as it should (when run from the Powershell prompt), and my Windows Application seems to execute it like it should, but it is unable to find the methods on my OU.
When I execute the script from the Windows Application, I get these messages out:
ERROR: The following exception occurred while retrieving member "Create": "There
is no such object on the server.
"
ERROR: The following exception occurred while retrieving member "Delete": "There
is no such object on the server."
Powershell script:
function New-AdUser {
param (
[string] $Username = $(throw "Parameter -Username [System.String] is required."),
[string] $Password = $(throw "Parameter -Password [System.String] is required."),
[string] $OrganizationalUnit = "Users",
[string] $DisplayName,
[string] $FirstName,
[string] $LastName,
[string] $Initials,
[string] $MobilePhone,
[string] $Description,
[switch] $CannotChangePassword,
[switch] $PasswordNeverExpires,
[switch] $Disabled
)
try {
$currentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$dn = $currentDomain.GetDirectoryEntry().distinguishedName
$ou = [ADSI] "LDAP://CN=$OrganizationalUnit,$dn"
$userAccount = $ou.Create("user", "cn=$Username")
$userAccount.SetInfo()
$userAccount.userAccountControl = ($userAccount.userAccountControl.Item(0) -bxor 0x0002) #Enable the account
$userAccount.SetInfo()
$userAccount.sAMAccountName = $Username
$userAccount.SetInfo()
$userAccount.userPrincipalName = ("{0}#{1}" -f $Username, $currentDomain.Name)
if ($DisplayName) {
$userAccount.displayName = $DisplayName
}
if ($Description) {
$userAccount.description = $Description
}
if ($FirstName) {
$userAccount.givenName = $FirstName
}
if ($LastName) {
$userAccount.SN = $LastName
}
if ($Initials) {
$userAccount.initials = $Initials
}
if ($MobilePhone) {
$userAccount.mobile = $MobilePhone
}
$userAccount.SetInfo()
$userAccount.SetPassword($Password)
# Password
if ($PasswordNeverExpires) {
$userAccount.userAccountControl = ($userAccount.userAccountControl.Item(0) -bxor 0x10000)
}
if ($CannotChangePassword) {
$everyOne = [System.Security.Principal.SecurityIdentifier]'S-1-1-0'
$EveryoneDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($Everyone,'ExtendedRight','Deny', [System.Guid]'ab721a53-1e2f-11d0-9819-00aa0040529b')
$self = [System.Security.Principal.SecurityIdentifier]'S-1-5-10'
$SelfDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule ($self,'ExtendedRight','Deny', [System.Guid]'ab721a53-1e2f-11d0-9819-00aa0040529b')
$userAccount.get_ObjectSecurity().AddAccessRule($selfDeny)
$userAccount.get_ObjectSecurity().AddAccessRule($EveryoneDeny)
$userAccount.CommitChanges()
}
$userAccount.SetInfo()
if ($Disabled) {
$userAccount.userAccountControl = ($userAccount.userAccountControl.Item(0) -bxor 0x0002)
}
$userAccount.SetInfo()
} catch {
Write-Error $_
$ou.Delete("user", "cn=$Username")
return $false
}
return $true
}
The C# code I have is this:
PowerShell ps = PowerShell.Create();
ps.AddScript(GetScript("New-AdUser.ps1"));
ps.Invoke();
ps.AddCommand("New-AdUser").AddParameters(
new List<CommandParameter>() {
new CommandParameter("Username", username),
new CommandParameter("Password", password),
new CommandParameter("FirstName", firstName),
new CommandParameter("LastName", lastName),
new CommandParameter("DisplayName", realName),
new CommandParameter("Initials", initials),
new CommandParameter("MobilePhone", mobilePhone),
new CommandParameter("OrganizationalUnit", "Users"),
new CommandParameter("PasswordNeverExpires")
}
);
var results = ps.Invoke();
foreach (var obj in results)
Console.WriteLine(obj.ToString());
if (ps.Streams.Error.Count > 0)
{
foreach (var err in ps.Streams.Error)
Console.WriteLine("ERROR: {0}", err.ToString());
}
Seems that you are just creating a user in AD. By having the c# code calling a powershell script, you are adding another moving part in your script. Why not call it directly in C# code. Check this MSDN article.
The problem appears to be that the Create method on your ADSI object, $ou, doesn't exist. I would check that it is getting created properly. Run the script outside your application to ensure that it works, or have an extra line that displays its members:
$ou | Get-Member
It almost appears as though the Runspace in the application is being created with a restrictive RunspaceConfiguration, so it can't find System.DirectoryServices for the AD functionality you need.
What do you get when you run the following within in your application?
string script = #"[AppDomain]::CurrentDomain.GetAssemblies()";
PowerShell ps = new PowerShell();
ps.AddScript(script);
var output = ps.Invoke();
foreach (var a in output.Select(pso => (System.Reflection.Assembly)pso.BaseObject))
Console.WriteLine("Assembly: " + a.FullName);
When I run that under the debugger in a plain console application I get 28 assemblies (19 outside the debugger), including System.DirectoryServices. The [AppDomain]::CurrentDomain.GetAssemblies() bit shows 16 when I run it on a vanilla command prompt. System.DirectoryServices shows up in all three lists.
When run from within C# I found that I need to add the PowerShell snap-in "Microsoft.Windows.AD" before being able to run the cmdlet's it provides.