Use SSIS with WinSCP to Download Files Via SFTP

Creating a script tasks to handle SFTP processing, using WinSCP, can be intimidating. However, once you understand how the pieces fit together, the coding is quite simple. In this post, I will show you how to create an SSIS script task for downloading a file, via SFTP. We will be setting up our environment before creating the SSIS project. We will be completing four different tasks in this tutorial.

1. Creating directories that will be used by our SSIS script task.

2. Downloading and installing WinSCP to obtain the necessary files.

3. Download a local SFTP server for developing and testing, not needed if you already have a Development environment and credentials.

4. Create the SSIS project and script task that will implement the WinSCP.net assembly.

Task 1 - Directory Creation

Create the following directories.

C:\csharp_lib\WinSCP.Net - The name is arbitrary, but you should use the provided directory name and structure for this tutorial. Feel free to change the naming structure to your preference once you have a working proof of concept.

Note: The directory structure will need to exist, in the same location, in all environments where you plan to deploy the project (i.e. locally, Development server, QA Server, Production server.  The directory structure and name must be consistent across environments.)

WinSCP .exe and dll versions should be kept consistent across devices.  This will make sure that any errors are not directly related to version issues.

C:\Import - The name is arbitrary, but required for use with this tutorial. This directory will serve as a staging area for the files imported using SFTP.

C:\Export - The name is arbitrary, but required for use with the next tutorial, demonstrating how to upload a file. This directory will serve as a staging area for the files we will export using SFTP.

C:\LogFiles-The name is arbitrary, but required for use with this tutorial. This directory will store the log files generated by our SSIS script task

Task  2 - WinSCP Files

The WinSCP executable and DLL are obtained by downloading and installing WinSCP.  You can download the installation file by going to https://winscp.net/

Here is a brief tutorial, if needed, https://www.youtube.com/watch?v=58KmUBaEW34.

Once installation is complete, navigate to your C:\Program Files (x86)\WinSCP directory and copy the following files:

• WinSCP.exe

• WinSCPnet.dll

Navigate to C:\csharp_lib\WinSCP.Net and paste the files you just copied.

Task 3 - Development SFTP Server

If you do not have access to a Development SFTP server, you can obtain a local SFTP server for testing purposes,  Rebex Tiny SFTP Server: https://www.rebex.net/tiny-sftp-server/

Simply download and unpack the zip file to a directory of your choice. Free for commercial and non-commercial use.  It is not recommended for Production level use.

Double click on the Rebex Tiny Sftp Server application file. Click on the start button in the application window to start the local SFTP server.

You now have a local development SFTP server.

Task  4 - SSIS Package Development

Create the following package level variables.

Package Level Variables

Name                                 Scope       Data Type        Value

SFTP_FileName                 Package       String         testfile.txt

SFTP_FingerPrint                 Package       String         See below

SFTP_FullFileImportPath Package       String         See below

SFTP_ImportPath                 Package       String         /

SFTP_LogFileName         Package       String         See below

SFTP_Password                 Package       String         password

SFTP_PortNumber                 Package        Int                 22

SFTP_Server                         Package       String         See below

SFTP_User                         Package       String         tester

vFileExistsFlag                 Package         Int                 0

vLocalFilePath                 Package       String         c:\Import\

1. SFTP_FileName – Name of the file we will be importing from the SFTP server.

2. SFTP_FingerPrint – You can obtain the SshHostKeyFingerprint value by using WinSCP to generate a code template providing you with the key. To learn how.

3. SFTP_FullFileImportPath – Combination of two package variables. @[User::SFTP_ImportPath]+ @[User::SFTP_FileName]

4. SFTP_ImportPath – Path to file on remote server.

5. SFTP_LogFileName – Dynamically created file name using an expression. There are many date expressoin examples for SSIS on the web. This link can provide some detail on the expression. The value will be c:\LogFiles\SFTP_Import_MMDDYY_log.txt, which is generated by using the following expression ( copy all of the text including double quotes):

"c:\\LogFiles\\SFTP_Import_"+(LEN((DT_STR,2,1252)(DATEPART("MM",GETDATE()))) == 2 ? (DT_STR,2,1252)(DATEPART("MM",GETDATE())) : "0" + (DT_STR,2,1252)(DATEPART("MM",GETDATE()))) +(LEN((DT_STR,2,1252)(DATEPART("dd",GETDATE()))) == 2 ? (DT_STR,2,1252)(DATEPART("dd",GETDATE())) : "0" + (DT_STR,2,1252)(DATEPART("dd",GETDATE()))) +(DT_STR,4,1252)(DATEPART("yyyy",GETDATE())%100)+"_log.txt"

6. SFTP_Password – “password”, the password is fixed and defined by Rebex Tiny SFTP server change if you are using your own development server.

7. SFTP_PortNumber– 22, standard for SFTP connections.

8. SFTP_Server – Defined by Rebex Tiny SFTP Server when the server is started. Your value will differ from mine.

9. SFTP_User  – “Tester”, this user name is fixed and defined by Rebex Tiny SFTP server.

10. vFileExistsFlag – 0, value will be updated by SSIS script task,  at runtime,  if the file exists.

11.    vLocalFilePath - c:\Import\, this is the directory that will serve as a staging area for the imported file.

SFTP credentials are displayed when Rebex Tiny SFTP Server is started.

Package Development

Step 1

Create an SSIS project and package.

Step 2

Drag a script task onto the canvass.

Step 3

Double click on the script task and ensure C# is the selected language.

Step 4

Mark the following variables as ReadOnly Variables.

• User::SFTP_FingerPrint

• User::SFTP_FullFileImportPath

• User::SFTP_LogFileName

• User::SFTP_Password

• User::SFTP_PortNumber

• User::SFTP_Server

• User::SFTP_User

• User::vLocalFilePath

Your script task editor window should resemble the following image.

Step 5

Mark the following variables as ReadWrite  variables.

• User::vFileExistsFlag

Your script task editor window should resemble the following image.

Step 6

Click on the “Edit Script” button to open up the script task editor. We will need to add a reference to the WinSCP.net Assembly in the next step.  We will also add WinSCP to the list of namespaces.

Step 7

Add a reference to the WinSCP.net assembly.

• Right click on References and select the “Add Reference” menu item.

Click on the browse button and navigate to the directory the WinSCP.net assembly is stored.

Select the WinSCP dll file, and click the “Add” button.

Click on “Ok” on the next window.

Add WinSCP to the namespace. Remove the namespaces for System.Data and System.Windows.Forms.

Step 8 – Enter the script to call the WinSCP.net assembly.

Add the following script to create and entry point for  calling  the WinSCP.net assembly from a location outside of the Global Assembly Cache (GAC). Placement is just above public void Main(). The source of this code and more detailed information can be found here Assembly in SSIS outside of the GAC.

      static ScriptMain()

       {

           AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

       }

       static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)

       {

           if (args.Name.Contains("WinSCPnet"))

           {

               string path = @"C:\csharp_lib\WinSCP.Net\";

               return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "WinSCPnet.dll"));

           }

           return null;

       }

Step 9 – Populate values for SessionOptions class properties.

   SessionOptions sessionOptions = new SessionOptions

           {                

               Protocol = Protocol.Sftp,

               HostName = Dts.Variables["User::SFTP_Server"].Value.ToString(),

               PortNumber = (int)Dts.Variables["User::SFTP_PortNumber"].Value, //Requires an integer.

               UserName = Dts.Variables["User::SFTP_User"].Value.ToString(),

               Password = Dts.Variables["User::SFTP_Password"].Value.ToString(),

               SshHostKeyFingerprint = Dts.Variables["User::SFTP_FingerPrint"].Value.ToString(),

           };

Step 10 - Encapsulate the remaining code in a Try...Catch statement

try

           {

               using (Session session = new Session())

               {  

                   //Set absolute path for log file creation.

                   session.SessionLogPath = Dts.Variables["User::SFTP_LogFileName"].Value.ToString();

                   //Automatic connection and authentication of the session.

                   session.Open(sessionOptions);

                   //Absolute path to the file on the SFTP server.

                   string curFile = Dts.Variables["User::SFTP_FullFileImportPath"].Value.ToString();

                   if (session.FileExists(curFile))//FileExists?

                   {

                       //Default constructor.

                       TransferOptions transferOptions = new TransferOptions();

                       //Use binary mode to transfer all files unless you know the file contains only

                       // text, then you are able to use ASCII mode.

                       transferOptions.TransferMode = TransferMode.Binary;

                       //With SFTP protocol, WinSCP by default transfers files over 100 KB via a

                       //temporary file. It will fail if you do not have permissions to create a new

                       //file. Turning the option OFF avoids possible failure.

                       transferOptions.ResumeSupport.State = TransferResumeSupportState.Off;

                       //Get files from server

                       TransferOperationResult TransferResult = session.GetFiles(Dts.Variables

                       ["User::SFTP_FullFileImportPath"].Value.ToString(),

                       Dts.Variables["User::vLocalFilePath"].Value.ToString(), false, transferOptions);

       

                       // Checks if there is any failure

                       TransferResult.Check();

                       //Set File Exists variable value when file is found.

                       Dts.Variables["User::vFileExistsFlag"].Value = 1;                  

                       // Print results, displayed on Execution Results tab.

                       bool fireAgain = false;

                       foreach (TransferEventArgs transfer in TransferResult.Transfers)

                       {

                           Dts.Events.FireInformation(0, null,

                               string.Format("Download of {0} succeeded", transfer.FileName),

                               null, 0, ref fireAgain);

                       }

                   }

                   else

                   {

                       Dts.Variables["User::vFileExistsFlag"].Value = 0;//Value if file is not found.

                     

                   }

               }

               Dts.TaskResult = (int)ScriptResults.Success;

             

           }

           //Print Errors, displayed on Execution Results tab.

           catch (Exception e)

           {

               Dts.Events.FireError(0, null,

                   string.Format("Error when using WinSCP to download files: {0}", e),

                   null, 0);

               Dts.TaskResult = (int)DTSExecResult.Failure;

           }

   enum ScriptResults

       {

           Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,

           Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure

       };

 

Step 11 - Testing

Start Tiny SFTP server by double clicking on the Rebex Tiny SFTP Server executable file in the directory containing the application contents.

Click on the start button in the application window.

You should see a window resembling the following. Your server IP will differ.

Once the SFTP server is started, execute the package. You should now have testfile.txt in the C:\Import directory.

Navigate to C:\LogFiles. You should see a log file related to your WinSCP session.

Code in its Entirety

#region Namespaces

using System;

using Microsoft.SqlServer.Dts.Runtime;

using WinSCP;

#endregion

namespace ST_baea964635034bbf9735e63b0f979109

{

[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]

public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase

{

       static ScriptMain()

       {

           AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

       }

       static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)

       {

           if (args.Name.Contains("WinSCPnet"))

           {

               string path = @"C:\csharp_lib\WinSCP.Net\";

               return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "WinSCPnet.dll"));

           }

           return null;

       }

       public void Main()

{

         

           SessionOptions sessionOptions = new SessionOptions

           {                

               Protocol = Protocol.Sftp,

               HostName = Dts.Variables["User::SFTP_Server"].Value.ToString(),

               PortNumber = (int)Dts.Variables["User::SFTP_PortNumber"].Value,

               UserName = Dts.Variables["User::SFTP_User"].Value.ToString(),

               Password = Dts.Variables["User::SFTP_Password"].Value.ToString(),

               SshHostKeyFingerprint = Dts.Variables["User::SFTP_FingerPrint"].Value.ToString(),

           };

           try

           {

               using (Session session = new Session())

               {  

                   session.SessionLogPath = Dts.Variables["User::SFTP_LogFileName"].Value.ToString();

                   session.Open(sessionOptions);

                   string curFile = Dts.Variables["User::SFTP_FullFileImportPath"].Value.ToString();

                   if (session.FileExists(curFile))

                   {

                   

                       TransferOptions transferOptions = new TransferOptions();                      

                       transferOptions.TransferMode = TransferMode.Binary;

                       transferOptions.ResumeSupport.State = TransferResumeSupportState.Off;

                       TransferOperationResult TransferResult = session.GetFiles(Dts.Variables["User::SFTP_FullFileImportPath"].Value.ToString(), Dts.Variables["User::vLocalFilePath"].Value.ToString(), false, transferOptions);

                   

                       TransferResult.Check();

             

                       Dts.Variables["User::vFileExistsFlag"].Value = 1;              

           

                       bool fireAgain = false;

                       foreach (TransferEventArgs transfer in TransferResult.Transfers)

                       {

                           Dts.Events.FireInformation(0, null,

                               string.Format("Download of {0} succeeded", transfer.FileName),

                               null, 0, ref fireAgain);

                       }                    }

                   else

                   {

                       Dts.Variables["User::vFileExistsFlag"].Value = 0;      

                   }

               }

               Dts.TaskResult = (int)ScriptResults.Success;              

           }

           catch (Exception e)

           {

               Dts.Events.FireError(0, null,

                   string.Format("Error when using WinSCP to download files: {0}", e),

                   null, 0);

               Dts.TaskResult = (int)DTSExecResult.Failure;

           }

       }

       enum ScriptResults

       {

           Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,

           Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure

       };

 

}

}

I hope you found this tutorial helpful. In the next post, I will show you how to upload a file using SSIS and the WinSCP.net assembly.