Build and Deploy to multiple IIS servers and SQL Server using VSTS

The League was asked by twitter to show how to build and deploy a web app to multiple on prem IIS Servers and the schema of the database for the web app to an on prem SQL Server. Totally a fair request. All of my demos recently have been using PaaS, Azure SQL and Containers. I have totally neglected on prem servers and IaaS! So to remedy that situation, here is a detailed walkthrough.

If you don’t want to read all this nonsense, skip to the bottom of the blog to my tldr section Smile

Scenario

Application

For this blog, I created a ci/cd pipeline that built and deployed an asp.net web app which connected to a SQL Server. The schema of the DB was captured in a SSDT Database project and checked into source control, right along side the actual source.
image

In the web project, the Web.config’s connection string was pointing to localdb. This way, the developer could easily dev on his own box. Also in the Web.config, there was an appSettings with key = Environment and value = (LOCAL). This appSettings key and value was used by the web app ui to display where the app was running from.

image

Infrastructure

In this example, my infrastructure had two environments. A Canary environment and a Production environment. Both Canary and Production were identical. They consisted of three machines. Two IIS web servers sitting behind a load balancer and a SQL server.

Setup

Setup was done in two parts. First, I set up my infrastructure to support deploying using Deployment Groups. Then, I created my build and release pipelines in VSTS.

Setting Up My Infrastructure For Deployment

My infrastructure was already in place. IIS was installed and configured on the web server machines and SQL Server was also installed and configured on the db machine. All I needed to do was setup my infrastructure for deployment. I used Deployment Groups in VSTS to deploy to all the servers in my environment (2 web servers and a sql server). Using Deployment Groups allowed me to easily deploy my web app to multiple servers in parallel, as well as my db schema to my db server.

One of the pre-requisites for this type of deployment was .NET 3.5 on all of my servers.  This was because the deloyment ultimately used msdeploy.exe and sqlpackage.exe to deploy my web app and database schema and those libraries required .NET 3.5. When I built out my environment, .NET 3.5 was not installed on my web servers so to install it, I opened an admin level power shell prompt and entered:

dism.exe /online /enable-feature /all /featurename:NetFX3 /Source:D:\sources\sxs

Creating Deployment Group In VSTS

The first thing I needed to do was create a Deployment Group in VSTS. To do that, I hovered over the Build and Release tab and then selected Deployment Groups.

image

Next, I clicked on + New

image

Gave the deployment group a name (I named this deployment group MercuryHealth-Canary) and clicked Create

image

This took me to the Details page where I got the powershell script needed to install and configure the VSTS agent on all the machines in my environment.

Install VSTS Agent on Environment Machines

Next, I installed and configured the VSTS agent on all the machines in my environment. I clicked the Use a personal access token in the script for authentication and then clicked Copy script to clipboard.

image

This copied the script I needed to my clipboard, including a PAT for authentication. Then, I went to each of the machines in my Canary environment (my two web servers and my db server), brought up an admin level PowerShell command prompt and pasted the script and ran it. After installing the VSTS agent, the script asked me two questions. Enter deployment group tags for agent (I entered Database for my database server and  Webserver for my web servers) and enter user account to use for the service (I chose the default NT AUTHORITY\SYSTEM).

This installed the VSTS agents and registered them to the MercuryHealth-Canary deployment group. Clicking on Targets

image

I saw all my machines in the deployment group. Notice how the web servers were tagged as WebServer and the db was tagged as Database.

image

Next I did the same thing again for my MercuryHealth-Prod environment/deployment group.

Setting Up the Build

Since I was using an asp.net app, I just used the asp.net template to create my build pipeline. I hovered over Build and Release and then selected Builds
image
Clicked + New
image

And selected the ASP.NET (PREVIEW) template.
image

This created a build pipeline that was appropriate for an asp.net application.

image

There were some minor tweaks that I did to the configuration specific to my project. I picked a hosted VS2017 as my Agent queue

image

and also tweaked my test task to only run my tests with the TestCategory = Unit Test. I also turned on code coverage.

image
one final tweak, I needed to copy the “built” dacpac file to the staging directory so I added a Copy Files task
image

After tweaking the build and making sure it worked, I created my release pipeline

Setting Up The Release

I hovered over Build and Release and selected Release

image

Then clicked + New definition

image

And selected the IIS Website and SQL Database deployment template
image

I then renamed the environment to Canary and clicked on + Add artifact

image

Selected the build I just created and clicked Add

image

Next I needed to configure my release tasks so I clicked the tasks
image

Which brought up the release pipeline configuration page.

image

For my app, the website was named MercuryHealth on port 80, the application pool name was MercuryHealth the web app was named MercuryWeb and my database name was MercuryWeb. The website path was C:\MercuryHealth. The web app path was C:\MercuryHealth\MercuryWeb.

Next in my Environment, I unlinked all. You can keep everything linked. Just enter in the correct configuration. I unlinked them because I thought I was going to do a more involved deploy with even more machines so I unlinked them at the environment level and just configured my info at the task level.

image

Next I selected the IIS Deployment phase

image

For deployment group i selected MercuryHealth-Canary. For Required tags I entered in WebServer. So now all tasks in the IIS Deployment phase will deploy in parallel to the machines in the deployment group MercuryHealth-Canary with the tag Webserver.
image

Next I clicked on the IIS Web App Manage task
image

Because I set things up as a WebApp, I changed the Configuration type to IIS Web Application, Parent website name to MercuryHealth, Virtual path to /MercuryWeb and Physical path to C:\MercuryHealth\MercuryWeb, check Create or update app pool, set the app pool name to MercuryHealth.

image

Next, I clicked on IIS Web App Deploy to configure that task
image

I entered MercuryHealth for Website Name, MercuryWeb for my Virtual Application
image

Next, I needed to enable variable substitution for my web deploy. Remember that web.config file?

image

I needed to be able to substitute the connection string with the name DefaultConnection and the appsettings with the key Environment with the correct values for the environment I’m deploying to.  At this moment, my deployment bits are all zipped up and they are holding values used for local dev. Not my Canary environment. The IIS Web App  Deploy task makes swapping in the correct value super easy.

First, I clicked File Transforms & Variable Substitution Options, then I checked XML variable substitution
image

Now, if there are variables defined in my release definition that matches a connectionString name  or appSettings key in my config files, this task will swap in the values in my variables section!!! How cool is that?
image

Ok, so to add in my Canary connection string and my Canary environment, I clicked Variables

image

And added variables for DefaultConnection with the connection string to my canary sql server, then locked it. Added Environment with value (Canary) and I scoped both of those variables to the Canary environment

image
I still needed to finish configuring my tasks for my db deployment so i next clicked back to Canary Task and selected the SQL Deployment phase.
image

Selected MercuryHealth-Canary for my Deployment group, typed in Database for Required tags

image

Clicked on SQL DB Deploy task

image
Entered MercuryWeb for my database name. Selected SQL Server Authentication for my DB and entered in my DB user name and password (i added them to my variables for canary). Also, since sometimes I want to delete columns or do stuff that destroys data, i added /p:BlockOnPossibleDataLoss=False to the Additional Arguments
image
And my variables now looked like this

image
That’s it for the canary environment.  This release definition deploys my web app in parallel to all my web app servers in the canary environment, then my db schema deploys to the db server  in the canary environment.

To create a the release to Production, I cloned the Canary environment

image

Then I tweaked the Deployment Group to be MercuryHealth-Prod for both the IIS Deployment phase and the SQL Deployment phase

image

image

And then added the variables and values for my prod environment

image

And that builds out my full CI/CD pipeline into multiple IIS servers and a SQL server database.

TLDR

Ok, that was really long. So if all that nonsense was way too much to read, here is the condensed version.

  1. use deployment groups to deploy to each on prem/IaaS environment
  2. tag the servers in your deployment group so you can easily deploy in parallel to similar servers
  3. Because the dacpac deploy task and the website deploy tasks require .net 3.5, make sure .net 3.5 is installed on machines you are deploying to
  4. to change values in your config files, just check the XML variable substitution checkbox under File Transforms & Variable Substitution options in the IIS Web App Deploy task and then add the variable values for the release variables and scope it to the right environment
  5. If you are using dacpacs to deploy your DB schema, it is using SqlPackage.exe. So to enable destroying data, add /p:BlockOnPossibleDataLoss=False as an additional argument to the SQL Server Database Deploy task.

For more information about Deployment Groups in VSTS, check out http://cda.ms/43

Leave a Reply

Your email address will not be published. Required fields are marked *