Deploying it right with AppVeyor CI and PowerShell

Introduction

You can find a lot of articles on how to make continuous delivery of a single ASP.NET web application. Many of those articles model a perfect world where a simple, a slightly-modified VS.NET template web application is deployed using Web Deploy. Everything works smoothly in a perfect world.

Deploying real applications is hard. Questions arise when there are configuration settings in the Registry, custom folders structure, custom permissions, Windows services hosting WCF back-end or you have to deploy to a web cluster.

In this article we look at the process of setting up continuous delivery for a solution consisting of ASP.NET web application and Windows service to a staging and production environments using PowerShell remoting and AppVeyor CI.

AppRolla uses PowerShell remoting to execute deployment tasks on the target machine. Deployment task downloads application package, unpacks it, updates configuration settings and creates or updates application website and pool. Application package is just a zip with application folder contents uploaded to some external storage and available via HTTP. There is no magic at all - the module is written in PowerShell and could be easily explored or modified.

To give you a sneak peek of PowerShell deployment let’s create a simple web application and deploy it to a server.

Perhaps, the most challenging part in the whole process is setting up PowerShell remoting with SSL certificate. We strongly recommend using HTTPS to communicate with remote servers as all traffic is encrypted.

When you create a new virtual machine on Windows Azure PowerShell remoting will be automatically enabled and configured. PowerShell remoting HTTPS endpoint port 5986 is allowed on firewall and we also add HTTP endpoint to our demo server:

Let’s create a trivial web application with a single “Hello, world!” page and deploy it to the demo server.

Create a new SimpleWebApp-1.0.zip archive with the first version of default.aspx inside.

Now, we have to upload application package to some external storage to make it available from target server via HTTP. It could be a web server with FTP enabled, Amazon S3 or Azure blob storage. For our demo we are going to use DropBox. It provides public download links to any items in your DropBox folder.

Copy SimpleWebApp-1.0.zip to your DropBox folder then right-click the file and select “Share DropBox link”. Open the link in the browser and copy URL of “Download” button:

Deploy “SimpleWebApp” application to “Demo” environment as version 1.0:

New-Deployment SimpleWebApp 1.0 -to demo

(Click on the image to enlarge it)

Voila! You just deployed web application to a demo server using PowerShell:

Now, let’s change page contents and deploy another version of our demo app. Change “Hello, world!” to “Hello, world 2.0!” Create a new SimpleWebApp-1.1.zip archive with changed default.aspx and upload to DropBox again.

As you can see from the log a separate folder is created for every new deployment in location c:\applications\<application-name>\<role-name>\<version>. By default, 5 previous deployments are stored on the target server, so the application could be easily rollback to previous version:

Restore-Deployment SimpleWebApp -on demo

To delete all application deployments from demo environment:

Remove-Deployment SimpleWebApp -from demo

We use simple, concise commands to the job!

Continuous builds with AppVeyor CI

AppVeyor CI is a cloud-based continuous integration and deployment platform for Windows developers. It’s hosted, so you don’t need to install and configure it and it’s super easy to setup CI for your project. Oh, and forgot to mention that AppVeyor CI is free for open-source projects!

To setup your project in AppVeyor CI its sources must be hosted in online source control repository like GitHub, BitBucket or Kiln. Both Git and Mercurial are supported.

In our tutorial we use Mercurial repository hosted on BitBucket. BitBucket is pretty cool for commercial projects as it offers unlimited private repositories for free.

Enable NuGet restore

If your solution depends on NuGet package manager do not forget to enable NuGet restore to automatically download packages on build server. Use this guide to enable NuGet package restore and make sure NuGet.exe inside .nuget folder is added to the repository.

Add new project

OK, let’s start by creating a new project:

Once the project is created a new web hook is automatically added to the project repository to kick-off new build on every push.

Build configuration and config transforms

When deploying with Web Deploy config transforms is a key part in application configuration process. For each environment you are going to deploy to you define a new VS.NET solution configuration and then use config transforms to generate web.config with database connection strings and other application settings specific for each environment. This approach seems natural, but has a number of disadvantages:

Sensitive information like database connection strings is stored in a source control.

Config transform is applied during the build and every time the project is deployed to a different environment it has to be re-built.

When deploying with AppVeyor config transforms are helpful, but not essential. By default, VS generates two configurations Debug and Release and this is perfectly OK for many cases. Debug configuration is used locally during development and Release configuration is used by CI process to produce a package that could be deployed to any environment. Config transforms should be “really” used to transform configuration file structure, such as disabling “debug” flag, enabling custom errors, disable tracing or replacing Autofac modules, i.e. configuration common for all environments.

Go ahead and change project build configuration to Release on its settings page:

AppVeyor offers three build scenarios:

Visual Studio solution - runs MSBuild against VS.NET solution or project file (search for the first .sln or .*proj if not specified) and packages build results of all projects as artifacts.

When “Update assembly version attributes” is enabled AppVeyor will patch all AssemblyInfo.* files in solution directory to set current version.

Running tests

AppVeyor could discover and run tests in assemblies using these testing frameworks:

MSTest

NUnit

xUnit

When “Test” stage is enabled AppVeyor will analyze all assemblies in “out” folder to check if they have references to any supported testing framework. If they do all tests within the assembly will be run using appropriate test runner. Test results from all assemblies are aggregated and shown on UI:

(Click on the image to enlarge it)

Build. Test. Package!

OK, now let’s kick-off a new build by pushing some changes to a repository or clicking “New build” button.

For our demo project the build process produced two artifacts: web application and Windows service. Download them to check their contents - they are just regular zips with application files!

As Windows service package is just basically contents of its “bin” folder to produce a package for web application there are more steps involved:

Web application is built as part of solution.

A new publishing profile with “File system” publishing method is created and WAP project is published using MSBuild and this profile to make sure web.config transforms and other publishing settings are applied.

Published web application is packaged to a zip.

Artifacts are stored in Geo-redundant cloud storage and available by unique private download links. To have your own artifacts naming structure and to enable public access you can configure a custom storage.

Deploying successful builds to staging

Now, let’s setup automated deployment to staging as part of the build process.

Deployment should be done in a script that can be PowerShell or batch file. Create “deployment” folder in the root of solution repository to hold our deployment scripts.

Open PowerShell console, navigate to “deployment” folder and run the following command to download template scripts into to current directory:

There are three scripts will be added: configure.ps1, project.ps1 and deploy.ps1.

Basically, to setup deployment we have to edit only one file - project.ps1. This file defines environments we are going to deploy to. Uncomment the line next to new staging environment and add our demo server:

Both ServerUsername and Password are used to create Credential object for authenticating calls via PowerShell remoting. API keys are required for: a) reading project artifacts to get their package URLs and b) authenticating target server to download artifact packages. AppVeyor API keys could be found on “API Keys” tab of your user profile.

That’s it! Commit “deployment” folder and push it to repository to start a new build with deployment.

How to update connection string in web.config?

Deployment variables are passed to the script in $variables parameter which represents a hashtable. To make additional configuration values available to the application or role use “Configuration” parameter for Set-Application, Set-WebsiteRole or Set-ServiceRole cmdlets. Open project.ps1 and add the following statement:

Deployment script applies role configuration to web.config for web apps and app.config for Windows apps using the following rules:

Setting with name “ConnectionStrings.<name>” updates connection string with <name> name in “connectionStrings” section.

Setting with name “AppSettings.<name>” updates “appSettings” value with <name> name.

Conclusion

In this article we are not trying to downplay Web Deploy tool which might work well for your web application. But if your project is beyond of a blueprint web application that must be deployed to a clustered environment or you need more flexibility and control over the deployment process it’s definitely worth considering alternative solution like PowerShell remoting. This could be especially appealing for developers with PowerShell skills or those ones using PSake.

About the Author

​Feodor Fitsner is a .NET developer with entrepreneur spirit, being around Windows Web platform for more than 10 years now. Feodor's newest project is Appveyor CI - hosted Continuous Integration solution for .NET developers. Prior to AppVeyor Feodor developed DotNetPanel control panel for Windows hosting and then was working at Microsoft in Azure org.

Is your profile up-to-date? Please take a moment to review and update.

Email Address

Note: If updating/changing your email, a validation request will be sent

Company name:

Keep current company name

Update Company name to:

Company role:

Keep current company role

Update company role to:

Company size:

Keep current company Size

Update company size to:

Country/Zone:

Keep current country/zone

Update country/zone to:

State/Province/Region:

Keep current state/province/region

Update state/province/region to:

Subscribe to our newsletter?

Subscribe to our industry email notices?

You will be sent an email to validate the new email address. This pop-up will close itself in a few moments.

We notice you're using an ad blocker

We understand why you use ad blockers. However to keep InfoQ free we need your support. InfoQ will not provide your data to third parties without individual opt-in consent. We only work with advertisers relevant to our readers. Please consider whitelisting us.