Scary 404

It’s almost Halloween and I’ve been haunted by this scary 404 – Page not found error all of last week. This happened on our UAT environment which had 1 CM and 2 CDS. The error happened quite at random. To complicate things further, this was related to EXM, where a custom Unsubscribe Confirmation Page would not show up when the email recipient hits the “Unsubscribe” Option on the Email. More about the implementation in the blog series

Sitecore support to the rescue, here’s the fix. I had to disable the following settings from Sitecore.Config file on each of the CDs.

CD Config

I added a patch to out deployment process to reset the settings on the CDs and it worked!

Here’s how

Patch Code

And that’s all for today!

Install Sitecore 10 in 10 using SIF

Sitecore 10.0 Released: A Great Day for Sitecore Developers

Sitecore 10 is here and I believe this is the first time ever I proactively installed the latest greatest Sitecore version within 10 days of its release. Sitecore 10 within 10. Isn’t it quite a catchy phrase!

Allright, here how to install in 10 easy steps!

For sometime now, we have both the options SIF and SIA. We also have the container deployment package. Today, I’ll go the SIF way simply because I love Powershell ๐Ÿ™‚

Step 1 – Login to the download site with your credentials and download the following.

The Installation Guide –

Quick Installation Guide

Packages for XP Single –

Step 2 – READ THE DOCUMENT! Why? Because I did not and after progressing quite far, got this nasty error.
Error: ALTER DATABASE statement failed.

Okay, At least read the highlighted sections.

I had 9.3 Installed on the same machine and the ONLY additional requirement was SQL Server 2017. I had missed that and hence the above error. Actually when I scrolled up, I did see a warning.

Nevermind, let us proceed with the good stuff.

Step 3 – Install Sitecore Installation Framework (SIF) – Sitecore Experience Platform 10 requires SIF 2.3.

The Sitecore Gallery is a public MyGet feed where you can download and install PowerShell modules
created by Sitecore, including SIF.
To set up SIF:

  1. In Windows, open PowerShell as an administrator.
  2. To register the repository, in a PowerShell command line, run the following cmdlet:
Register-PSRepository -Name SitecoreGallery

When prompted to install, press Y, and then press Enter.

  1. To install the PowerShell module, run the following cmdlet:
Install-Module SitecoreInstallFramework

When prompted to install, press Y, and then press Enter.

  1. After you install SIF, you can validate the installation to conศดrm that it is available for use.
Get-Module SitecoreInstallFramework โ€“ListAvailable 

The versions of SIF that are compatible with Sitecore Experience Platform:

Sitecore Experience Platform VersionSIF Version
9.0.x 1.2.1 or later or later

To install a specific version of SIF, Run the following cmdlet:

Install-Module -Name SitecoreInstallFramework -RequiredVersion x.x.x

Enter the appropriate value in the RequiredVersion parameter.

To run a specific version of SIF, Run the following cmdlet:

Import-Module -Name SitecoreInstallFramework -Force -RequiredVersion x.x.x

You use the specific version for the remainder of the session. The next time you start a PowerShell session it automatically uses the latest available version.

Step 4 – Install Solr as a service

The required version is 8.4. Follow the steps in the Blog to get this done.

Step 5 – Install Single Developer Topology (XP0) using SIF

Follow the Chapter 5 of the Quick Installation Guide mentioned in Step 1.

Consider the step 2 of section 5.4 where we need to make necessary changes in the Powershell script. My settings are shown here.

You will have to update the following values

$PrefixThe Prefix that will be used on SOLR, Website and Database instances.“scTen”
$SitecoreAdminPasswordThe Password for the Sitecore Admin User. This will be regenerated if left on the default.“ten1!”
$SolrUrlThe URL of the Solr Server. Ensure the correct URL especially if you have multiple instances of the Solr service.https://localhost:8984/solr”
$SolrRootThe Folder that Solr has been installed to“C:\Solr\solr-8.4.0solr-8.4.0”
$SolrServiceThe Name of the Solr Service.“solr-8.4.0solr-8.4.0”
$SqlServerThe DNS name or IP of the SQL Instance for SQL 2017. Ensure the correct instance. You may have SQL 2016 on the same machine.“RPATWARI-PK\MSSQL2017”
$SqlAdminUserA SQL user with sysadmin privileges“sa”
$SqlAdminPasswordThe password for $SQLAdminUser“Password1”

Step 6 – Success!

If all goes as expected, this is how it looks.

Step 7 – Browse to the Site.

Step 8 – Our Favorite End ๐Ÿ™‚

Step 9 – If you need to Uninstall, simply toggle the command in the XP0-SingleDeveloper.ps1 script file as shown here.

Step 10 – Enjoy the experience!

Quotes about Enjoying the experience (40 quotes)

Customize EXM Final Confirmation Page.

In this Part 1 of the three series blog on extending the EXM Unsubscribe functionality, we shall walk though the steps required to customize the Final confirmation page with additional information about the Contact’s email Id and the Campaign from which the contact is being unsubscribed. Sounds easy ? Aha! You are about to learn.Okay, let us start from the basics. Final Confirmation Page is the Page that is rendered after the contact’s email is excluded from the list.

As part of the Email root configuration we set a custom Final Confirmation Page as shown here. Follow the sitecore documentation for configuration of this along with the other settings.

Final Confirmation Page

Here are the steps.

  1. Build the Page in the standard MVC model using Rich-Text Editor tokens as placeholders for the Values. Rich Text Editor tokens ? Read this interesting blog.
  2. Copy the default \sitecore modules\Web\EXM\Unsubscribe.aspx into <Project Path>\CustomUnsubscribe.aspx.
Default Unsubscribe.aspx
  1. Replace the default implementation by your custom CustomUnsubscribe.aspx.cs. (Use Dotpeek to look into the implementation of the code for UnsubscribeContact)
Default Unsubscribe code.
  1. In the UnsubscribeContact() code, modify the return URL with Querystring of custom values. In the following image I have the querystring parameters, EmailId and ListName.
Modified implemenation with Querystring Parameters
  1. Swap the default to your custom implementation in the Email Footer settings.
Footer changed to CustomUnsubscribe Page

That’s it for today. Feel free to reach out for any queries. See you all next week with Part 2 : List Exclusion use case for Automated EXM messages.

Extending EXM Unsubscribe

Been reading a lot since march on a variety of things and one of them is

The Life-Changing Magic of Tidying Up: The Japanese Art of Decluttering and Organizing by Marie Kondo.

Okay, where do i start? Lets start from my personal mailbox. It’s really time to unsubscribe from emails from local businesses in Edinburgh! (Lived there 5 years ago). It was a very enabling experience letting go of unwanted subscriptions and seeing the goodbye messages. So that got me to write this blog to share my experiences on customizing EXM Unsubscribe.

Sitecore provides a good working feature out of the box. My good friend Pete Navarra has explained very elaborately in the series 25 Days of Sitecore EXM.

In the following weeks I will guide you through the following implementations.

Part 1 : Adding custom information to Final Confirmation Page.

Part 2 : List Exclusion use case for Automated EXM messages.

Part 3 : Unsubscribe in scaled CM/CD environment using message bus.

Lets Unsubscribe!

Language Falling for you…

Once upon a time we spoke just English and then we made new friends across the time zones and geographical borders. Now we are trying to learn their language and get to know better. This is taking time and we still use English in some instances to avoid an awkward pause in dialogue.

Yes, I am still speaking Sitecore (v9.0.2) and the big topic today is Language Fallback and particularly the process of enabling new language versions to automatically have all the predefined fallback language’s content. Sitecore provides a great feature of Language Fallback for content to automatically render in the defined fallback language for elements not having a intended language version.

The enabling is a 3-step process.

One…..Enable Item Fallback for all the template standard values to ensure new content automatically renders in the defined fallback language. For this I used a script as detailed by Shu Jackson in

Two…..Enable Item Fallback for all existing content.This was necessary in for our client’s existing sites. I wrote a powershell extension script, for accomplishing the task.

More about Powershell Extensions here

Three…..So tempted to write last but not the least (:rolling eyes ๐Ÿ™‚ the background task of actually moving the final renderings to the new language version. This proved exceptionally useful for our use case of what we call “Experience Blocks”. Somewhere on the lines of Jeff Darchuk’s blog post

For this I trapped the item:versionAdded event and copied the Final Layout renderings to the new version.

I will not bore you with the details of patching your code into the pipeline etc. etc. (I’m sure you know it all ๐Ÿ™‚

Hope this helps!

Testing Marketing Automation

I am knee deep in the waters of Marketing automation working on providing a custom solution. The solution binds all the what I would like to call building blocks of automation in a nice package.. The blocks are the Lists, Custom emails, Forms with submit-actions, Landing Pages, and Goals.

In this blog I want to highlight my lessons learnt and provide an idea to re-use existing pool of test users. Don’t tell me you never heard of test users !!

What did I learn ?

  1. Have a high level design of the automation plan. Literally go old fashioned and put it on paper. That way it will be easier to envision and identify the building blocks. Having it on paper will help you get all the building blocks ready before you start the campaign design and minimize the number of times you need to step away to get something. (I need to get away for a cup of coffee now ๐Ÿ™‚
  2. Okay, I’m back and here’s the one. Let’s talk about enrollment. I have done MA in 9.0.2 and 9.3 and need I say 9.3 was such a BIG relief with the Auto enrollment feature. (That’s made it’s entry from 9.1). Well, most of our customer base is on 9.0.2, so it was a necessity to explore creative enrollment ideas. I used goal triggers, with IsLiveEvent option checked such that it immediately triggers the campaign without waiting for the session to end. This worked in enrolling the contact as soon as the submit button with submit-action to trigger the goal was hit!
  3. Provide meaningful, descriptive names for your campaign activities. Campaigns can quickly grow complex and when you revisit your campaign with a long life-line, it makes it much easier to follow it through. Say a goal listener can be named “Registered for Meeting “.
  4. Keep it simple! Heard that one before? Yes, split up a campaign into multiple campaigns and logically enroll contacts from one to other.

Well, all said and done, automation campaigns are not made in a day and go through various steps of exploration. I had several sleepless nights and restless days before got that first campaign out in all its glory. One of the biggest challenge was testing on QA and UAT environments. I had to test the whole scenario end-end with real world applications like Outlook with a pool of test users. With the version of Sitecore (9.0.2) I did not have the luxury of unlimited enrollment, so had to work out a work-around to clean-up the enrollment and repeat.

Basically, behind the scenes, the enrollments are stored as ContactFacets in the XConnect shard databases. <prefix>_Xdb.Collection.Shard0 and <prefix>_Xdb.Collection.Shard1. I wrote this SQL Query to erase the enrollment for a contact from both the databases. This could be converted into a StoredProcedure for Automated testing. Also include in the FacetKeys clause any custom facets you need to cleanup for the contact.

  FROM [<prefix>_Xdb.Collection.Shard0].[xdb_collection].[ContactFacets]
  where FacetKey in ('AutomationPlanEnrollmentCache','AutomationPlanExit','Classification','EngagementMeasures','ExmKeyBehaviorCache','InteractionsCache','KeyBehaviorCache')
  AND ContactId in (
  SELECT ContactId from [<prefix>_Xdb.Collection.Shard0].[xdb_collection].[ContactIdentifiers]
  where Identifier = CONVERT(VARBINARY(MAX), '') 
  AND Source = '<Source>')

  FROM [<prefix>_Xdb.Collection.Shard1].[xdb_collection].[ContactFacets]
  where FacetKey in ('AutomationPlanEnrollmentCache','AutomationPlanExit','Classification','EngagementMeasures','ExmKeyBehaviorCache','InteractionsCache','KeyBehaviorCache')
  AND ContactId in (
  SELECT ContactId from [<prefix>_Xdb.Collection.Shard1].[xdb_collection].[ContactIdentifiers]
  where Identifier = CONVERT(VARBINARY(MAX), '') 
  AND Source = '<Source>')

So that’s it from my explorations this week! Hope it helps ๐Ÿ™‚

Sitecore Marketing Automation – Database Owner Error

Today I was tormented by my perfectly working Marketing automation solution suddenly ignoring me! The Campaign would simply not respond to my goal. Well, it was perfectly working on other environments but was my first time on an UAT environment. (Sitecore 9.0.2)

Troubleshooting hat on and I start with the logs. First thing that seemed unusual was the size of the log files. What the heck was it doing to bloat them to six-digit sizes! (Six digits look great on salaries, not on logs ๐Ÿ™‚

This image has an empty alt attribute; its file name is image-2.png
Bloated log sizes

When I checked the most recent one, I see the following error in a loop.

This image has an empty alt attribute; its file name is image.png

Cannot execute as the database principal because the principal “dbo” does not exist

I checked my database by running the following query and that came with nothing.

SELECT [name],[sid]
FROM [<prefix>_MarketingAutomation].[sys].[database_principals]
WHERE [name] = 'dbo'

I realized that my database were restored from production and hence may not have the security principal ‘dbo’ in the database. So now that you know the problem, fixing it is easy.

I ran the following query to assign it the ‘sa’ user as the DBO.

USE [<prefix>_MarketingAutomation] 
EXEC sp_changedbowner 'sa'

This can also be done through the UI. Right click on the <MarketingAutomation > database -> Files ->Owner->Select Database Owner -> Browse for Objects -> Select [sa] -> OK

Troubleshooting Sitecore 9.3 docker & Habitat Installations

Sitecore/Habitat on Docker

In the last few weeks we are all in the middle of a serious existential crisis with the onset of the pandemic COVID-19. Information is bombarding us from all avenues and sources and it is proving challenging to ignore our fears and stay calm during this overwhelming time.

As for me, I decided to focus on things I can comprehend rather than get distracted by stuff I cannot. Luckily for us technologists, there is never a dearth of mysteries to unravel!

In this blog, I am going to share my work-around some of the hurdles in my installation journey for both Sitecore docker and docker for Habitat 9.3.

First, I like checklists and here’s one about my best practices.

  1. On this date, both the Project are in a fairly stable stage and the best practice is to keep visiting the repositories for document updates and fixes.
  2. Pay close attention to Optional modules.. It is fairly tedious to update modules, so start with having them all. E.g. Sitecore Docker Installation dependency Sitecore Publishing Module rev. r00546.2197.scwdp. You will have to do download it explicitly and place it in the Packages folder. After downloading, make sure the file name matches the json file’s entry. You may have to add the <.scwdp> to complete it.
  3. This may sound cliche, but really read the Readme’s. The projects are evolving and the teams are doing an awesome job in keeping them updated. Kudos!

So lets get started. And what better place than the sources.

Docker Images


Well if you do not, then you will be running in circles.

Well, if all “does not” go well, here’s the list of issues you may have and the working resolution tips.

Troubleshooting Sitecore Docker Installation

Issue #1 – Download timeout

Occurrence – during the following command execution for any of the downloads.

.\Build.ps1 -SitecoreUsername "YOUR USERNAME" -SitecorePassword "YOUR PASSWORD"

Resolution – Copy the stuck link similar to ‘; and browse to it. Download will automatically start and once done, copy the package into the \docker-images\Packages folder.

Issue #2 – Hash Error

Occurrence – during the following command execution for any of the downloads.

.\Build.ps1 -SitecoreUsername "YOUR USERNAME" -SitecorePassword "YOUR PASSWORD"
Hash Error

Cause – Remember I said, read the Readme! Well, I missed out an important instruction. “….Download any missing packages into .\packages, if you have another location with files already present you can call Build.ps1 with the parameter -InstallSourcePath.

After getting stuck a number of times during downloads, I downloaded the packages directly and placed them in the packages folder. This is when the hash check would fail.

Resolution – Use -InstallSourcePath if you already have the packages. Complete list of packages is in the file \docker-images\sitecore-packages.json

Issue #3 – curl: (6) Could not resolve host :

Occurence – During the command execution of  

docker-compose -f docker-compose.xp.yml up

DNS Resolve

Cause – This happens due the DNS resolution is failing for docker images.

Resolution – Adding the following to the docker daemon configuration fixed the above issue.

DNS Resolve

Issue #4 – Cannot start service <>: network <> not found

Occurence – During the command execution of  

docker-compose -f docker-compose.xp.yml up

Compose Error

Causes – This could be due to images stuck.

Resolution – Try restarting the docker, and if they are still stuck, do the following steps.

Step 1 – list all the containers with the command

docker container ls -a

Step 2 – Remove the problem container id with the command

docker container rm <containerid>

Execute the docker-compose -f docker-compose.xp.yml up and this time the containers will be up again.

Troubleshooting Habitat Installation

Issue #5 – Could not load file or assembly

Could not load file or assembly ‘file:///C:\sat\tools\Sitecore.Cloud.Cmdlets.dll’ or one of its dependencies.

Occurrence – During the running of the command

.\build.ps1 -DeploymentTarget DockerBuild

Resolution – Following the suggested reply from this post helped me resolve.


Issue #6 – The path…\\Data Exchange Framework…either does not exist or….

Occurrence – This occurred during the running of the step

docker-compose build -m 8G

Root cause – Raised a request for the team.

Work around – Use the command

docker-compose -f docker-compose-vanilla.yml up -d 

Wish you luck for a successful installation!

Sitecore 9.3 Installation Error

I was at Sitecore Symposium 2019 where the launch of the feature rich 9.3 version was announced and I couldn’t wait to get my hands on the cool stuff. Well, finally (rolling eyes…well, life a.k.a project duties came in my way) I got around to install it last week. The Installation was not without hiccups, blame it on my ancient brick of a work laptop and the myriad of existing versions of prerequisites on it.

Started with downloading the fundamental standalone version (Setup XP0 Developer Workstation 1.1.1-r4) package and was completely over the moon seeing the Setup-exe with the ever so familiar Sitecore logo on it!

Obviously, I am not the first one out there to try it so there are a number of great blogs and DIY guides out there.

My favourite one is

Followed it to the word and it all seemed to go well, until I hit this.

This image has an empty alt attribute; its file name is image-2.png

Ah..well, so after some disappointment, I rolled up my sleeves and started looking under the hood. to find the cause of the error

The property ‘Value’ cannot be found on this object. Verify that the property exists.

The logs indirectly pointed me to the root cause, being the version of SIF was not correct.

Again, back to googling and found this helpful blog to fix the above cause.

Hope it helps someone out there!