I recently had a requirement, to set up numerous Azure App Services, each in their own app service plan. They were all using a shared custom domain name and one common SSL certificate. Some of the app services had to allow anonymous access, while others had to be secured using Azure AD Authentication. Using an application gateway was the way to go, to be able to do all of this, and I created a couple of proofs of concept before ultimately deciding on using path-based routing to accomplish this.

My first proof of concept set up each app service on their own sub-domain of my custom domain name. Such as app1.mycustomdomain.com, app2.mycustomdomain.com, app3.mycustomdomain.com

Pros of using the sub-domain approach:

  • Fairly easy to configure. Each app service has its own hostname, and application gateway listeners can be set up to listen to each specific hostname.
  • Overview of design: several app services, one application gateway, one app gateway listener per hostname (sub-domain), pointing to the correct app service backend. Since each hostname is unique, and sites were hosted at the hostname root level, there was no need to change anything related to Azure AD Authentication redirects. In fact, I was able to use the built-in Azure Active Directory (Azure AD) enterprise identity service provided by Azure, with no real code changes necessary.

Cons of using the sub-domain approach:

  • You need a SAN or wildcard certificate that will contain every sub-domain you’re going to be using. These certs are typically more expensive and will need to be regenerated every time you want to add a new sub-domain (at least in the case of a SAN cert).

My next proof of concept was to set up each app service as a sub-directory under my custom domain, like so: mycustomdomain.com/app1, mycustomdomain.com/app2, mycustomdomain.com/app3. 

Pros of using the path-based routing approach:

  • You can use one basic SSL certificate for your domain name. No need for a SAN cert, or wildcard cert. One certificate for mycustomdomain.com, will cover all of my app services, and there will be no need to regenerate a new certificate while adding new app services.

Cons of using the path-based routing approach:

  • At least initially, I had a lot of headaches with getting the Azure AD authentication redirects to work properly. I’ll share more on that later, as that is the majority of the reason I created this post.

Obstacles I ran into, using Azure AD Authentication Service with Application Gateway path-based routing and solution for me:

When you use the Azure Active Directory (Azure AD) enterprise identity service provided by Azure, you have no control over the redirect URI (at least that I have found). This becomes an issue when you’re trying to use an application gateway with path-based routing. This is because authentication requests will go to the root url, in this case, mycustomdomain.com, rather than staying on the path for the entire request. (mycustomdomain.com/app1 for example). Once a request gets issued to the root domain, the app gateway will listen and send it to whatever your default listener is, which will not redirect properly back to your path, to complete the authentication lifecycle.

Your best bet, in this scenario, is to ditch the Azure Active Directory (Azure AD) enterprise identity service and instead implement the authentication yourself, in your code, using the OpenIDConnect middleware with Owin.  At least this was the case with my app, because it was an older .NET framework application, and was not built with .NET Core. An example of wiring this up can be found here: https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-asp-webapp (for .NET Framework) or here for .NET Core. Also, be mindful of the default sigin-oidc path, as your redirect URI will look something like https://mycustomdomain.com/app1/signin-oidc. If you leave the signin-oidc off the redirect URI, you will notice that your app will seem to authenticate but will not be able to set the ASP.NET authentication cookies properly. 

If anyone is trying to do something similar and has issues, or suggestions of other ways this is possible, please let me know! thanks!