Skip To Content

Streamline Kentico Xperience Deployments using the Staging Configuration Module

Introduction  

BlueModus has been enjoying the benefits of automated deployments for years. It has dramatically improved deployment reliability and efficiency. However, platforms that require database configurations and schemas have additional deployment challenges. Kentico Xperience has made great headway in this area, so continuous deployments to dev environments are entirely automated. Using Xperience’s continuous integration feature, we can include object changes like page types in automated deployments to the dev environment. 

However, deployments to the upper environments, like UAT and production, do not use Xperience’s continuous integration feature. Instead, we deploy Xperience database changes with the staging solution. Even in the MVC development model, which doesn’t require storing templates and transformations in the database, database configurations are core parts of the project. My current project requires deploying a variety of object types, including page types, roles, custom modules, and macro rule translators.  

Checking a list 

Managing the deployment of Xperience objects using out-of-the-box staging configurations can be challenging. We must manually select each object type that we want to deploy to prevent overwriting objects. In the past, I’ve handled this by maintaining a document listing the objects to be deployed with the next build. When I change a page type, I would add page types to the list. When I develop a marketing automation action, I would add actions to the list, and so on. I’m sure you can imagine the trouble. The process is rife with human error. I could forget to put an object type on my list or overlook one during deployment.  

I wished I could deploy the web files and then synchronize all staging tasks. It would eliminate the need to maintain lists and manually select objects during deployments. However, there’s almost always types of objects that I would never want to deploy from one environment to another. For example, I don’t want to deploy user objects from the QA environment to the production environment, nor do I want to accidentally deploy environment specific settings like the debug configurations. So, for many projects, I resigned myself to maintaining a list of updated object types for each deployment. Can you imagine how many times I heard about a production bug resolved with “oops, I forgot to stage…”. 

Xperience global system events 

Like me, you might have been resigned to live with this error prone deployment process, because like many other platforms that include schema changes, it seemed like a necessary part of working with Xperience. Perhaps my experience with other platforms made me complacent. All this time, Xperience had a great solution to simplify staging objects. I wish I leveraged this option from the beginning.  

Xperience provides global system events for managing staging tasks. Using two system events, we have complete control of the staging deployment process. The StagingEvents.LogTask.Before event allows cancelling staging tasks on the source server, and the StagingEvents.GetChildProcessingType.Execute event allows controlling how child objects are processed on the target server. 

StagingEvents.LogTask.Before event 

The LogTask.Before event allows preventing staging tasks from being created on the source server. If I don’t want user objects to be propagated from a dev environment to a QA environment, I use this event to prevent staging tasks for user objects from being created. Likewise, if I don’t want settings to be propagated, I can use this event to “eat” the tasks.  The process is simple: create a custom module, register an event handler, and check the parameters of the event. To prevent a task from being logged, call the event parameter’s Cancel method.  Here is a rudimentary example of a module that cancels staging tasks for user objects: 

using CMS; 

using CMS.DataEngine; 

using CMS.EventLog; 

using CMS.Membership; 

using CMS.Synchronization; 

using Acme.Content.Kentico.Modules; 

 

[assembly: RegisterModule(typeof(StagingCustomizationModule))] 

 

namespace Acme.Content.Kentico.Modules 

{ 

    public class StagingCustomizationModule : Module 

    { 

        public StagingCustomizationModule() : base("StagingCustomizationModule") 

        { 

        } 

 

        protected override void OnInit() 

        { 

            base.OnInit(); 

            StagingEvents.LogTask.Before += LogTask_Before; 

        } 

 

        /// <summary> 

        /// Stops staging tasks from being created for user objects. 

        /// </summary> 

        /// <param name="sender"></param> 

        /// <param name="e"></param> 

        private void LogTask_Before(object sender, StagingLogTaskEventArgs e) 

        { 

            // Gets the synchronized object 

            var synchronizedObject = e.Object; 

 

            // Cancels any tasks that are related to the user 

            if (synchronizedObject.TypeInfo.ObjectType == UserInfo.OBJECT_TYPE) 

            { 

                var user = (UserInfo)synchronizedObject; 

                var message = $"Stopped ({user.UserName}) from being tracked in staging."; 

                EventLogProvider.LogEvent(EventType.WARNING, "AcmeModule", "Cancelled staging tasks.", 

                    eventDescription: message); 

                e.Cancel(); 

            } 

        } 

    } 

} 

StagingEvents.GetChildProcessingType.Execute event 

The GetChildProcessingType.Execute event doesn’t prevent staging tasks from being created on the source server.  This event allows inspecting a staging task as it is being processed on the target server so we can change if child relationships are included with a parent object. Our primary use of this event is to prevent user-role relationships from being included when a role object is processed. I prefer to manage role configurations in a dev environment, because they include detailed options like UI personalizations. It’s important to test them. Without customizations, staging a role object from one environment to the next will overwrite the role’s user list on the target server.  So, deploying an editor role from the QA environment to the production environment could cause users to lose their permissions. To solve this, we can prevent user roles from being replace by handling the GetChildProcessingType.Execute event.  Here is another rudimentary example that shows this: 

using System; 

using CMS; 

using CMS.DataEngine; 

using CMS.EventLog; 

using CMS.Membership; 

using CMS.Synchronization; 

using Acme.Content.Kentico.Modules; 

 

[assembly: RegisterModule(typeof(StagingCustomizationModule))] 

 

namespace Acme.Content.Kentico.Modules 

{ 

    public class StagingCustomizationModule : Module 

    { 

        public StagingCustomizationModule() : base("StagingCustomizationModule") 

        { 

        } 

 

        protected override void OnInit() 

        { 

            base.OnInit(); 

            StagingEvents.GetChildProcessingType.Execute += Staging_GetChildProcessingType; 

        } 

 

        /// <summary> 

        /// Prevent user-role relationship synchronization with a role object.  

        /// </summary> 

        /// <param name="sender"></param> 

        /// <param name="e"></param> 

        private void Staging_GetChildProcessingType( 

            object sender, 

            StagingChildProcessingTypeEventArgs e) 

        { 

            if (String.Equals(e.ParentObjectType, RoleInfo.OBJECT_TYPE) && 

                String.Equals(e.ObjectType, UserRoleInfo.OBJECT_TYPE)) 

            { 

                var message = $"Preventing userrole synchronization."; 

                EventLogProvider.LogEvent( 

                    EventType.WARNING, 

                    "AcmeModule", 

                    "Protecting roles by not executing child tasks.", 

                    eventDescription: message); 

 

                e.ProcessingType = IncludeToParentEnum.None; 

            } 

        } 

    } 

} 

Adding configuration 

At first, these rudimentary examples seemed adequate. All I wanted to do was to suppress staging users and settings, but soon I discovered more needs. First, I wanted to prevent scheduled task configurations from being deployed from one environment to the next, but after some time I developed a larger list. For example, marketers would create newsletters, contact groups, and online forms. I didn’t want to deploy samples of these objects created in the UAT environment and potentially overwrite their work in production.  

This led to creating a configurable module to enable defining a list of object types in the web.config. The module also allowed creating a list of child types to exclude from processing on the target server.  This solution became very effective when combined with web.config transformations. It allowed the staging customizations to be environment specific. For example, it enabled preventing forms and categories from being deployed from the UAT environment to the authoring environment, while allowing these object types to be staged from the authoring environment to the live environment.  

Streamlined deployments 

The ability to tweak and refine the configurations enables a streamlined deployment process. We can now deploy to the upper environments by simply clicking “synchronize all” in the staging module. It eliminated the need to manage lists of updated object types or to handpick the objects during deployment. Every time, we are confident that all needed object updates are deployed and nothing more.  

Staging Configuration Module in the Marketplace 

This configurable module seemed so valuable that we are sharing it with you. The “Staging Configuration Module” is available either in the Xperience DevNet Marketplace or on GitHub

I hope this module makes your life easier with faster and more reliable Xperience deployments, and this solution inspires you to consider what else is possible with Xperience’s robust API. As I’ve said before, Xperience’s extensibility may be its best kept secret and one of its most valuable qualities.  

 Have questions on how to get the most out of your Kentico implementation? Drop me a line on Twitter (@HeyMikeWills), or view my other articles here.