Sitecore Site Definitions in Azure Deployment Slots
Azure App Service Plans allow you to define multiple deployment slots in order to stage a Sitecore site prior to go-live. This powerful feature allows you to swap in your site once it is warmed up… and my personal favorite, set a dedicated Web database per slot so I can smoke test and warm up the site with the appropriate content before swapping.
Of note, deployment slots are only appropriate for a CD App Service as you don’t want two CM App Services running at the same time due the conflicting jobs that could run.
But what do we do about setting the proper Site Definitions when using deployment slots? If you have an Enterprise topology, you likely have separate Dev, QA, UAT, Staging, Prod and DR environments… which also contain multiple deployment slots.
For example, Azure has its own hostname added to an App Service that ends in the .azurwebsites.net extension… Everything before the dot in .azurewebsites.net is a hostname tied to an App Service. When creating a deployment slot, it also gets its own hostname. In essence, you will end up with multiple hostnames per App Service/Deployment Slot spanning multiple environments.
If using custom domains instead of the azurewebsites.net address for your access needs (either for testing a staging slot or serving public traffic), you will still have hostnames per App Service/Deployment Slot. If you use both (as you can’t remove the .azurewebsites.net address) or have a multisite configuration, you can see how quickly all these hostnames pile up.
Contents
Why is this a Challenge?
This becomes a challenge as you will have developers who need to account for the various site hostnames in their Site Definitions.
These hostnames will vary by environment and deployment slot. This means a Sitecore Site Definition could have many hostnames to account for every environment and deployment slot, or the developers will need to do some potentially clever transforms to modify a include file for the particular environment to match the exact hostname upon deployment… and that includes deployment slots.
Having seen a lot of transforms go bad over the years, you have to carefully govern this process. So, what are the options for setting hostnames in Site Definitions that span multiple environments and deployment slots?
4 Options for Sitecore Definitions in Deployment Slots/Multiple Environments
Here are four options for setting Sitecore Site Definitions in deployment slots/multiple environments. In these examples, I am using an include file that has a multi-site configuration for those that also run multi-site with deployment slots.
Option 1 – Transforms
The first option is to create a transform that changes the hostname in your Sitecore Site Definition to the environment and slot you are targeting. This option may defeat the value of deployment slots in that if you set the hostname of the deployment slot, you will need to change that before you swap to production as doing so requires a .config change that triggers a restart of Sitecore… so your transform will need to include a piped hostname value set per environment that accommodates each slot, including the primary slot serving your traffic.
This leads us to the “One Pipe of Hostnames to Rule Them All” option
Option 2 – Include All Hostnames Across All Environments via Piping
In this option, you would set all the hostnames of every environment and deployment slot. This can end up being a very long pipe, but avoids the need to transform or have individual files per environment. Notice that I am not stating a file per deployment slot, as you would defeat the purpose of using the hot slot swapping if you have to change out the file for match the hostname of a slot receiving traffic.
Note that we are focused on the hostname in a Site Definition. This is because we can set multiple piped entries and still match on the targethostname. If something does not match on the target, it fails over to search our hostnames:
If the targetHostName attribute is not available, Sitecore uses the value of the hostName attribute instead.
targetHostName – SDN (sitecore.com)
An example include file with piping could look like:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:environment="http://www.sitecore.net/xmlconfig/environment/">
<sitecore>
<sites environment:require="!Local" role:require="ContentDelivery">
<site name="2nd-site"
inherits="website"
targetHostName="my-cd.azurewebsites.net"
hostName="my-cd-dev.azurewebsites.net|my-cd-dev-staging-slot.azurewebsites.net|my-cd-qa.azurewebsites.net|my-cd-qa-staging-slot.azurewebsites.net|my-cd-uat.azurewebsites.net|my-cd-uat-staging-slot.azurewebsites.net|my-cd-stage.azurewebsites.net|my-cd-stage-staging-slot.azurewebsites.net|my-cd-prod.azurewebsites.net|my-cd-prod-staging-slot.azurewebsites.net|my-cd-dr.azurewebsites.net|my-cd-dr-staging-slot.azurewebsites.net|dev.mysite.com|qa.mysite.com|stage.mysite.com|prod.mysite.com|dr.mysite.com"
set:virtualFolder="/2ndsite"
set:physicalFolder="/2ndsite"
set:rootPath="/sitecore/content/2ndsite"
enablePreview="true"
patch:before="site[@name='website']" />
<site name="main-portal"
inherits="website"
targetHostName="my-cd.azurewebsites.net"
hostName="my-cd-dev.azurewebsites.net|my-cd-dev-staging-slot.azurewebsites.net|my-cd-qa.azurewebsites.net|my-cd-qa-staging-slot.azurewebsites.net|my-cd-uat.azurewebsites.net|my-cd-uat-staging-slot.azurewebsites.net|my-cd-stage.azurewebsites.net|my-cd-stage-staging-slot.azurewebsites.net|my-cd-prod.azurewebsites.net|my-cd-prod-staging-slot.azurewebsites.net|my-cd-dr.azurewebsites.net|my-cd-dr-staging-slot.azurewebsites.net|dev.mysite.com|qa.mysite.com|stage.mysite.com|prod.mysite.com|dr.mysite.com"
set:rootPath="/sitecore/content/home"
enablePreview="true"
patch:before="site[@name='website']" />
</sites>
</sitecore>
</configuration>
Option 3 – Wildcards in Hostnames
In the third option, you can set wildcards in the hostname. This would allow you to not require as long of piping or clever transforms. Of note, the hostname is trying to catch a subdomain so you must use something akin to *azurewebsite.net as *.azurwebsite.net will not function properly. Here is an example of option 3 would function… a lot less pipes!
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:environment="http://www.sitecore.net/xmlconfig/environment/">
<sitecore>
<sites environment:require="!Local" role:require="ContentDelivery">
<site name="2nd-site"
inherits="website"
targetHostName="my-cd.azurewebsites.net"
hostName="*azurewebsites.net|*mysite.com"
set:virtualFolder="/2ndsite"
set:physicalFolder="/2ndsite"
set:rootPath="/sitecore/content/2ndsite"
enablePreview="true"
patch:before="site[@name='website']" />
<site name="main-portal"
inherits="website"
targetHostName="my-cd.azurewebsites.net"
hostName="*azurewebsites.net|*mysite.com"
set:rootPath="/sitecore/content/home"
enablePreview="true"
patch:before="site[@name='website']" />
</sites>
</sitecore>
</configuration>
Option 4 – Hybrid Subdomain Piping and Wildcards
There are a few situations (ex. a very specific site SEO URL for a part of your site vs. serving up that site via routing) where you may want a hybrid approach of wildcards and subdomains piped in your hostnames for a Site Definition. A potential include file could look like the following:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:environment="http://www.sitecore.net/xmlconfig/environment/">
<sitecore>
<sites environment:require="!Local" role:require="ContentDelivery">
<site name="2nd-site"
inherits="website"
targetHostName="my-cd.azurewebsites.net"
hostName="*azurewebsites.net|2ndsite.mysite.com"
set:virtualFolder="/2ndsite"
set:physicalFolder="/2ndsite"
set:rootPath="/sitecore/content/2ndsite"
enablePreview="true"
patch:before="site[@name='website']" />
<site name="main-portal"
inherits="website"
targetHostName="my-cd.azurewebsites.net"
hostName="*azurewebsites.net|*mysite.com"
set:rootPath="/sitecore/content/home"
enablePreview="true"
patch:before="site[@name='website']" />
</sites>
</sitecore>
</configuration>
Conclusion
There are a few different approaches, each with their own pros and cons when it comes to managing the various hostnames spanning environment and deployment slots. Whichever option you use (and please test!), you have to make sure that you can properly govern it.