coder, gamer, parent
If you don’t have an automated build and deploy script to deploy your latest web application to IIS7 you may have problems.
Here is a solution that uses Cygwin and SSH that can allow you to build your application once and deploy to as many windows servers as you’d like.
What you’ll need
Installing Cygwin on your Windows Server
Download Cygwin and and install the following packages:
- wget
- openssh
- openssl
- zip
- unzip
As Administrator run Cygwin from your desktop type the following commands:
$ ssh-host-config
Answer yes to all questions except for “This script plans to use cyg_server, Do you want to use a different name?” Answer no.
$ cyglsa-config
Start the Secure Shell Service
$ net start sshd
Ensure that port 22 is open in your windows firewall.
Download cygwin and install the same packages as listed for the windows server.
Setting up Key Pairs
On the windows server open cygwin and type
$ ssh-keygen -t dsa
When asked for a passphase just hit Enter (no passphrase)
On your build server open cygwin and type
$ ssh-keygen -t dsa
When asked for a passphrase just hit Enter (no passphrase)
This will generate your dsa public/private key pair.
Copy your public key to the target server
$ scp id_dsa.pub Administrator@remote_windows_server_ip_address:~/.ssh/newkey
Append the new public key to the authorized keys for the server
$ ssh Administrator@remote_windows_server_ip_address
$ password: [enter_users_password]
$ cat newkey >> authorized_keys; rm newkey; chmod 644 authorized_keys;
Now that your key is in the servers list of authorised keys you can now run remote commands on the deployment server from your build server.
Configuring NAnt
There is no special NAnt configuration required to perform your actions.
However you DO have to run NAnt inside your Cygwin prompt. This will allow NAnt to take advantage of ssh.
Here is where the magic happens:
<!-- Deploys the build file to the selected Environment --> <target name="deployToStaging" depends="staging zipDeployFiles"> <exec program="scp" commandline="${build.date}.zip ${stagingServer}:/cygdrive/c/inetpub/website" /> <exec program="ssh" commandline="${stagingServer} 'cd /cygdrive/c/inetpub/website; unzip ${build.date}.zip -d ${build.date}; rm ${build.date}.zip;'" /> <exec program="ssh" commandline="${stagingServer} 'appcmd stop site MyWebsite; appcmd stop apppool MyWebsite;'" /> <exec program="ssh"> <arg line="${stagingServer}" /> <arg line="'appcmd set vdir" /> <arg value="MyWebsite/" /> <arg line='-physicalPath:"C:\inetpub\website\${build.date}"' /> <arg line="'" /> </exec> <exec program="ssh" commandline="${stagingServer} 'appcmd start site MyWebsite; appcmd start apppool MyWebsite;'" /> </target>
${stagingServer} is a property which contains the username@ip_address of the target environment.
You’ll notice that this NAnt target script depends on a few previous targets.
<target name="clean"> <delete dir="build" /> </target> <target name="export"> <exec program="${svnExecutable}" commandline="export https://my.svn.server/main/dependent_project/tags/4.0.3 build --username ${nantSvnUsername} --password ${nantSvnPassword}" /> </target> <target name="compile" depends="clean export copy"> <csc target="library" output="${output.dir}/Mammoth.dll"> <sources> <include name="${projectName}/*.cs" /> </sources> <references> </references> </csc> </target> <!-- Copies the Master Pages and Templates to the Build Directory --> <target name="copy"> <copy todir="${build.dir}/xslt"> <fileset basedir="${projectName}/xslt"> <include name="*.xslt" /> </fileset> </copy> <copy todir="${build.dir}/masterpages"> <fileset basedir="${projectName}/masterpages"> <include name="*.master" /> </fileset> </copy> <copy todir="${build.dir}/media"> <fileset basedir="${projectName}/media"> <include name="**/*" /> </fileset> </copy> <copy todir="${build.dir}/sql_scripts"> <fileset basedir="${projectName}/sql_scripts"> <include name="*.sql" /> </fileset> </copy> </target> <target name="config"> <xmlpoke file="${build.dir}/web.config" xpath="/configuration/appSettings/add[@key='DSN']/@value" value="${serverConfig}" /> </target> <target name="production"> <property name="serverConfig" value="${productionConfig}" dynamic="true" /> <call target="config" /> </target> <target name="staging"> <property name="serverConfig" value="${stagingConfig}" dynamic="true" /> <call target="config" /> </target> <!-- set a build date property to today's date with am/pm stored in ${build.date} --> <target name="setBuildDate" description="Creates a build date property for use in our deployment scripts"> <tstamp property="build.date" pattern="yyyy-MM-dd" verbose="true" /> <echo message="Current build label: ${build.date}" level="Debug" /> </target> <!-- Zips all files in the current deployment folder --> <target name="zipDeployFiles" depends="setBuildDate"> <zip zipfile="${build.date}.zip" includeemptydirs="true"> <fileset basedir="${build.dir}" > <include name="**/*" /> </fileset> </zip> <!-- delete dir="${build.dir}" / --> </target>
setBuildDate simply creates a timestamp property with todays date. I use this to create separate folders on the target server in which I have my individual deploys in reverse date format.
This achieves several things:
zipDeployFiles just creates a zip file with your build. Assuming that you’re performing a copy of only your necessary files into a /build directory using NAnt’s copy features.
Zipping the build is especially handy when copying files using secure copy. It will reduce the overhead of the file transfer significantly.
Running the Script
To run the nant script simply open cygwin from your build server and run the following command. This assumes that you have nant in your $PATH environment.
$ nant compile deployToStaging
Extending the Script
With this setup it is easy to extend the NAnt script to execute other commands remotely during a deploy.
The possibilities are endless and highly configurable.
Further Reading
If you’re new to using NAnt I recommend reading the following articles to get yourself up to speed. Once done, you’ll be performing automated build and deploys to multiple platforms/servers in no time.
Justin is a Senior Software Engineer living in Brisbane. A Polyglot Developer proficient in multiple programming languages including [C#, C/C++, Java, Android, Ruby..]. He's currently taking an active interest in Teaching Kids to Code, Functional Programming, Robotics, 3D Printers, RC Quad-Copters and Augmented Reality.
Software Engineering is an art form, a tricky art form that takes as much raw talent as it does technical know how. I'll be posting articles on professional tips and tricks, dos and donts, and tutorials.
1 Response to Automated deploy to IIS7 using NAnt
Essays – Software Craftsmanship - JUSTINSHIELD.COM
June 28th, 2011 at 4:51 pm
[…] web development, use an automated build and deploy scriptIf you don’t have an automated build and deploy script you may have […]