Jacob Pretorius
+8
Sep 12, 2022
visibility 2482
star star star star star
(6 votes)

Waving To Advance Over the Sunset

While doing some maintenance work for a client we noticed that on solution startup there were some DNS failure messages for the URL “emea.epcc.episerver.net” and looking at their appSettings.config it is clear where this comes from.

<!-- EPiServer Advance -->
<add key="episerver:RecommendationServiceKey" value="secret" />
<add key="episerver:RecommendationServiceSecret" value=" secret" />
<add key="episerver:RecommendationServiceUri" value="https://emea.advance.episerver.net/" />
<add key="episerver:CmsCloudSynchronizationKey" value=" secret" />
<add key="episerver:CmsCloudSynchronizationSecret" value=" secret" />
<add key="episerver:CmsCloudSynchronizationUri" value="https://emea.epcc.episerver.net/" />

However, getting to the bottom of these turned out to be quite a rabbit hole. Join me as we explore it.

Optimizely product names and package names in bold.

Episerver Advance

Optimizely support let us know that Advance has been sunset, and pointed us to this document of retired products https://world.optimizely.com/contentassets/c1670edf938343f6b1b1076ece6abea6/episerver-discontinued-services-and-products---20200701-distro.pdf

Some sunset Episerver products (Including Advance - December 2020)

Okay great, we can get rid of it. But what was it and which packages were a part of it? Of course, there isn’t a nice and easy Episerver.Advance package to uninstall and be done with it.

The Advance product announcement post in World by Joakim mentions installing the packages Episerver.Personalization.CMS and EPiServer.Tracking.PageView.

https://world.optimizely.com/blogs/joakim-platbarzdis/dates/2018/4/episerver-advance-launched/

 

We can also see in this article which is in a “CHECKED” category, whatever that means, that it needs the same packages https://world.optimizely.com/documentation/developer-guides/archive/personalization/checked/advance-api/

 

Let’s now drill into the packages a bit. There doesn’t seem to be any existing proper documentation around these anymore so time to spin up the decompiler.

First starting with the packages for Advance.

Episerver.Personalization.CMS – safe to remove
This uses the connections strings matching the URL section “emea.advance.episerver.net”. This URL does not have DNS mappings and is now non-functional.

namespace EPiServer.Personalization.CMS
{
  [InitializableModule]
  [ModuleDependency(typeof (ServiceContainerInitialization))]
  public class InitializationModule : IConfigurableModule, IInitializableModule
  {
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
      context.Services.AddSingleton<IHttpClientFactory, HmacHttpClientFactory>();
      string applicationKey = ConfigurationManager.AppSettings.Get("episerver:RecommendationServiceKey");
      if (string.IsNullOrEmpty(applicationKey))
        throw new ConfigurationErrorsException(string.Format("AppSetting '{0}' is required for recommendation service", (object) "episerver:RecommendationServiceKey"));
      string secret = ConfigurationManager.AppSettings.Get("episerver:RecommendationServiceSecret");
      if (string.IsNullOrEmpty(secret))
        throw new ConfigurationErrorsException(string.Format("AppSetting '{0}' is required for recommendation service", (object) "episerver:RecommendationServiceSecret"));
      string baseUri = ConfigurationManager.AppSettings.Get("episerver:RecommendationServiceUri");
      if (string.IsNullOrEmpty(baseUri))
        throw new ConfigurationErrorsException(string.Format("AppSetting '{0}' is required for recommendation service", (object) "episerver:RecommendationServiceUri"));
      // Snip
    }
  }
}

Episerver.Personalization.CMS.UI – safe to remove
Same as above.

namespace EPiServer.Personalization.CMS.UI
{
  [InitializableModule]
  [ModuleDependency(typeof (ServiceContainerInitialization))]
  public class InitializationModule : IConfigurableModule, IInitializableModule
  {
    private void SetupStrategyConfiguration(ServiceConfigurationContext context)
    {
      string applicationKey = ConfigurationManager.AppSettings.Get("episerver:RecommendationServiceKey");
      if (string.IsNullOrWhiteSpace(applicationKey))
        throw new ConfigurationErrorsException("AppSetting 'episerver:RecommendationServiceKey' is required for recommendation service");
      string secret = ConfigurationManager.AppSettings.Get("episerver:RecommendationServiceSecret");
      if (string.IsNullOrWhiteSpace(secret))
        throw new ConfigurationErrorsException("AppSetting 'episerver:RecommendationServiceSecret' is required for recommendation service");
      string baseUri = ConfigurationManager.AppSettings.Get("episerver:RecommendationServiceUri");
      if (string.IsNullOrWhiteSpace(baseUri) || !Uri.IsWellFormedUriString(baseUri, UriKind.Absolute))
        throw new ConfigurationErrorsException("AppSetting 'episerver:RecommendationServiceUri' is required for recommendation service");
      string s = ConfigurationManager.AppSettings.Get("episerver:RecommendationServiceCacheDuration");
      // Snip
    }
  }
}

Episerver.Personalization.CMS.Core – safe to remove
Same as above.

namespace EPiServer.Personalization.CMS.Core
{
  public static class Constants
  {
    public const string ApplicationKey = "episerver:RecommendationServiceKey";
    public const string Secret = "episerver:RecommendationServiceSecret";
    public const string BaseUri = "episerver:RecommendationServiceUri";
    public const string CacheDuration = "episerver:RecommendationServiceCacheDuration";
    public const int DefaultStrategyCacheDuration = 5;
    public const string DefaultStrategyValue = "251679C7-088F-45EE-8B39-238F73C7C5C6";
    public const int RecommendationItemBufferNumber = 5;
  }
}

EPiServer.Tracking.PageView - keep!
This is an interesting one. While as part of the Advance installation steps they recommended installing this package, it doesn’t relate directly to the Advance product. Instead, it relies on the Profile Store product internally.

Profile store is a dependency of EpiServer.Tracking.PageView

Which in turn uses a different set of connection strings.

namespace EPiServer.Profiles.Client
{
  [ServiceConfiguration(Lifecycle = ServiceInstanceScope.Scoped, ServiceType = typeof (IProfilesTrackingConfiguration))]
  public class ProfilesTrackingConfiguration : IProfilesTrackingConfiguration
  {
    private const string TrackingApiBaseUrlKey = "episerver:profiles.TrackingApiBaseUrl";
    private const string SubscriptionIdKey = "episerver:profiles.TrackingApiSubscriptionKey";
    private const string ProfileStoreTrackingEnabledKey = "episerver:profiles.ProfileStoreTrackingEnabled";
    // Snip
  }
}

 

The Profile Store is crucial for good recommendations so this package should be kept along with the PageView tracking package.

Removing the three personalisation packages above is required when removing the connection string values otherwise there is a runtime error preventing startup, as these packages need the connection strings in place to allow the solution to run.

Cloud Synchronization

Now for the URL we see in the startup error logs, https://emea.epcc.episerver.net/ which is used by EPiServer.Cms.CloudSynchronization

namespace EPiServer.Cms.CloudSynchronization
{
  public static class ConfigurationSettings
  {
    public const string ApplicationKey = "episerver:CmsCloudSynchronizationKey";
    public const string Secret = "episerver:CmsCloudSynchronizationSecret";
    public const string ServiceLocation = "episerver:CmsCloudSynchronizationUri";
    // Snip
  }
}
  • I also found by exploring the code for it that it added the “Cloud Provisioning” scheduled job
namespace EPiServer.Cms.CloudSynchronization.Internal
{
  [ScheduledPlugIn(Description = "Connects to a cloud service and uploads entities such as content, categories, sites etc. Job should be run one time upon installation and should not be scheduled since changes are incrementally synchronized as soon as they occur.", DisplayName = "Cloud Provisioning", GUID = "3FAF1A3C-DF44-4FB3-9F47-B9C3B2A36020")]
  [ServiceConfiguration(IncludeServiceAccessor = false)]
  public class CloudProvisioningJob : ScheduledJobBase
  {
    public const string JobId = "3FAF1A3C-DF44-4FB3-9F47-B9C3B2A36020";
    private static readonly ILogger Logger = LogManager.GetLogger();
    private readonly IEnumerable<IProvisioningJob> _syncJobs;
    private CancellationTokenSource _cancellationTokenSource;

    public CloudProvisioningJob(IEnumerable<IProvisioningJob> syncJob)
    {
      this._syncJobs = syncJob;
      this.IsStoppable = true;
    }
    // Snip
  }
}
  • Running it leads to an error since the URL also does not have DNS mappings anymore.




 

It should be safe to remove this along with the three Advance personalisation packages and the related connection strings for both products. (Please leave a comment with what it did if you know as I'm burning to find out)

Content Recommendations

Advance has been superseded by the Content Recommendations product. Which uses the package EPiServer.Personalization.Content.UI and is working well in our client’s solution. Connection strings below for interest sake.

namespace EPiServer.Personalization.Content.UI.Configuration
{
  [Options]
  public class ContentPersonalizationOptions
  {
    public string Environment { get; set; }
    public string ClientId { get; set; }
    public string ClientName { get; set; }
    public string ApiToken { get; set; }
    public bool DisableDefaultTracking { get; set; }
    internal void Validate()
    {
      if (string.IsNullOrEmpty(this.Environment) || string.IsNullOrEmpty(this.ClientId) || string.IsNullOrEmpty(this.ApiToken) || string.IsNullOrEmpty(this.ClientName))
        throw new ConfigurationErrorsException("Episerver Content Personalization requires the following properties to be set: episerver:personalization.content.Environment, episerver:personalization.content.ClientId, episerver:personalization.content.ClientName and episerver:personalization.content.ApiToken are set in the appSettings section.");
    }
  }
}

Conclusion

I hope this helps anyone having to do the same kind of maintenance/clean-up work as it was quite shocking to me how little information I could find about these packages online.

Sep 12, 2022

Comments

error Please login to comment.
Latest blogs
Composition Over Inheritance: Why Your Content Contract Is Not a Base Class

Inside the structural conformance pattern in SaaS CMS — from server-side inheritance to pipeline governance. A contract in Optimizely SaaS CMS is...

Vipin Banka | Sep 19, 2026

Optimizely Opti ID Integration: Why the API Key Was Missing from the DXP Management Portal

While working on an Optimizely CMS 12 Opti ID integration recently, we ran into an issue that initially appeared to be related to SSO authenticatio...

Madhu | Sep 18, 2026 |

Extending a Contract: What You Can Override

Contracts share fields across content types — but not every field setting can be overridden. Push the same contract onto several components, and CM...

Chirag Khanna | Sep 18, 2026 |

Building a Blazor Server UI Inside the Optimizely CMS 13 Admin Shell

What I learned shipping a Blazor Server UI inside Optimizely CMS 13 — Razor components in a NuGet package, the Blazor hub next to MapContent(),...

Stanisław Szołkowski | Sep 17, 2026 |