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
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.
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.
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 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. To learn more about deployment groups, read this.
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.
Next, I clicked on + New
Gave the deployment group a name (I named this deployment group MercuryHealth-Canary) and clicked Create
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.
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
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.
Next I did the same thing again for my MercuryHealth-Prod environment/deployment group.
Setting Up the Build
This created a build pipeline that was appropriate for an asp.net application.
There were some minor tweaks that I did to the configuration specific to my project. I picked a hosted VS2017 as my Agent queue
and also tweaked my test task to only run my tests with the TestCategory = Unit Test. I also turned on code coverage.
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
Then clicked + New definition
I then renamed the environment to Canary and clicked on + Add artifact
Selected the build I just created and clicked Add
Which brought up the release pipeline configuration page.
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.
Next I selected the IIS Deployment phase
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.
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.
Next, I needed to enable variable substitution for my web deploy. Remember that web.config file?
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.
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?
Ok, so to add in my Canary connection string and my Canary environment, I clicked Variables
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
Selected MercuryHealth-Canary for my Deployment group, typed in Database for Required tags
Clicked on SQL DB Deploy task
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
And my variables now looked like this
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
Then I tweaked the Deployment Group to be MercuryHealth-Prod for both the IIS Deployment phase and the SQL Deployment phase
And then added the variables and values for my prod environment
And that builds out my full CI/CD pipeline into multiple IIS servers and a SQL server database.
Ok, that was really long. So if all that nonsense was way too much to read, here is the condensed version.
- use deployment groups to deploy to each on prem/IaaS environment
- tag the servers in your deployment group so you can easily deploy in parallel to similar servers
- 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
- 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
- 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