The expectations surrounding app releases have changed greatly in the last several years. From months-long release cycles to release many times in a day. In part, this is made possible by smaller applications powered by micro services and some of the typical technologies employed with these applications. In particular, Containers bring a lot of new capabilities including easier deployments via orchestration tools such as Kubernetes.
As a developer and team lead, one of the best added benefits of containers is how easy it is to spin up an environment for each change. What is typically called Environment per PR. Basically, you have a new, short-lived, environment dedicated for your changes.
This is beautiful, but, what if you don’t or can’t use containers? Can you spin up environments like this with good old VMs and IIS?
Yes. But not easily.
I’m sure that what I will be showing here is not directly applicable to you, but it should be a good start for most .NET apps deployed using AzureDevOps Pipelines to IIS and SQL Server. These are the steps:
- Build stage
- Build, unit test, and create artifacts ready for deploy
- PR Deploy stage
- Deploy IIS website(s)
- Create IIS website and app pool
- Update appsettings.json
- Copy app files to the right location
- Create DB
- Create/update DB using a good backup
- Apply DB Migrations
- Add comment to PR
- Deploy IIS website(s)
The main pipeline
The main pipeline will only describe the stages and set up the parameters. All the actual work happens in the build and deploy stages. Skip to the end of the article to see the complete pipeline yaml files.
In this stage we want to build the app(s) and publish any deployables as build artifacts. I will assume that you have your app built and ready to be deployed so I won’t provide details on how to do this. For the purposes of this post, you should have your backend, frontend, and db migrations as published artifacts in your build.
Deploy PR stage
In this stage, we will do everything needed to spin up a new environment. This includes: creating a SQL Server DB, restoring a backup with good test data, creating or updating IIS App Pools and Web Sites, Deploy build stage files to appropriate folders, and finally drop a comment in the PR with the new environment URL. To make things easier to understand, let’s talk about each step first and see the final stage yaml at the end.
Restore a good backup as your PR DB
We can achieve this somewhat easily with a procedure in master DB that you can call from Powershell. You should install the latest Sql Server PS Module. Also, the user running your pipeline needs very high level privileges. I will not go into whether this is a good idea or not from a security standpoint, but do tighten your DB security as much as you can.
Then, you can invoke that procedure from Powershell like so:
Update DB version with PR migrations
By now, most projects are using Db Migrations one way or another. I typically do them the old fashioned way where the developer is responsible for manually creating versioned scripts. If you have the scripts in a folder like I do, DbUp is a great tool to update your DB. There is also a Visual Studio Marketplace extension for it.
You may use EF migrations or anything else that suites you though.
Deploy IIS website(s) from template xml
You can achieve this using appcmd and netsh as they should be installed with any IIS version you may use. Below are some commands that can be executed in powershell to create a website:
You can either create the website and app pool with specific commands like above, or, you can export and import the site and app pool. First, export existing site and app pool:
Since we want each PR site to be unique, we need to do some templating in the xml files. Just change the app pool and site names to something you can find and replace later via powershell:
You can then use some powershell to replace the placeholder before using appcmd to apply the file:
Finally, use cmd and below commands to import the site and app pool:
Finally, appcmd won’t assign certificates to HTTPS bindings. We can use netsh for that though:
Updating configuration files
There is a good chance you keep some settings in the appsettings.json for .NET apps and perhaps in other files for your frontend. You can easily update these settings using pipeline variables and tokenized files. I like to use Replace Tokens from Guillaume Rouchon marketplace extension for this.
Make sure the variable exists in your pipeline stage:
Add comment to Pull Request
After the PR is deployed, you should add some instructions to the PR on how to test it. This usually includes an environment URL and anything else relevant for the test. There are many tasks to add comments to PR, but I’ve used Pull Request Utils from Joachim Dalen successfully before.
In below example, we comment the URL for the environment as well as instructions on how to update the hosts file so that DNS resolution works:
PR Environment teardown
Because your PR Environment is in the intranet, there is a good chance you can’t reach it from Azure DevOps. If this is true, you won’t be able to use Webhooks to tear down your environment when the PR is closed.
You can achieve this with a bit more of powershell. Use it to read the active PRs and compare to the active environments. If any environment does not have a corresponding open PR, you can tear it down.
Putting everything together
There are a lot of steps to do and they are harder than they would be in more “modern” scenarios. The key point is: you don’t need containers to get Environments per PR.
The final pipeline files may look like this:
Here is some very useful content to read how other people have done this: