When I first started reading about Deployment Slots I had more questions than answers. My most obvious concern was what was swapped and what was not. The original documentation made statements like the following:
“A slot that you intend to swap into production needs to be configured exactly as you want to have it in production.”
This was a nonstarter for me. There is no way I intend to have my Dev, QA or even my Staging code pointing at a production database. I am sure we all agree that pointing Dev or QA at a production database is foolish. But some might argue that Staging is production code just waiting to be swapped into production. But I would argue that there are times where this is not true. If the changes in stage require “breaking” database schema changes things fall apart quickly. However, some companies address this issue by requiring all releases never break the previous release. That way if you need to quickly swap back to the previous version your application will be able to run.
Nevertheless, I prefer Stage to point at a different database than Production until the time I want to swap them. When I preform the swap I don’t want my connection strings from Stage to follow me into Production. I need the Production connection strings to stick with the Production slot as I update the Virtual IP (VIP) address. A featured introduced in 0.8.10 of the Azure PowerShell Tools actually allows just that. The SlotStickyConnectionStringNames switch enables connection strings not to be moved during swap operations.
With this new feature in place I decided to see if I can combine Web Deployment Slots with Release Management. I will use Visual Studio Online (VSO) and Release Management Online (RMO) to manage the movement of the code from each slot with database changes being promoted with SSDT via Proxy Servers. A Proxy Server is a machine that sits between RMO and the target compute instance (PaaS Website, Cloud Service, Linux Machine, etc.). I will deploy to the first slot and simply swap my way into production.
[ ] Add PowerShell to Project
[ ] Configure build to create package
[ ] Create Azure Website with Dev and QA slots
[ ] Create 3 Azure SQL Databases
[ ] Create 3 Azure IaaS Proxy VMs (install SSDT, Web Deploy, Azure SDK)
[ ] Add Azure Subscription to RM
[ ] Configure Environments in RM
[ ] Create Release Path in RM
[ ] Create Components in RM
[ ] Create Release Template in RM
[ ] Trigger Build
The application I am deploying is my People Tracker application I first introduced at TechEd North America 2014.
It is a simply ASP.NET MVC application backed by SQL Server. I am using Entity Framework database first with SSDT to deploy. A key point to be aware of is you need to make sure your SSDT Database Project is configured to target Microsoft Azure SQL Database.
My goal is to setup an Azure Website with two deployment slots Dev and QA.
Each slot of the website will be backed by an Azure SQL Database (Production, Dev & QA).
In the MVC project I create a configurations folder to hold the PowerShell scripts I need to deploy the database, deploy the website and swap the slots.
It is very important that you set the Copy to Output of each of these files to Copy always. This will ensure they are copied to the drop location of your build.
Below you will find the three required files to deploy using Deployment Slots. Make sure you use -Verbose to produce command output. All the configuration variables we add to Release Management and several system variables will be available to our PowerShell scripts. The $AzureWebsiteName is a global configuration variable I will set under the Administration tab. The $Slot is the component level configuration variable. Finally $applicationPath and $PackageName are system variables automatically passed in by Release Management. The Publish-AzureWebsiteProject cmdlet copies the files from the drop location to the provided slot. This script will only be used during the first stage of our release path.
Publish-AzureWebsiteProject -Name $AzureWebsiteName -Slot $Slot -Package "$applicationPath\_PublishedWebsites\$($ProjectName)_Package\$ProjectName.zip" -Verbose
To move the code the rest of the way we are simply going to swap it from slot to slot. Make sure you don’t forget the -Force. If you do it will fail because it will display a dialog asking for confirmation.
Switch-AzureWebsiteSlot -Name $AzureWebsiteName -Slot1 $From -Slot2 $To -Force -Verbose
Finally we have the PowerShell script that calls SqlPackage.exe to deploy our SSDT dacpac.
& 'C:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin\SqlPackage.exe' /Action:Publish /SourceFile:$applicationPath\$FileName /TargetConnectionString:"Data Source=$SqlServer;User ID=$UserID;Password=$Password;Initial Catalog=$DatabaseName"
Now check in the code to VSO. With the solution ready we can turn our attention to the build.
Thanks to the integration of VSO and RMO you can use the out of the box build process template to trigger a release from a build. The only thing we need to do is add arguments to pass to MSBuild so it creates a package for our web project. Simply set the value of MSBuild Arguments to ‘/p:DeployOnBuild=True’.
With the solution and build configured we need to create the environments in Azure to deploy to.
Below is the contents of the resource group I created to put this demo together. The new Resource Group feature of the Preview Portal makes it very easy to see all the related resources.
As you can see it is more than you might first expect. This demo requires a Website with two deployment slots, three IaaS Virtual Machines, three Azure SQL Databases, a virtual network and a storage account. Your first question might be why if we are using PaaS Websites do I have three IaaS virtual machines? When working with PaaS you need a machine from which you can execute your PowerShell because you cannot connect to the computing instance for your Website. Because Release Management currently has a restriction where a server used in an Azure deployment can only appear in one environment, we are required to create an IaaS virtual machine to act as a Proxy Server for each slot. I have been assured a better solution is coming. Another alternative is to forego RMO and use an On Premises installation of Release Management and use a single Agent based proxy for all the stages.
Azure SQL Databases
I had to select a Service Tier of at least Standard to get my deploys to work. Basic would always timeout.
If you intend to connect to your Azure SQL Databases from your development machine be sure and add your machine IP Address to the databases firewall. Otherwise you will never be able to connect.
Deployment slots are only available in the Standard Web Hosting Plan Mode so make sure you select Standard or you will have to upgrade them.
After you have created the Dev and QA slots make sure you set the desired connection strings for each slot pointing to the correct Azure SQL Database.
Because I am using Entity Framework Database first (same would be true for Model first) when setting the connections strings for the slots, I had to select Custom as the connection string type.
Now we have come to the most important point of this post. Now I have to use the Set-AzureWebsite cmdlet with the Slot Sticky Connection String Names switch to make sure the connection strings stick to the slot during a swap.
Set-AzureWebsite -Name mysite -SlotStickyConnectionStringNames @("DemoDBEntities")
You only have to run this command on the production slot. The other slots will share the sticky connection string names settings.
We are now to the oddest part of this demo. We cannot connect directly to a PaaS website to install an agent or target it with DSC. So we have to use another machine to execute our PowerShell script using the cmdlets from the Azure SDK to deploy our websites and swap our slots. These machines are called Proxy Servers. They sit between RM and the Websites and provide a place for us to execute our scripts.
When I create virtual machines in Azure I prefer to create the Cloud Service and Storage Account that is going to hold it first so that I can control the names. If you simply use the Virtual Machine wizard it will create a Cloud Service and Storage Account with crazy names. You can store all three Virtual Machines in the same Cloud Service and Storage Account.
Proxy servers do not have to be powerful machines. I created the smallest Windows Server 2012 R2 machine I could. You need to create one for each slot you intend to deploy too. Once the servers are provisioned and running do yourself a favor and disable IE Enhanced Security Configuration.
Now we need to install the following components on the machine:
Once the platform installer starts you can click the back arrow. Search for all the components and install them all at once instead of one at a time.
Once the components are installed we need to configure the Azure PowerShell tools to connect them to your Azure Subscription. To begin run the Add-AzureAccount cmdlet. If you have more than one subscription connected to the account you will need to set the default subscription. You can see how to do that here.
Below is the flow of the code through the Deployment Slots:
At time 0 when you have simply created the PaaS Website with a Dev and QA slot and three empty Azure SQL Databases. All three slots show the default page.
Once the release deploys to the first stage using the deployDb.ps1 and deployWebSite.ps1 files the environments will look like this.
Now that the code has been copied to the Dev slot all we have to do is update the QA database and swap the Dev and QA slots.
At this point the QA slot will be the only slot that can be accessed. Moving to Production is very similar to moving to QA. Simply update the database in Production and swap the QA and Production slots.
I think the images really drive home the fact that it is indeed a swap and not a copy from one environment to another. The next three images show the movement of V2 through the stages. The V2 version of the site has a column for Middle Name.
If we elect to enforce the requirement that each release must be backwards compatible with the current version both the Production and QA slots would be accessible at this point.
We now have to connect Release Management to your Azure Account. From the Administration tab click on “Manage Azure”. Click the New button to open the “New Azure Subscription” page. Use any name you like. You can locate your “Subscription ID” from the Azure Management Portal. From the Azure Management Portal select “Settings”. The settings page list all your Subscriptions with their Subscription ID’s. Just copy and paste the GUID into Release Management. Next you will need to get your “Management Certificate Key” from https://manage.windowsazure.com/publishsettings. Just save the file to somewhere safe and open it with a text editor. Copy the ManagementCertificate value without the quotes and paste into Release Management. Finally you need to provide a Storage Account Release Management will use to move files to Azure. You can use the same Storage Account we created to hold our virtual machine.
With your Azure account configured in Release Management we can now create our Environment. Click the “New vNext Azure” button on the Environments tab under “Configure Paths”. Then click “Link Azure Environment”. Select your subscription and select the storage account we created and click Link. Now click "Link Azure Servers", select our virtual machine and click Link. You can now Save & Close the Environment. You need to repeat this for all three proxy servers.
Next we have to create the “vNext Release Path” using our new environments. Using the environments we just created add as many stages as you need (one for each slot). The bits of our build will be copied there and the PowerShell we be executed from these machines. The PowerShell will publish our application to the Azure Website, deploy our database changes and swap our deployment slots.
Next we need to define a component. I decided to create a component for each PowerShell I am going to run (DB, Swap and Website). From the “Configure Apps” tab select “Components”. Click “New vNext” and give it a name and set the “Path to package” to “\”.
You will also need to create some Configuration Variables.
There is one configuration variable that I need in several scripts so I decided to define it at the global level under Administration Settings.
The final step is creating a “vNext Release Template”. Click “New” from the “vNext Release Templates” page. Give it a name, select the release path we just created and set the build definition to the correct build then click Create. Right click on Components in the Toolbox and click Add. Link the components to the Release Template. Now drag the “Deploy Using PS/DSC” to the Deployment Sequence. Select the server name from the dropdown. Enter the username you selected when you created the virtual machine in .\UserName format. Enter the password, select the component, set PSScriptPath to “Configuration\deployDb.ps1” and set SkipCaCheck to “True”. Add a Custom configuration variable for DatabaseName and set it to the Dev database name. Now add another “Deploy Using PS/DSC” to the Deployment Sequence. Enter the username you selected when you created the virtual machine in .\UserName format. Enter the password, select the component, set PSScriptPath to “Configuration\deployWebSite.ps1” and set SkipCaCheck to “True”. Add a Custom configuration variable for Slot and set it to the Dev slot name.
The QA and Production stages are very similar except instead of using the deployWebSite.ps1 script in the second action you are going to use the swapSlots.ps1 file and add a Slot1 and Slot2 configuration variables.
Now simply trigger a build and your release will begin.