How to create a shared access signature for a specific folder in Azure Storage

As we move into a serverless wold where devlopment work is done less on generic multipurpose servers that require patching both to the OS and to the application installed on them, things become easier and in some cases cheaper.

For instance I am currently working on a project that needs to send and recieve XML files from different sources and with no other requirements for a server, Azure storage seemed the perfect fit. As it’s just a storage service you pay for what you need and nothing more. All the patching and maintencance of API’s is provided by Microsoft and we have an instant place to store out files. Ideal!

However new technology always comes with new challenges. My first issue came with the plan on how we would be getting files in and out with partners. This isn’t the first time I’ve worked with Azure File Storage, but it is the first time I did it without an opps person setting things up. Previously we used FTP, which while dated works with a lot of applications. Like a headphone jack it’s not the most glamorus of connection, but it works, everyone knows how it works and everyone has things that work with it. However it transpires that despite Azure storage being 10 years old and reciving requests for FTP from the start, Microsoft have decided to go the same route as Apple with the headphone jack and not have it. Instead the only option for an integration is REST. As it transpires the opps people I had worked with in the past when faced with this issue had just put a VM infront of it, which kind of defeats the point of using Azure storage in the first place!

So we’re going with REST and Microsoft provide quite a straightforward REST API all good so far, but how do we limit access? Well there’s a guide to Using the Azure Storage REST API which contains a section on creating an authorisation header. It’s long and overly complex, but does point you in the direction that to do this you need a Shared Access Signature. The other option is an access key, but this is something you should never give away to a third party.

Shared Access Signature

After a bit more digging through the documentation (and just clicking the thing that sounded right in the potal) I found this documentation on creating an Account SAS which sounded like what I wanted (it wasn’t, but it’s close).

With a shared access signature you can say what kind of service should be allowed, what permissions they should have, IP address’s, start and end dates. All awesome things.

Once I had this I could then use the REST API, but there was a problem. I could access every folder in the storage account and there was no way to stop this! For integrating with 2 third partys they would both be able to access each others stuff, and our own private stuff.

There is also no way to revoke the SAS once it’s been generated other than refreshing the access keys which would affect everyone.

Folder Level Shared Access Signature

After a bit more research I found what I was looking for. How to create a shared access signature at a folder or item level and how to link it to a policy.

The first thing you need is Azure Storage Explorer. Once your set up with this you will be able to view all your storage accounts.

From here you are able to browse to the folder you want to share right click it and choose Manage Access Policies.

This will open a dialoge to manage the policies for this specific object rather than the account.

Here you can set all the same permissions as you could for a signature at an account level but now for a specific object and against a policy rather than an actual signature, meaning the policy can be updated in the future with no change to the signature.

Better still you can remove the policy which will then invalidate any signature using it.

For the actual signature key right click the same folder and click Get Shared Access Signature.

Then in the dialoge select the policy from the drop down rather than spcifying the individual permissions.

Click create and you can copy the keys.

You now have an access key that is limited to a specific folder rather than the entire account.

This is only possible to do though one of the code/scripting interfaces. e.g. powershell or the storage explorer. The azure portal will only let you get signatures at an account level.

Debugging Sitecore 9 Analytics Issues

With Sitecore 9 the way analytics is recorded and processed changed. Rather than everything being done by the IIS application, the architecture changed to include:

  1. A second IIS application called xconnect
  2. A windows services called Sitecore XConnect Search Indexer

As well as this Mongo was replaced by:

  1. SQL Server databases
  2. A SOLR XDB Core

Sitecore 9 also introduced data being secure in transit as well as at rest which means all traffic is encrypted using certificates.

While all these peices can be considered good, it does also create more points of failure that becomes harder to debug. So after spending a decent amount of time debugging why no analytic reports were loading and then why no data was appearing in the reports, I’ve made a checklist to go through.

Debugging Sitecore Analytics Checklist

1. XConnect Connection String

The main Sitecore application has connection strings for where it will find the XConnect service. They will look like this:

  <add name="xconnect.collection" connectionString="https://mysite.xconnect" />
  <add name="xconnect.collection.certificate" connectionString="StoreName=My;StoreLocation=LocalMachine;FindType=FindByThumbprint;FindValue=EF7F38B623E6664359110F2C6EB6DA00D567950F" />

One gives the path to the XConnect service and the other gives the path to find the certificate.

Firstly check that the XConnect path is correct and that you can access it and secondly check that the thumbprint corresponds with a certificate in the certificate store.

You can see what certificates are on your machine using this powershell script:

Get-ChildItem -Path "cert:\LocalMachine\Root" | Format-Table Subject, FriendlyName, Thumbprint
Get-ChildItem -Path "cert:\LocalMachine\My" | Format-Table Subject, FriendlyName, Thumbprint
Get-ChildItem -Path "cert:\CurrentUser\My" | Format-Table Subject, FriendlyName, Thumbprint

2. Check certificate expiry date

Having a certificate is a good start, but it could still have expired.

Find the certificate in the certificate store by hitting start > manage computer certificates and find it in one of the folders.

Check the expiration date. If it’s in the past then it’s not going to work. This is common because the installation script for Sitecore 9 will set the expiration date to a year after install by default.

If it has expired you will need a new cert, you can create this by using the same script that you used to install sitcore origionally (Just the certificate bit).

#Switch to correct vesion of SIF
Remove-Module -Name SitecoreInstallFramework
Import-Module -Name SitecoreInstallFramework -RequiredVersion 1.2.1

#define parameters
$prefix = "SitePrefix"
$PSScriptRoot = "C:\resourcefiles9.0"
$XConnectCollectionService = "$prefix.xconnect"
$sitecoreSiteName = "$prefix.sc"

#install client certificate for xconnect
$certParams = @{
 Path = "$PSScriptRoot\xconnect-createcert.json"
 CertificateName = "$prefix.xconnect_client"
 RootCertFileName = "SIF121Root"
}

3. Check security permissons on the certificate

If you have a certificate and it’s still valid then it could be that the app pool the site is running in doesn’t have access to read the certificate.

To check this:

  1. Right click the certificate
  2. All Tasks
  3. Manage Private Keys
  4. Check that the app pool user for your site is listed in the list of users and that it has read permission. If it’s not there add it using the name
    IIS APPPOOL\app pool name

4. Check License Files

Partner licenses only last for a year so if your using one of those it may have expired.

We’re all used to checking the license file in the Sitecore application but XConnect has a license too.

These will be in:
sitename.xconnect\App_data\
sitename.xconnect\App_data\jobs\continuous\IndexWorker\App_data
sitename.xconnect\App_data\jobs\continuous\AutomationEngine\App_Data

Only the first will be used when your viewing the site, but it’s worth knowing about the others to, incase you ever run a job manually.

5. Check manual rebuild of indexes

You can trigger a manual rebuild of the xDB index by following these instructions:

https://doc.sitecore.com/developers/90/sitecore-experience-platform/en/rebuild-the-xdb-index-in-solr.html

Remember in point 4 that it has it’s own license file. It also has it’s own connection strings.

6. Check XConnect site works in a browser

If you open XConnect in a browser you should recieve no certificate errors and a timestamp saying how long XConnect had been running for.

7. Check certificates are in the right store

This stack overflow post was a big help for me (https://stackoverflow.com/questions/26247462/http-error-403-16-client-certificate-trust-issue ). I was at the point where everything seemed right, but moving the certificates as shown here got it to the point of the analytics reports loading.

Windows 2012 introduced stricter certificate store validations. According to KB 2795828: Lync Server 2013 Front-End service cannot start in Windows Server 2012, the Trusted Root Certification Authorities (i.e. Root) store can only have certificates that are self-signed. If that store contains non-self-signed certificates, client certificate authentication under IIS returns with a 403.16 error code.

To solve the problem, you have to remove all non-self-signed certificates from the root store. This PowerShell command will identify non-self-signed certificates:

Get-Childitem cert:\LocalMachine\root -Recurse | 
    Where-Object {$_.Issuer -ne $_.Subject}

In my situation, we moved these non-self-signed certificates into the Intermediate Certification Authorities (i.e. CA) store:

Get-Childitem cert:\LocalMachine\root -Recurse | 
    Where-Object {$_.Issuer -ne $_.Subject} | 
    Move-Item -Destination Cert:\LocalMachine\CA

Checklist for data not going into Analytics

If you’ve got to the point of the analytics reports working, but not showing any data, this is my checklist for making sure data goes in. In my case I was trying to log site searches as per my article from a few years ago https://himynameistim.com/2017/09/13/populating-the-internal-search-report-in-sitecore/ there wern’t any errors, but no data ever showed.

1. Enable Analytics Debugging

In Sitecore.Analytics.Tracking.config there is a setting to set the analytics logging level to debug. You will also need to set the log level on log4net root in Sitecore.config to debug.

2. Disable Robot Detection

In my case Sitecore thought I was a robot. Changing these settings will disable that:

<setting name="Analytics.AutoDetectBots" set:value="false" />
<setting name="Analytics.Robots.IgnoreRobots" set:value="false" />

3. Test analytics is tracking something

One of the hardest parts about analytics is it’s not instant. The initial tracking only goes into the DB at the end of the users session and that’s only for collection. It won’t appear in the reports until processing has happened. So to speed this up:

Create a page called kill.aspx as follows. This will end the users session and trigger the data to be fed into the DB.

<%@ Page language="c#" %>

<!DOCTYPE html>
<html>
  <head>

  </head>
  <body>

<div>Session Abandoned</div>
<% Session.Abandon(); %>

  </body>
</html>

Next do something on the site that will cause some tracking to get added to the DB. In my case it was the search. Then go to kill.aspx to force session abondon.

Check the logs. You should see something like this…

25016 11:19:13 DEBUG [Analytics]: The CommitSession pipeline, ProcessSubscriptions is skipped - there is no subscriptions for location id: 4ebd0208-8328-5d69-8c44-ec50939c0967

Check the DB an entry should have gone into a shard db for [xdb_collection].[Interactions] table

To speed up processing, restart the main sitecore application.

Config file transforms with Azure Devops

For a long time now our primary CI setup has been based around Team City and Octopus deploy, but as reliable as it is there are things I don’t like about it:

  1. It’s not a SASS setup meaning there’s a VM to occassionaly think about and updates to install. While Octopus is now availiable as a SASS option, Team City is not and moving Octopus will only solve half the problem.
  2. That VM they both sit on every so often gets and issue with it’s hard disk being full.
  3. It’s complicated to recommend the same setup to clients. You end up having to go through multiple things they need to buy which then require some installation and ongoing maintenance. Ideally we would have a setup thats easy for them to replicate and own themselves with minimal maintenance.

So when we took over a site recently that typically came with no existing CI setup in place, I decided to take a look at using Azure Devops instead. You can use Azure Devops with Octopus Deploy but as it claims to be able to manage releases as well as builds we went for doing the whole thing just in Azure Devops.

Getting a build set up was relatively straight forward so I’m going to skip past that bit, but in short we ended up with a build that will create a web deploy package and publish it as an artifact. Typical msbuild type stuff.

File transforms and variable substitution

The first real tricky point came with replacing variables in config files during a release to each envrionment. We were using the IIS Web App Deploy task to deploy the application to IIS on a VM (no new Azure Web App Services in this setup 😦 as I said we took over the site and this was just to get automated deploys of what they already have). A simple starting point with this is some built in functionality for XML Variable Substituion in the IIS Web App Deploy task.

Quite simply you can add all your varibles to the variable list, set the scope for which envrionment you want it to apply to and the during the deploy they are replaced in your config. Unlike some tag replacement tools I’ve used in the past this one actually uses the name of the connecting string or app setting you need to set, so if you need to set a connection string named web, the variable name will be web.

This is also where my problem stated. The description for what XML variable substitution does is:

Variables defined in the build or release pipeline will be matched against the ‘key’ or ‘name’ entries in the appSettings, applicationSettings, and connectionStrings sections of any config file and parameters.xml

This was a Sitecore solution and for Sitecore most of your config settings are in Sitecores own Sitecore section of the config file. So in other words the connection string will get updated but the rest won’t.

Parameter and SetParameter XML files

My next issue was trying to find a solution is actually quite hard. Searching for this problem either gave me a lot of results for setups using ARM templates (as I said, this was a solution we took over and that kind of change is not on the agenda), or you just get the easy bit above. Searching for Sitecore and Azure Devops also leads you to a lot of results on a cloud infrastructure setup (again not what we’re doing here, at least in the short term). Everything that was coming up felt far more complicated than the solution should be.

However the documentation on the XML variable substitution did have one interesting sentance.

If you are looking to substitute values outside of these elements you can use a (parameters.xml) file, however you will need to use a 3rd party pipeline task to handle the variable substitution.

A parameters.xml file isn’t something I’ve used before which makes this sentance a bit cryptic. The first half says I can do what I want with an xml file, but the second half says I’ll need something else to actually do it.

After a bit of reasearch this all comes back to web deploy. When you do a build that outputs a web deploy package, you get 5 files.

A zip file containing the actual site, a command file which has the script to do the deploy and a set parameters file which is used to set config variables during the deploy. The others aren’t so imporant.

To have different config set on different envrionments you just need to edit the set parameters file. But first you need to have the parameter in the set parameters file so that you can actually change it and this is where the parameters.xml file comes in.

Creating the parameter files

Add a file called parameters.xml file to the root of your project and then add parameters as follows.

<?xml version="1.0" encoding="utf-8" ?>
<parameters>
  <parameter name="DataFolderLocation" defaultvalue="#{dataFolder}">
    <parameterEntry kind="XmlFile" scope="App_Config\\Include\\Z.Project\\DataFolder\.config$" match="/configuration/sitecore/sc.variable[@name='dataFolder']/patch:attribute/text()" />
  </parameter>
</parameters>

Some important parts:

default valueThe value that the config setting will get set to
scopeThe path to the file containing the setting
matchAn XPath expression for find the part of the config file to update

Once you have this the build will start producing a SetParameters.xml file containing the extra parameters.

<?xml version="1.0" encoding="utf-8"?>
<parameters>
  <setParameter name="IIS Web Application Name" value="Default Web Site/SiteCore.Website_deploy" />
  <setParameter name="DataFolderLocation" value="#{dataFolder}" />
</parameters>

Note: I’ve set the value to be something I intend to replace in the release process.

Replacing the tokens

With our SetParameters.xml file now contining all the config we need to update, we need a step in the release process that will replace all the tokens with the correct values.

To do this I used a replaced tokens task https://marketplace.visualstudio.com/items?itemName=qetza.replacetokens

Config options need to be set for:

Root DirectoryPath to the folder containing the SetParameters.xml file
Target filesA list of files to have replacements done in. In our case this was SiteCore.Website.SetParameters.xml
Token prefixThe prefix on tokens to be search for. Ours was #{
Token suffixThe suffix to denote the end of a token. Ours was }

Lastly in the IIS Web App Deploy step the SetParameters file needed to be selected and the new variables added to the variable list in Azure Devops. The variable names need to be called the bit between your prefix and suffix. i.e. #{datafolder} would be called datafolder.

If you don’t set the variables then the log’s will show warning for each one it couldn’t find.

2019-09-24T17:17:21.6950466Z ##[section]Starting: Replace tokens in SiteCore.Website.SetParameters.xml
2019-09-24T17:17:23.9831695Z ==============================================================================
2019-09-24T17:17:23.9831783Z Task         : Replace Tokens
2019-09-24T17:17:23.9831816Z Description  : Replace tokens in files
2019-09-24T17:17:23.9831861Z Version      : 3.2.1
2019-09-24T17:17:23.9831891Z Author       : Guillaume Rouchon
2019-09-24T17:17:23.9831921Z Help         : v3.2.1 - [More Information](https://github.com/qetza/vsts-replacetokens-task#readme)
2019-09-24T17:17:23.9831952Z ==============================================================================
2019-09-24T17:17:27.2703037Z replacing tokens in: C:\azagent\A1\_work\r1\a\PublishBuildArtifacts\SiteCore.Website.SetParameters.xml
2019-09-24T17:17:27.3133832Z ##[warning]variable not found: dataFolder
2019-09-24T17:17:27.3179775Z ##[section]Finishing: Replace tokens in SiteCore.Website.SetParameters.xml

With all this set our config has it’s variables configured within Azure Devops for each envrionment,

Sitecore strange language switching

The other day we started experiencing a strange issue on one of our test sites. Pages were starting to error and looking at the logs the errors were happening in view’s which hadn’t been updated in a long time. Some of these seemed like the code wasn’t robust enough to handle when a datasource hadn’t been set (the classic object not set to an instance of an object error), but fixing these just resulted in an error in another. It also didn’t explain why this suddenly started happening, not to mention the components in question should have all had data.

Then we noticed that on the first view of any page of the site with a new session the site would display fine even though these were server errors happening. Navigating to any other page or refreshing though caused the server error to return.

This led us to look at the differences between the request headers and we had our answer. On all subsequent requests the language context had been changed to some random thing which there is no content for. The site itself is not a multilanguage site and only has content for en. Adding the language code into the URL would force the context language back and the page would work again.

How language works in Sitecore

To understand what is happening it’s good to know how languages in Sitecore work.

Sitecore is built as a platform which can server content in multiple languages. By default you start with one language in the editor (en) but are able to add more. You can read my blog post from a few years ago on how to do this here (https://himynameistim.com/2015/06/30/sitecore-adding-languages-for-a-multilingual-site/).

Sitecore will recognise which language should be displayed based on a language code in a URL. e.g. himynameistim.com/en-gb/ for English – Great Britian. This is done through the strip language pipeline which picks up these languages and sets the context language.

Config for the link manager then controls if links are generated with these language codes in the URL or not. On a single language site you would have this set to never resulting in URL’s without a language code and the default language will be used.

The flaw in all of this though is the strip language pipeline always runs, even when your pages only have one lauange. The pipeline also doesn’t check if the language it finds in the url is set up as a language on the site, so quite a lot of two letter combinations will work and change the language context. When this is changed, it is changed for the users session meaning it is possible for a url to inadvertedly cause the language context to change for a user on the site when the site only has one language.

Disabling Strip Language

As the site in question only has one language the fix is quite simple. For multilingual sites the solution is a bit harder.

For a single language site you can simply turn off the strip language functionality. You can do this using a patch config file as follows:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
 <sitecore>
   <settings>
    <setting name="Languages.AlwaysStripLanguage">
       <patch:attribute name="value">false</patch:attribute>
    </setting>
   </settings>
 </sitecore>
</configuration>

Removing port 443 from urls generated by Sitecore

For as long as I’ve been working on Sitecore there has been this really annoying issue where setting the link manager to include server url and running under https will cause urls to be generated with the port number included. e.g. https://www.himynameistim.com:443/ which naturally you don’t actually want.

Aside: This issue was finally fixed in Sitecore 9.1

To overcome this there are a few methods you can take.

Method 1 – Set the Scheme and Port on you site defenition

This is possibly the smallest change you can make as it’s just 2 settings in a config file.

Setting the external port on site node to 80 (yes 80) tricks the link manager code into not appending the port number as it does it for everything other than port 80.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <sitecore>
    <sites xdt:Transform="Insert">
      <site name="website">
        <patch:attribute name="hostName">www.MySite.com</patch:attribute>
        <patch:attribute name="rootPath">/sitecore/content/MySite</patch:attribute>
        <patch:attribute name="scheme">https</patch:attribute>
        <patch:attribute name="externalPort">80</patch:attribute>
      </site>     
    </sites>
  </sitecore>
</configuration>

What I don’t like about this method though, is your setting something to be wrong to get something else to come out right. It’s all a bit wrong.

Method 2 – Write your own link provider

The second method which I have generally done is to write your own provider which strips the port number off the generated URL.

For this you will need:

1. A patch file to add the provider:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <linkManager defaultProvider="sitecore">
      <patch:attribute
          name="defaultProvider"
          value="CustomLinkProvider" />
      <providers>
        <add name="CustomLinkProvider"
             type="MySite.Services.CustomLinkProvider, 
                         MySite"
                       languageEmbedding="never"
                       lowercaseUrls="true"
                       useDisplayName="true"
                       alwaysIncludeServerUrl="true"
          />
      </providers>
    </linkManager>
    <mediaLibrary>
      <mediaProvider>
        <patch:attribute name="type">
          MySite.Services.NoSslPortMediaProvider, MySite
        </patch:attribute>
      </mediaProvider>
    </mediaLibrary>
  </sitecore>
</configuration>

2. A helper method that removes the ssl port

namespace MySite
{
    /// <summary> 
    /// Link Helper is used to remove SSL Port 
    /// </summary> 
    public static class LinkHelper
    {
        /// <summary> 
        /// This method removes the 443 port number from url 
        /// </summary> 
        /// <param name="url">The url string being evaluated</param> 
        /// <returns>An updated URL minus 443 port number</returns> 
        public static string RemoveSslPort(string url)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                return url;
            }

            if (url.Contains(":443"))
            {
                url = url.Replace(":443", string.Empty);
            }

            return url;
        }
    }
}

3. The custom link provider which first gets the item url the regular way and then strips the ssl port

using Sitecore.Data.Items;
using Sitecore.Links;

namespace MySite
{
    /// <summary>Provide links for resources.</summary> 
    public class CustomLinkProvider : LinkProvider
    {
        public override string GetItemUrl(Item item, UrlOptions options)
        {
            // Some code which manipulates and exams the item...

            return LinkHelper.RemoveSslPort(base.GetItemUrl(item, options));
        }
    }
}

4. The same provider for media

using Sitecore.Data.Items;
using Sitecore.Resources.Media;

namespace MySite
{
    /// <summary> 
    /// This method removes SSL port number from Media Item URLs 
    /// </summary> 
    public class NoSslPortMediaProvider : MediaProvider
    {
        /// <summary> 
        /// Overrides Url mechanism for Media Items 
        /// </summary> 
        /// <param name="item">Sitecore Media Item</param> 
        /// <param name="options">Sitecore Media Url Options object</param> 
        /// <returns>Updated Media Item URL minus 443 port</returns> 

        public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
        {
            var mediaUrl = base.GetMediaUrl(item, options);
            return LinkHelper.RemoveSslPort(mediaUrl);
        }
    }
}

What I don’t like about this method is it’s messy in the opposite way. The port number is still being added, and we’re just adding code to try and fix it after.

Credit to Sabo413 for the code in this example

Method 3 – Official Sitecore Patch

Given that it’s Sitecore’s bug, it does actually make sense that they fix it. After all people are paying a license fee for support! This simplifies your solution down to 1 extra patch file and a dll. What’s better is as it’s Sitecores code they have the responsibility of fixing it, if it ever breaks something, and you have less custom code in your repo.

You can get the fix here for Sitecore version 8.1 – 9.0.

So this may leave you wondering how did Sitecore fix it? Well having a look inside the dll reveals they wen’t for method 2.

Sitecore: Returning a 404 response on the page requested rather than redirecting to a 404 page

Previously I’ve blogged about:

but while looking through the posts today, I realised I had never written about how you stop Sitecore from issuing 302 redirects to your 404 page and instead return a 404 on the URL requested with the contents of the 404 page.

While search engines will recognise a 302 response to a 404 as a 404 (in fact they’re intelligent enough to work out that a 404 page without a correct response status code is a 404) it’s considered SEO best practice for the URL to stay the same and to issue the correct status code.

Creating a NotFoundResolver class

When Sitecore processes a request it will run the httpRequestBegin pipeline, and within that pipeline is a Item Resolver processor that will attempt to find the requested item. If after this the context item is still null then the logic to redirect to the ItemNotFoundUrl will kick in. To stop this happening we can simply add our own process to the pipeline after ItemResolver and set the item.

Our class looks like this:

using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.HttpRequest;

namespace Pipelines.HttpRequest
{
    public class NotFoundResolver : HttpRequestProcessor
    {
        private static readonly string PageNotFoundID = Settings.GetSetting("PageNotFound");

        public override void Process(HttpRequestArgs args)
        {
            Assert.ArgumentNotNull(args, nameof(args));

            if ((Context.Item != null) || (Context.Database == null))
                return;

            if (args.Url.FilePath.StartsWith("/~/"))
                return;

            var notFoundPage = Context.Database.GetItem(new ID(PageNotFoundID));
            if (notFoundPage == null)
                return;

            args.ProcessorItem = notFoundPage;
            Context.Item = notFoundPage;
        }
    }
}

To add our process to the pipeline we can use a patch file like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor
          patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"
          type="LabSitecore.Core.Pipelines.NotFoundResolver, LabSitecore.Core"  />
      </httpRequestBegin>
    </pipelines>
    <settings>
      <!-- Page Not Found Item Id -->
      <setting name="PageNotFound" value="ID OF YOUR 404 PAGE" />
    </settings>
  </sitecore>
</configuration>

Notice the setting for the ID of your 404 page to be loaded as the content.

Remember if you do do this make sure you also follow one of the methods to return a 404 status code, otherwise you will have just made every URL a valid 200 response on your site.

Setting up local https with IIS in 10 minutes

For very good reasons websites now nearly always run under https rather than http. As dev’s though this gives us a complication of either removing any local redirect to https rules and “hoping” things work ok when we get to a server, or setting local IIS up to have an https binding.

Having https setup locally is obviously a lot more favourable and what has traditionally been done is to create a self signed certificate however while this works as far as IIS is concerned, it still leaves an annoying browser warning as the browser will recognise it as un-secure. This can then create additional problems in client side code when certain things will hit the error when calling an api.

mkcert

The solution is to have a certificate added to your trusted root certificates rather than a self signed one. Fortunately there is a tool called mkcert that makes the process a lot simpler to do.

https://github.com/FiloSottile/mkcert#windows

Create a local cert step by step

1. If you haven’t already. Install chocolatey ( https://chocolatey.org/install ). Chocolatey is a package manager for windows which makes it super simple to install applications. The name is inspired from NuGet. i.e. Chocolatey Nuget

2. Install mkcert, to do this from a admin command window run

choco install mkcert

3. Create a local certificate authority (ca)

mkcert -install

4. Create a certificate

mkcert -pkcs12 example.com

Remember to change example.com to the domain you would like to create a certificate for.

5. Rename the .p12 file that was created to .pfx (this is what IIS requires). The certificate will now be created in the folder you have the command window open at.

You can now import the certificate into IIS as normal. When asked for a password this have been set to changeit

Installing Sitecore 9 when you’ve installed 9.1

Installing Sitecore 9 was never the easiest of things, particularly when you compare it to how relatively simple Sitecore 8 was. But if you install Sitecore 9.1 on the same machine and then try your trusty Sitecore 9.0 script you may find it’s got even harder and there’s a bunch of new issues to worry about.

Multiple version of SIF

The first issue your probably going to run into is an error saying a name parameter is missing. Your script hasn’t change, but what has changed is the default version of SIF that’s now running.

So the first change you need to make is to ensure your running the correct version of SIF. You can do this either by adding the command to your script or running this before calling you install script. It will take effect for the duration of your PowerShell session.

#Switch to correct vesion of SIF
Remove-Module -Name SitecoreInstallFramework
Import-Module -Name SitecoreInstallFramework -RequiredVersion 1.2.1

If you want to check what the active version of SIF is you can do this in a PowerShell window using

Get-Command -Module SitecoreInstallFramework | Select-Object -Property name, version

Certificates Error – Part 1

Now we’re calling the right version of SIF, the next issue I encountered was to do with certs. Specifically I got this error:

TerminatingError(New-SignedCertificate): "Cannot process argument transformation on parameter 'Signer'. Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Security.Cryptography.X509Certificates.X509Certificate2"."
Install-SitecoreConfiguration : Cannot process argument transformation on parameter 'Signer'. Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Security.Cryptography.X509Certificates.X509Certificate2".

This is due to the certificate for Sitecore 9.1 that has been installed. You can remove the certificate but then your Sitecore 9.1 install will break instead.

Alternatively add a “RootCertFilename” to the certificate definition:

# Install client certificate for xconnect
$certParams = @{
  Path = "$SCLocation\xconnect-createcert.json"
  CertificateName = "$prefix.xconnect_client"
  RootCertFileName = "SIF121Root"
}
Install-SitecoreConfiguration @certParams -Verbose

Certificate Error – Part 2

This error looks exactly the same as the error above but you’ve already added that Root Cert File Name, so what’s happening now.

Install-SitecoreConfiguration : Cannot process argument transformation on parameter 'Signer'. Cannot convert the
"System.Object[]" value of type "System.Object[]" to type
"System.Security.Cryptography.X509Certificates.X509Certificate2".
At C:\resourceFiles9.0\install.ps1:47 char:1
+ Install-SitecoreConfiguration @certParams -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Install-SitecoreConfiguration

The error is saying that it expected to find 1 certificate but found many instead. Each time you run the script the number of thumbprints also keeps going up.

TBH I’m not overly certain what causes this as most of the time you get the one root cert and your done forever more. But somehow you get a second and then get you in a loop of repeatedly deleting certificates only to discover they still exist somewhere. You know it’s also not the certificate for the 9.1 install as the certificate has the new name you added to it.

For me the issue was although I had deleted them from my personal certificates, trusted root certificates, and even the c:\certificates folder they were being added to, what I needed to do was run this in powershell.

 Get-ChildItem -Path "cert:\LocalMachine\Root" | Where-Object { $_.subject -like "*SIF121Root*" }
  | Remove-Item

If your wanting to find out what certificates are installed on your machine you can run these

Get-ChildItem -Path "cert:\LocalMachine\Root" | Format-Table Subject, FriendlyName, Thumbprint
Get-ChildItem -Path "cert:\LocalMachine\My" | Format-Table Subject, FriendlyName, Thumbprint
Get-ChildItem -Path "cert:\CurrentUser\My" | Format-Table Subject, FriendlyName, Thumbprint

Make sure your config is actually correct

This one is really just my mistake. Multiple versions of Siteocore have meant rather than just having one “C:\reousrcefiles\” folder on my machine (as per instructions), I now have a few with the version post-fixed to the end. It only becomes apparent that the path in the install.ps1 file is wrong part way through the install process.

What I learnt at Sugcon 2019

This year Sugcon came to London which given that’s where I’m based is awesome for me. In total it was a 3 day conference starting with Sitecore Experience aimed more at marketers than developers. As a developer I only went to the 2 developer days, so for your benefit here’s a summary of everything I saw.

Day 1

Day 1 started with a keynote, sadly life got in the way and I missed the first few hours. I’m told it was good though.

After that the day was split into a mix of sessions in the big room for all and smaller break out sessions where you could pick 1 of 4 to attend.

JSS Immersion – Lessons learned and looking ahead with Anastasiya Flynn

To kick things off I went to a talk on JSS, mostly because JSS is a subject I know very little about. This was something that became even more apparent as the talk went on! At the end of it I came away with an appreciation that I need to invest some time in learning a lot more, but my other take away was a few links on things that will help me out if I ever try some React stuff.

https://www.styled-components.com

https://www.react-spring.io

PAAS It on: Learning’s from a year on Sitecore with Criss Titschinger

Criss works as a dev opps person and over the last year went on the journey of having a Sitecore 8.2 install upgraded to 9 using a fully cloud architecture in Azure.

Overall his experience sounded positive but he did have a few warnings from pain he experienced:

  • Beware of cold start up times with web apps. These can be a real performance hit, especially when Azure decides its going to move your web app instance
  • Web app slots share processing usage so when your warming one up, your live one is taking a hit. If you run on the edge of capacity, this will be an issue
  • Azure search is easy to install but it has a field limitation of 1000 to watch out for
  • Data migration in an upgrade takes a long time the second time. It took 9 days to migrate a years data from mongo! Only do it once.
  • Run your upgrade on clean instances and do the code in visual studio.
  • Web apps need to be on the premium service plan. The others are to weak
  • Use elastic pools for your database to save money. The microservice architecture introduces a LOT of new dbs which are going to cost money in azure resources. Most of the time they also don’t do that much so put them in a pool to share resources
  • Moving to 9 is going to increase hosting charges. Be honest with clients about it.

Day 2

On Day 2 I got to attend from the start so it was a much fuller day for me.

10x your Sitecore development with Mark Cassidy

The day started with a talk on questioning how long it should take to build a Sitecore site. It was a question that never really got answered but the main thing Mark really raised was, do we over engineer what we do and would simpler actually be enough? He went on to show a time lapse video of himself implementing a bootstrap template in Sitecore which took 15 hours.

To build this site he didn’t install any modules (no glass) and used just the standard Sitecore api. As he pointed out, it was all stuff that could be done by a dev with only the basic Sitecore training, which as there’s a short supply of devs in the world, we can potentially make better use of who does what.

Extending and implementing cloud architectures with Rob Habraken

After one talk on cloud the day before I almost gave this one a miss, but I’m glad I didn’t.

Rob gave us some of his learning’s and things to look out for. As the the previous session the theme of Sitecore 9 becoming far more complex came up and he had some interesting takes on it:

  • Use what you need, disable roles that you don’t. I see plenty of Sitecore customers not making use of all the features, and when your in a microservice architecture it does raise the question of why even have this stuff turned on. If you don’t use marketing automation then you don’t need the role running. It’s just costing money to do nothing.
  • Scale down when your not using resource. Unlike a VM web apps can not be turned off so they always cost money. You can delete and recreate, but that’s a pain. Instead set up a pipeline to scale them to the lowest resource setting when not being used.
  • He went on to discuss and show how we can use azure functions and logic apps to implement our code rather than building into the main Sitecore project. However you should be careful overdoing it as it can become complex quickly and it’s easy to end up with a massive unorganised list of individual azure functions.

Automated personalisation with Chris Nash and Niels Kuhnel

Chris and Niels pointed out the flaw in Sitecores reporting on personalised content. How do we know the rate each converts to a goal at? There’s the A/B Test report’s but that’s not quite the same thing.

They went on to show how they had started measuring the display impressions and click through on personalised content. Then linking the results collected in the reporting db up to a Power BI dashboard.

Sitecore identity: A new Sitecore authentication mechanism with Himadri Chakrabarti

Himadri gave us a look at the new Identity Server framework in Sitecore 9.1:

  • Identity server 4 framework
  • Still uses old asp net membership provider underneath
  • Can work with sub providers like Azure

Measure if you want to go faster with Jeremy Davis

Jeremy was in the situation where a site they were developing would have TV adverts during one of the most watch programs on British TV. Naturally he got scared and went looking for tools to help with performance. He told us about two of them:

  • Sitecore debug tool in experience editor showing the time it takes for components to load.
  • Using Visual Studio debugger to monitor processor usage and memory usage.

Both of these tools are very good at pointing you in the direction of smelly code and the best part is you already have them.

Unfortunately it’s the kind of demo that really doesn’t convert to text to write here.

We released JSS, you’ll never guess what happened next with Adam Weber & Kam Figy

Adam and Kam showed us JSS working with SXA and Sitecore Forms. As mentioned before I don’t know much about JSS but after this talk I’m convinced I definitely need to.

Right now it doesn’t sound like I would make a site using it, but it could definitely be the future of how we build sites.

The stand out thing is being able to keep your Sitecore install unmodified which would essentially lead us to a real SAAS solution where a Sitecore instance could be spun up from the marketplace and then all other functionality added through server-less functions and a headless front end.

ASP.NET Core Platforms for a Blog

Like a lot of Sitecore developers my blog (at time of writing) is hosted on WordPress. The reason for it not being in Sitecore is simple. Sitecore is an enterprise level platform, which isn’t really needed for a personal blog.

For a .net dev to have there blog on a php platform however just seems plain wrong, but again there’s a logical reason. WordPress is actually really good as a blogging platform, and it doesn’t cost me anything.

Despite this I would much rather take control of my site and use it to play with all the cool features in Azure. It would also be nice to have the ability to do something about the Google PageSpeed result which is currently sitting at 24%. So in aid of this I’ve started looking into .net core based platforms and thought I’d share what I’ve found.

Miniblog.core

https://github.com/madskristensen/Miniblog.Core

As the name suggests Miniblog.core is both very small and based on .net core. Developed by Mads Kristensen its an extremely lightweight bare bones implementation, which if your after something you can help build upon is ideal. The code is straightforward to understand and very simple to adapt. Additionally if your after a 100% page speed score, then this achieves just that.

If on the other hand your after a deluxe admin experience full of functionality then this probably isn’t for you.

Piranha CMS

http://piranhacms.org/

Piranha CMS is built as a lightweight CMS platform rather than specifically as a blog, however it also contains a blog module which for me put’s it at a big advantage over the other CMS platforms I’ve listed below.

On the back end you get a choice of SQL Server, SQLite or MySQL. The documentation isn’t exactly complete, but on the day I tried it out, I found the team building it very responsive on GitHub. They even updated the documentation with one of my suggestions the very next day.

Another aspect I particularly liked about Piranha CMS was it’s block editor, which from the brief look I’ve had so far reminds me of the block editor Umbraco has. Whereas other platforms in this list were restricted to a large rich text field.

Orchard Core

https://github.com/OrchardCMS/OrchardCore

Orchard Core is the dot net core version of the Orchard CMS. It’s currently in beta, but I’m not sure that put’s it at much of a disadvantage over the others on this list.

My initial impressions of Orchard Core however weren’t as high as Piranha CMS. The admin interface wasn’t quite as nice and as far as I could tell, it didn’t have anything like Piranha’s block editor. The solution itself also seemed far more complex and I wasn’t certain what I got for this. I expect Orchard Core is likely better in some ways that I have yet to discover, but for my needs as a blog this is probably not the case. It also didn’t have a blog module out of the box.

Squidex

https://squidex.io/

I have’t had much of a chance to play with Squidex yet, but it does offer an interesting difference to the others mentioned so far.

For a start Squidex is an entirely headless cms, and is built around the concept of CQRS and Event Sourcing. Unlike the others it also uses MongoDB rather than a SQL based database.

Where MongoDB is concerned, I often get the impression people are using it because as developers we tend to have a preference to using something new rather than something adequate. However when it comes to Azure pricing, there is potentially a saving to be made by using Mongo rather than Azure SQL.