HTTP 301 redirects via Azure App Service for SharePoint Online

Quality of life user experience improvements.

This is the third time in the last year that I’ve had to setup a HTTP 301 redirect in Azure for a customer. Doing so improves the general quality of life experience for users accessing various Microsoft 365 services, like for example specific SharePoint Online team sites, or Exchange Online OWA.

With each implementation I turned to Azure App Service to deliver the functionality needed. The first time I created an App Service Web App and used the web.config file method, described in further detail in this blog post, to achieve that. The second time I had to do that, I simply copied the first process and got things up and running as quickly and easily as the first time. The third time, which was very recent, I decided to use Azure Functions to do that via an App Service Functions app instead.

Why?

Users (don’t forget that you’re one of them) want things to be efficient, reliable, and often times as easy as possible to get something done. Improving that quality of life user experience wins you (the administrator or architect of systems and services) brownie points and frees up time to do more important things than having to listen to those pesky users again.

The main example I’m going to use in this blog is around SharePoint Online and providing a streamlined way to access a specific team site that a department might have put together to outline a series of new initiatives or policies within your enterprise. SharePoint Online can have some length URI’s for those team site pages buried within the navigation tree in department X and business unit Y and so on. You’ll end up with something that looks like:

https://AwesomeCustomer.Sharepoint.com/Sites/DepartmentThatRunsTheShow/WhereAmI/WhatIsThisCoolThing/OhThereItIs/Important/SitePages/Home.aspx

Who on earth is going to remember that URI to be able to communicate that to users effectively? Sure, bookmarks work great and so does CTRL+C and CTRL+V. In reality, it’s not a pretty URI to look at, to remember or distribute. So, with the use of some Azure App Service, some HTTP 301, we can condense this down to something that users can remember:

https://Important.lucian.franghiu.com

I guarantee that no one in the enterprise will forget that URL! (Oh, and in case you were wondering, that URL does not work; it’s just an example)

Using an App Service Web App and the web.config method

This is nice and easy and quick to do. It’s a very reliable method of 301 redirect as well with even the lowest App Service Plan which for me seems like the right plan for this given that its very light work.

To execute this process, here’s all you need to do:

  • Log into the Azure portal
  • Select App Services
  • Select Add
    • Enter in globally unique name for your Web App
    • Select the subscription
    • Create a new or use an existing Resource Group
    • Select an App Service Plan or create a new one
    • Select weather you’d like App Insights enabled or not
  • Wait for the Web App to be created
    • While I wait, I like to jump across to reddit during this stage and check whats new in the world
  • Select your Web App once it’s been created
  • Select Custom Domain
    • Grab the IP address associated with your Web App
    • Either use this or the FQDN of your Web App to create a DNS A (IP address) or CNAME (FQDN) record for your domain, example
      • CNAME label example Important.lucian.franghiu.com
      • Target example ImportantLBDemo.azurewebsites.net
      • TTL 3600 (or 1 hour in seconds)
      • Wait a little bit more as DNS propagation time might be required at your DNS hosting provider
  • Back to Custom Domain after some time
    • Select Add Hostname
    • Enter in your FQDN for the domain, example important.lucian.franghiu.com
    • Select validate
    • If all is green for Hostname Availability and Domain ownership, select Add hostname
  • Select SSL settings from the main right-hand menu (just under Custom Domains)
    • You’ll need an SSL to upload
      • HTTP is not secure, so we never want to use that
    • Upload your public certificate (without the private key) to the Public Certificate (.CER) store
    • Follow up and do the same for your cert with the private key in the Private Certificate (.PFX) store
    • Select Bindings and let'’s bind our hostname to our SSL
    • Select Add SSL Binding
    • Choose the hostname from the drop-down menu
    • Select the matching SSL from the Private Certificate Thumbprint dropdown
    • Select SNI SSL from the SSL type dropdown
    • LASTLY, enable HTTPS ONLY mode by selecting the ON button next to the Protocol Settings HTTPS ONLY option
  • From the right-hand menu, select Advanced Tools
    • Select Go to advance to the Kudu Advanced tools menu
    • From the new window (which the URL should look like example https://ImportantLBDemo.SCM.azurewebsites.net) select the Debug Console and the CMD option, or PowerShell - it doesn’t matter
    • You’ll have the folder structure at the top
    • Drill down to SITE > WWWROOT
    • Select the + button and select New File
    • Name the file web.config
    • Select the Pencil icon to edit the code
    • Enter in the bellow
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Redirect" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="^important.lucian.franghiu.com$" />
</conditions>
<action type="Redirect" url="https://AwesomeCustomer.Sharepoint.com/Sites/DepartmentThatRunsTheShow/WhereAmI/WhatIsThisCoolThing/OhThereItIs/Important/SitePages/Home.aspx" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
  • Select Save
  • Job done!

I said earlier that this is a quick process, and in practice there isn’t too many difficult steps, but it can be a bit long to complete. In addition, having an App Service sitting there waiting, essentially running “warm” regardless if anyone hits the site to be redirected seems like it’s wasting compute and money.

Insert Azure Functions

When the third request for a redirect came in, I thought it would be great idea to reduce some of that consumption spend, reduce some of the wasted and idle compute and embrace serverless!

The following process is how you would achieve the same HTTP 301 redirect, but using some C# code running as an Azure Function app in the App Service stack:

  • Log into the Azure portal
  • Select + Create a resource
  • Search for Function App and select Create
    • Name the app (again globally unique name with an FQDN of example.azurewebsites.net)
    • Select the appropriate subscription
    • Create a new or use an existing Resource Group
    • Select the host OS Windows (Functions app supports Windows of Linux)
    • Hosting plan leave as Consumption Plan (we want to maximise costs in this example)
    • Select the appropriate location
    • Runtime stack leave as .NET
    • Create a new or select an existing storage account
    • Enable or Disable Application Insights
  • Wait for the Function App to be created
  • Select the Function App
  • Select Functions and select the + to add a new function
    • Select HTTP Trigger
    • For the new function select C# as the language
    • Name the function “Redirect”
    • Select Function as the HTTP trigger authorisation level
    • Select Create
  • Once created, select the Function “Redirect”
    • This is where you’ll enter the following code
using static System.Environment;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net;
using Microsoft.Extensions.Logging;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ILogger log)
{
string OriginUrl = req.Headers.GetValues("DISGUISED-HOST").FirstOrDefault();
log.LogInformation("RequestURI org: " + OriginUrl);
//create response
var response = req.CreateResponse(HttpStatusCode.MovedPermanently);
if((OriginUrl.Contains("Desired FQDN like imporant.lucian.franghiu.com")) || (OriginUrl.Contains("Azure Web App FQDN like example.azurewebsites.net")))
{ response.Headers.Location = new Uri("Target FQDN like https://lucian.franghiu.com/aboutlucian"); }
else
{ return req.CreateResponse(HttpStatusCode.InternalServerError); }
return response;
}
    • This is a modified version of the code I found from Daniel Nikolic via this blog post
    • The main difference is the updated way to provide logging in Azure using iLogger
    • To explain the process, here’s my explanation
      • Desired FQDN = this is the nice new, short, etc, FQDN you want users to use
      • Azure Web App FQDN = the FQDN of your Azure Function App Service
      • Target FQDN = the target where users will be redirected to
    • You can change the above to suit your redirecting needs
    • Save the code
    • Next to the save button at the top of the screen there is the </> Get Function URL button, select that
    • Copy the Function URL
  • Select Proxies from the left-hand menu
    • This is where we’ll need to create a proxy to complete the redirect
    • Name the proxy, something like RedirectProxy will do
    • Enter the Route Template detail of {*path}
    • Allow HTTP methods should be All Methods
    • Backend URL is where you paste the Function URL from earlier
    • Select Create
  • Select your Function app from the left hand menu
  • Prepare your domain name
    • You’ll need to create a DNS CNAME record, like in the web.config example
    • This is to be done at your domain registrar or DNS hosting provider
    • CNAME label example Important.lucian.franghiu.com
    • Target example ImportantLBDemo.azurewebsites.net
    • TTL 3600 (or 1 hour in seconds)
    • Wait a little bit more as DNS propagation time might be required
  • Select Platform Features
  • Select Custom Domains
    • Select Add Hostname
    • Enter in your FQDN for the domain, example important.lucian.franghiu.com
    • Select validate
    • If all is green for Hostname Availability and Domain ownership, select Add hostname
  • Go back to Platform Features and select SSL
    • You’ll need an SSL to upload
      • Again, HTTP is not secure, so we never want to use that
    • Upload your public certificate (without the private key) to the Public Certificate (.CER) store
    • Follow up and do the same for your cert with the private key in the Private Certificate (.PFX) store
    • Select Bindings and lets bind our hostname to our SSL
    • Select Add SSL Binding
    • Choose the hostname from the drop-down menu
    • Select the matching SSL from the Private Certificate Thumbprint dropdown
    • Select SNI SSL from the SSL type dropdown
    • LASTLY, enable HTTPS ONLY mode by selecting the ON button next to the Protocol Settings HTTPS ONLY option
  • Job done!

Oh no, (its like Citrix VDI) it’s slow!

After implementation and testing, the redirect via Azure Function App Service worked well. I tried a number of times in a short span of time across different web browsers on my Surface Pro 6. All looked great.

However, this was all within the range of the Azure Functions idle window of 20 minutes. From what I understand, once a Function is “warm”, it will remain idle in this state for about 20min to execute follow up requests in a nice snappy manner. After that idle period expires though, the warmth dissipates, and we’re left with some cold Function.

This cold state of Function is a deal breaker for using this particular solution for me and this third redirect request. For my customers use case, there’s not enough user volume over a 24-hour period to keep the Function App, under the Consumption service plan, to maintain the warm state, even with a total user count exceeding 35,000 Microsoft 365 licensed users.

To best describe this, I’d encourage you to check the following blog posts from Azure MVP Mikhail Shilkov, @MikhailShilkov:

In the testing I conducted with cold starts, he redirect Function took 18.37sec to run. I could imagine that this would likely happen quite often for this particular redirect I wanted to setup. I know that not every employee of the customer would be hitting the URL all that often. I also didn’t want to switch back to a non-Consumption Service Plan as I wanted to really embrace and take advantage of serverless.

In the end…

…I went with the original solution via the web.config file method because the cold start limitation. This original method is fast, is still relatively cheap to run and even cheaper to maintain (nothing needs to be done!). I’ll come back to this in the future when there’s improvements in Functions cold start. For now, if it isn’t broken, don’t fix it.

Read more on:
Comments...
Disclaimer
Shout out 👍