During the swap operation the site in the staging slot is warmed up by making an HTTP request to its root directory. More detailed explanation of that process is available at How to warm up Azure Web App during deployment slots swap. By default the swap will proceed as long as the site responds with any status code. Deployment slots or Azure slots are actually instances of Azure Web Apps which are tied to that website. A deployment slot will carry the name of the Azure Web App + SlotName. For example, if my Azure Web App is called DebDemo and I create a slot called staging, then my slot will be an Azure Web App with the name DebDemo(staging) and its url.
- Azure Websites recently added support for multiple deployment 'slots' like Dev, Test, Staging. Daria Grigoriu shows Scott how this works and we look at scenarios where slots can be useful.
- GitHub Action for deploying to Azure Static Web Apps. This Github Action enables developers to build and publish their applications to Azure App Service Static Web Apps. This action utilizes Oryx to detect and build an application, then uploads the resulting application content, as well as any Azure Functions, to Azure.
- Static means that we aren’t deploying any server code; just the front end files. Azure will do all of the web work for us. Azure will do all of the web work for us.
When I deploy software, I’m lazy. Very lazy. This is why I lean heavily on Continous Deployment (CD) to automatically test and deploy software when it’s merged into my
main
branch. I don’t have time to deploy code by hand. So gauche!Of course this requires a lot of trust in my automation and testing before merging code into
main
. But the overall time savings and agility gained through CD are tremendous!It’s possible to set up a continuous blue-green deployment process on Azure using deployment slots.
I did this recently, but ran into a subtle gotcha when I tried to set up a custom warm-up with my ASP.NET Core site. A custom warm-up is a URL that Azure will hit after code is deployed to the offline deployment slot or slots. This gives the app a chance to run custom warm-up code before the slot is swapped into production.
The documentation mentions two app settings you can use to set up a custom warm-up:
Azure Deployment Slots Swap
WEBSITE_SWAP_WARMUP_PING_PATH
: The path to ping to warm up your site. Add this app setting by specifying a custom path that begins with a slash as the value. An example is/statuscheck
. The default value is/
.WEBSITE_SWAP_WARMUP_PING_STATUSES
: Valid HTTP response codes for the warm-up operation. Add this app setting with a comma-separated list of HTTP codes. An example is200,202
. If the returned status code isn’t in the list, the warmup and swap operations are stopped. By default, all response codes are valid.
Seems straightforward. So I set something up like this:
WEBSITE_SWAP_WARMUP_PING_PATH
:/statuscheck
WEBSITE_SWAP_WARMUP_PING_STATUSES
:200
Azure Deployment Slots Database
Afterwards, all my deployments failed. I used
curl
to confirm my /statuscheck page returned a 200
code.I tried running the swap manually from the Azure Portal and it gave a very unhelpful error message. First of all, the error message contains the following string:
Cannot swap slots for site '{0}' because the application initialization in '{1}' slot either took too long or failed.
It looks like someone forgot to pass parameters to
string.Format
.Second, it mentioned a
Bad Request: 400
status code.I then looked in my logs and didn’t see any
400
status code requests. Instead, I saw some 307 Temporary Redirects
. I looked more closely and noticed requests for:http://mysite.example.com/statuscheck
Notice the problem? The Azure warmup ping request is making HTTP requests, not HTTPS requests. And you can’t change this. The setting
WEBSITE_SWAP_WARMUP_PING_PATH
does not allow a fully qualified URL. It will always make an HTTP request.The problem for me is that I have the following code in my app startup (
Startup.cs
).You might recognize that
UseHttpsRedirection
method call from the default ASP.NET Core project templates. That adds middleware to redirect HTTP requests to HTTPS.On top of that, I usually configure Azure Web Applications to be HTTPS only via the
TLS/SSL settings
. This is why the Azure swap warmup request fails. It expects a 200
but is getting a 307
and doesn’t follow the redirect.In a useful troubleshooting blog post, the author mentions using rewrite rules to solve this. Not sure that’s going to work for my site since I don’t even have a
web.config
file and I’m not using rewrite rules to enforce HTTPS in the first place.Fortunately the solution is straightforward. First, I went to the
TLS/SSL settings
for my site in the Azure Portal and turned HTTPS Only
to Off
.Then I branched my request pipeline using the
UseWhen
extension method.In my actual code, I wrote an extension method like so:
That way I could simplify the code in my startup class to:
UseWhen
takes two parameters. The first is the condition. In this case, when the request is NOT a status check request. The second is the code to call when that condition is true, in this case it registers the HTTPS redirect middleware.In effect, this code forks my app pipeline into two paths. For requests to
/statuscheck
HTTP requests are allowed. For all other requests, we continue to redirect HTTP to HTTPS.Problem solved!
What’s particularly subtle about all this is if you set up
WEBSITE_SWAP_WARMUP_PING_PATH
but do not set up WEBSITE_SWAP_WARMUP_PING_STATUSES
, the warm-up request by default accepts all status codes, including 307
. So your deploys will pass, but it may not necessarily be done with the warm-up as it’s unclear if Azure follows the redirect in this case.This was a frustrating experience, but I got it working in the end. My hope is that if you have a similar setup to me, this will save you time and headaches.
UPDATE: My deploys still fail. If I specify
WEBSITE_SWAP_WARMUP_PING_STATUSES
= 200
my deploys still fail even though I can use curl
to see the status check URLs return 200. I’m at a loss here and I’ve opened up a support ticket with Microsoft. I’ll post back here with what I learn.UPDATE: Resolved the issue. I tried to be clever and pass a query string parameter to my warmup URL. For example, by setting
WEBSITE_SWAP_WARMUP_PING_PATH
to /statuscheck?azure-warmup=true
. This way I could quickly distinguish my own testing of this URL from the request made by Azure in my logs. Upon reflection, of course this doesn’t work because Azure URL Encodes WEBSITE_SWAP_WARMUP_PING_PATH
before requesting it. So the request path is /statuscheck%3Fazure-warmup=true
and not /statuscheck
as I expected.Conclusion
In the end, I figured out what I was doing wrong and it’s easy to chalk it up to PEBCAK (Problem Exists Between Chair And Keyboard). While that may be true, it’s also true that the feature could improve its usability to reduce PEBCAK occurrences.
Sebastian Ros on Twitter suggested a couple of improvements:
- It should allow for https or accept a redirect to the https endpoint
- It should not encode the URL you passed
I definitely agree with the first suggestion. Otherwise everyone deploying a standard ASP.NET Core app will run into this and my not even realize it!
My preference would be to follow the redirect so IT JUST WORKSTM. I understand this is not trivial and requires a bit of thought. For example, it can’t blindly follow the redirect. The initial request is made internal to Azure, bypassing (as I understand it) DNS and the load balances etc. So it would need to be smart about following the redirect. If the redirect is to the same endpoint, but only the scheme is changed, then re-make the original request with HTTPS.
Azure Deployment Slots
Might be easier to add a radio button group to select between HTTP and HTTPS. Either way, the current behavior is a hidden gotcha.
As for the second suggestion, I also agree. It would require validation of the user input. For example, if I put something that isn’t allowed for the end of the URL, it should present an error message. Alternatively, have two fields, one for the path and one for the query string. Either way, the current behavior is also confusing and a hidden gotcha.
This post explains some of the not so well-known features and configurations settings of the Azure App Service deployment slots. These can be used to modify the swap logic as well as to improve the application availability during and after the swap. Here is what you can do with them:
Swap based on the status code
Azure Function Deployment Slot
During the swap operation the site in the staging slot is warmed up by making an HTTP request to its root directory. More detailed explanation of that process is available at How to warm up Azure Web App during deployment slots swap. By default the swap will proceed as long as the site responds with any status code. However, if you prefer the swap to not proceed if the application fails to warm up then you can configure it by using these app settings:
WEBSITE_SWAP_WARMUP_PING_PATH
: The path to make the warm up request to. Set this to a URL path that begins with a slash as the value. For example, “/warmup.php”. The default value is /.WEBSITE_SWAP_WARMUP_PING_STATUSES
:Expected HTTP response codes for the warm-up operation. Set this to a comma-separated list of HTTP status codes. For example: “200,202” . If the returned status code is not in the list, the swap operation will not complete. By default, all response codes are valid.
You can mark those two app setting as “Slot Settings” which would make them remain with the slot during the swap. Or you can have them as “non-sticky” settings meaning that they would move with the site as it gets swapped between slots.
Minimize random cold starts
In some cases after the swap the web app in the production slot may restart later without any action taken by the app owner. This usually happens when the underlying storage infrastructure of Azure App Service undergoes some changes. When that happens the application will restart on all VMs at the same time which may result in a cold start and a high latency of the HTTP requests. While you cannot control the underlying storage events you can minimize the effect they have on your app in the production slot. Set this app setting on every slot of the app:
WEBSITE_ADD_SITENAME_BINDINGS_IN_APPHOST_CONFIG
: setting this to “1” will prevent web app’s worker process and app domain from recycling when the App Service’s storage infrastructure gets reconfigured.
The only side effect this setting has is that it may cause problems when used with some Windows Communication Foundation (WCF) application. If you app does not use WCF then there is no downside of using this setting.
Azure Static Website Deployment Slots Software
Control SLOT-sticky configuration
Originally when deployment slots functionality was released it did not properly handle some of the common site configuration settings during swap. For example if you configured IP restrictions on the production slot but did not configure that on the staging slot and then performed the swap you would have had the production slot without any IP restrictions configuration, while the staging slot had the IP restrictions enabled. That did not make much sense so the product team has fixed that. Now the following settings always remain with the slot:
- IP Restrictions
- Always On
- Protocol settings (Https Only, TLS version, client certificates)
- Diagnostic Log settings
- CORS
If however for any reason you need to revert to the old behavior of swapping these settings then you can add the app setting
WEBSITE_OVERRIDE_PRESERVE_DEFAULT_STICKY_SLOT_SETTINGS
to every slot of the app and set its value to “0” or “false”.swap Diagnostics detector
Azure Static Websites
If a swap operation did not complete successfully for any reason you can use the diagnostics detector to see what has happened during the swap operation and what caused it to fail. To get to it use the “Diagnose and solve problems” link in the portal:
Azure Static Website Deployment Slots Tool
From there click on “Check Swap Operations” which will open a page showing all the swaps performed on the webapp and their results. It will include possible root causes for the failures and recommendations on how to fix them.