Automated deploy to IIS7 using NAnt

In: Automated Deployment| IIS| NAnt

13 Jun 2010

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:

  • It stops people from using SVN up to deploy files *shudders*.
  • By building a copying your files to production you don’t have subversion along for the ride.
  • Rolling back is as easy as stopping the site and pointing the VDIR to the appropriate folder.

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.

  • extend the deployToStaging command to execute DB Migrations after stopping IIS (see Migrator Dot Net)
  • or perform an export and an import from your DB using the Sql Publishing Wizard (comes standard with VS 2008)

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.


1 Response to Automated deploy to IIS7 using NAnt

Avatar

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 [...]

Comment Form

About this blog

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.

profile for Justin Shield on Stack Exchange, a network of free, community-driven Q&A sites

Photostream