Quality of life user experience improvements to SharePoint Online through the use of 301 redirects and Azure App Service

Originally posted on Lucian.Blog. Follow Lucian on Twitter, @LucianFrango.

***

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.Kloud.com.au

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.Kloud.com.au
      • Target example ImportantKBDemo.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.kloud.com.au
    • 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://ImportantKBDemo.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.kloud.com.au$" />
</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.kloud.com.au")) || (OriginUrl.Contains("Azure Web App FQDN like example.azurewebsites.net")))
{ response.Headers.Location = new Uri("Target FQDN like https://blog.kloud.com.au/azure"); }
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.Kloud.com.au
    • Target example ImportantKBDemo.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.Kloud.com.au
    • 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.

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.

Decoding Behaviour Driven Development

I have been thinking for a while to write a piece on BDD (aka Behaviour Driven Development), not that it was absolutely essential (I consider myself as lazy) but more so because there is very little write up available on relevance of BDD and its use in a .NET environment. BDD is also known to many in the community as ATDD (Acceptance Test Driven Development), certainly for some similarities that can not go unnoticed.

So really, why bother about BDD?

It starts with the notion ‘Did you give me what you promised ?’

I am consciously not going into the detail definition of what is BDD, hoping that you have some amount of understanding of what it is. For absolute beginners, this is just another way of interpreting your solution by its behaviour rather than objective feature (and expressing your development and testing in a business knowledge that is ‘executable’).

In traditional software testing, test cases are derived from the requirements (and nothing wrong in it!). The fact is – customers, at the end, will be bothered more about the solution, less about the ‘requirements’ and what has been tested. The delivered solution/product will always be matched against the customer expectations (and even sometimes, unrealistic expectations).

This is where the problem creeps in!

The bi-directional traceability matrix to establish what links stated requirements and delivered product will be of some assistance but often that’s lost in translation. With the adoption of Agile delivery approach, things have become a bit simpler but the problem doesn’t go away.

Make a living document: whose document is it anyway?

BDD fills that gap. That very difference where features promised to be delivered never met the customers’ acceptance because of a communication gap between what is expected versus what is delivered.

When you think a bit deeper, there are two direct benefits delivered by BDD:

  1. You can organise your requirements (feature file) and test scenarios at the same place following a structured format. These test scenarios will often be the ‘Acceptance Criteria’ for the feature and will be easily monitored (and managed) by business (PO, Business Analyst)
  2. Most importantly, these test scenarios written in natural language are ‘executable’. Yes, you can literally click the run button and the programs takes over from there. Which means, they can be integrated with the build (and follow continuous integration)

It almost becomes the single source of truth in delivering the software that can be integrated with your solution and the whole suite can run as a single entity. Remember, all that BDD is giving you a platform to structure your tests in a natural language. That means you can write any of your tests including, Unit, Integration and UI level automated tests.

I understand that – Tell me how to apply it?

Often seen, organisations using the BDD approach are not applying it right, leading to a non-optimal usage and less effective implementation. We have to keep in mind that our objective here is to bridge the gap between business, developers and test people and BDD is giving us the interface to do that. To write an effective feature file, these three have to come together and will have to agree before a feature can be implemented. An understanding and agreeable environment is the key here – supplemented by the inputs from all three of them to make the BDD feature file more comprehensive, correct and complete.

Always keep in mind that we develop or test something based on what is important for the end user, not what we want. End user behaviour is the key in adopting a BDD-style framework. By elevating your focus from object to feature, you are able look through the lenses of a customer, enabling an app with a great user experience.

Enough words, let’s see some action

Here is a simple demonstration of the way you can take a BDD path and implement using Specflow/Gherkin in a ASP.NET MVC app.

Project set-up:

If you are using Visual Studio 2010 or 2012, install the library from Visual Studio gallery along with the Nuget packages.

https://visualstudiogallery.msdn.microsoft.com/9915524d-7fb0-43c3-bb3c-a8a14fbd40ee

Check the Integration section of the Specflow website for details:

http://www.specflow.org/documentation/Visual-Studio-2012-Integration/

The Specflow website has all other relevant details on similar implementation using other Visual Studio versions.

Feature file and Executable tests

Below is an example of a typical feature file where you will write all your tests for a feature. It is always a good practice to organise your tests by feature and you can logically group them under a feature file. The feature file contains the behaviour of your application components and your tests are aligned to verify those behaviours. Once you have outlined the tests, you can generate a Definition file which will contain your underlying code.

clip_image001

The Definition file will give you to structure your code aligned with the scenarios outlined in the feature file. They will be replaced by the actual code that will allow you to run your tests – be it Unit, Integration or UI.

After you write your own piece of code in the bindings, it will look something like this. (You will continue to write the page Object Model to simulate the user behaviour in a similar way)

clip_image003

And, finally, one last thing before you run. Check that you have the right Test runner in App.config.

clip_image004

And you are all set to go!

clip_image005

There is no denying fact that BDD can instil good discipline. It is not something very new, not something radically different from what we have been doing to date (you probably already use ‘Arrange-Act-Assert’ anyway), but a great way to organise your thoughts, put them in a live document that is ‘not just another document lying somewhere’ but something you can execute and get an instant feedback.

So, why not give it a go in your next project?

Follow Us!

Kloud Solutions Blog - Follow Us!