Using WinSCP for SFTP connections from NAnt for Sitecore and EPiServer projects
Jun 25, 2012
Oshyn uses a combination of Jenkins and NAnt to do builds and deployments of our .NET projects (Sitecore and Episerver). While this has worked great for us, one of the challenges has been the FTP deployments. We had been using the FTPTask from Spin The Moose.
The NAnt tasks looked like this:
<loadtasks assembly="${path::combine(nant-ftptask.dir, 'bin/Release/ftptask.dll')}" />
<connection id="ftp-conn" server="${server.ftp}" username="${user.ftp}" password="${password.ftp}" />
<connection id="ftp-conn-cd" server="${cd.server.ftp}" username="${cd.user.ftp}" password="${cd.password.ftp}" />
<ftp connection="ftp-conn" remotedir="${remotedir.ftp}">
<put type="ascii" localdir="${website-source.ftp}" remotedir="">
<include name="Web.config" />
<include name="Global.asax" />
<include name="ClientEventTracker.ashx" />
<include name="ClientEventTracker.js" />
<include name="LuceneSearch.css" />
</put>
<!-- Include in the following PUT all DLLs that are not part of the standard Sitecore installation -->
<put type="bin" localdir="${website-source.ftp}/bin" remotedir="bin" createdirs="true">
<include name="*.dll" />
<include name="*.xml" />
</put>
<put type="ascii" localdir="${website-source.ftp}/layouts" remotedir="layouts" createdirs="true">
<include name="**.ascx" />
</put>
<put type="ascii" localdir="${website-source.ftp}/xsl" remotedir="xsl" createdirs="true">
<include name="**" />
</put>
<put type="ascii" localdir="${website-source.ftp}/js" remotedir="js" createdirs="true">
<include name="**" />
</put>
<put type="bin" localdir="${website-source.ftp}/img" remotedir="img" createdirs="true">
<include name="**" />
</put>
<put type="ascii" localdir="${website-source.ftp}/SearchUtils" remotedir="SearchUtils" createdirs="true">
<include name="*.aspx" />
</put>
<put type="ascii" localdir="${website-source.ftp}/WebServices" remotedir="WebServices" createdirs="true">
<include name="*.aspx" />
</put>
<put type="ascii" localdir="${website-source.ftp}/css" remotedir="css" createdirs="true">
<include name="**" />
</put>
<put type="ascii" localdir="${website-source.ftp}/App_Config/Include" remotedir="App_Config/Include" createdirs="true">
<include name="Sitecore.Analytics.ExcludeRobots.config" />
<include name="Sitecore.Analytics.config" />
<include name="ldap.config" />
</put>
<put type="ascii" localdir="${website-source.ftp}/App_Config/Security" remotedir="App_Config/Security" createdirs="true">
<include name="*" />
</put>
<put type="ascii" localdir="${website-source.ftp}/App_Config" remotedir="App_Config" createdirs="true">
<include name="*.config" />
</put>
<put type="ascii" localdir="${env-config-source.ftp}" remotedir="App_Config/Include" createdirs="true">
<include name="Sitecore.Analytics.config" />
<include name="ScalabilitySettings.config" />
<include name="Forms.config" />
</put>
<put type="ascii" localdir="${env-config-source.ftp}" remotedir="App_Config" createdirs="true" unless="${string::contains(environment.param, 'Prod')}">
<include name="*.config" />
</put>
<put type="ascii" localdir="${env-config-source.ftp}/CM" remotedir="App_Config" createdirs="true" if="${string::contains(environment.param, 'Prod')}">
<include name="*.config" />
</put>
</ftp>
<!-- The following FTP transfer is used only for Content Delivery server -->
<ftp connection="ftp-conn-cd" if="${string::contains(environment.param, 'Prod')}">
<put type="ascii" localdir="${env-config-source.ftp}/CD" remotedir="/Include">
<include name="SwitchMasterToWeb.config" />
</put>
<put type="ascii" localdir="${env-config-source.ftp}/CD" remotedir="/">
<include name="ConnectionStrings.config" />
</put>
</ftp>
However, Dave Alpert has apparently stopped supporting the task.
We've been using this task for FTP protocol, but it does not support SFTP. We quickly found there was no NAnt task alternative that did SFTP (at least not in a few hours of googling) so we decided to shell out using the <exec/> command. The options for this were psftp.exe and WinSCP. PSFTP appeared to be the easier and smaller footprint option so we tried that first. The issue we encountered was how to get the program to not prompt the user to accept the key. If Jenkins is running as SYSTEM service, it is not possible to get it to accept the key automatically. After various attempts with wrapper shell files to redirect "Y" to stdin and registry settings, PSFTP was eventually abandoned.
WinSCP, it turns out, has a way to send in the key fingerprint via the command line. This proved to work successfully and in fact, makes our NAnt a lot cleaner. There are two steps involved:
- Creating the FTP Command file
- Executing the FTP Command file
Creating the FTP Command File
The FTP Command file contains as the first line the connection string including the -hostkey parameter as follows:
<property name="connectionString.ftp" value="open sftp://${user.ftp}:${password.ftp}@${server.ftp} -hostkey="${rsakey.ftp}""/>
<echo file="${ftp.command.file}">
${connectionString.ftp}
option echo off
option batch on
option confirm off
option exclude "*/.svn; *.cs; *.example; *.pdb;"
</echo>
<echo file="${ftp.command.file}" append="true">
cd ${remotedir.ftp}
ascii
put "${website-source.ftp}\*.config"
put "${website-source.ftp}\*.as*x"
put "${website-source.ftp}\*.css"
put "${website-source.ftp}\*.js"
put "${website-source.ftp}\layouts"
put "${website-source.ftp}\xsl"
put "${website-source.ftp}\js"
put "${website-source.ftp}\ErrorPages"
put "${website-source.ftp}\SearchUtils"
put "${website-source.ftp}\WebServices"
put "${website-source.ftp}\css"
put "${website-source.ftp}\App_Config"
bin
put "${website-source.ftp}\bin"
put "${website-source.ftp}\img"
</echo>
<echo file="${ftp.command.file}" append="true">
ascii
cd /${remotedir.ftp}/App_Config
put "${env-config-source.ftp}\*.config"
</echo>
<echo file="${ftp.command.file}" append="true">
exit
</echo>
Executing the ftp.command.file
The next step is to execute the FTP Command file using the <exec/>
task. Use the WinSCP.com executable instead of WinSCP.exe as that is the one intended for use in batch/non-interactive programs:
<exec program="WinSCP.com" basedir="${sftp.exec.dir}" >
<arg value="/script=${ftp.command.file}"/>
</exec>
The only other challenge is to grab the Fingerprint key to be used as input for the -hostkey parameter. This can be done by connecting to the SFTP server from the WinSCP console interface and copying it.
Related Insights
-
Wladimir Moyano
-
Trotsky Gonzalez
-
Yamilet Contreras
Docker and Sitecore
Scalability & Efficiency in Web Development
-
Mateo Recoba
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.