Setup Unicorn for Helix based Sitecore project.

If you are following the helix architecture, then you need to add a project for Serialization under the Foundation layer. You can add a new empty MVC project.

Eg. [Customer Name|Brand Name|Sitecore].Foundation.Serialization.csproj

Sitecore.Foundation.Serialization.csproj

  • Right click on the newly added project and select “Manage NuGet Package”.
  • In the NuGet window, select the “Browse” tab if it isn’t selected already and search for “unicorn”.
  • Select the latest version available, install the package.

Once the Unicorn is installed, you can find the “Unicorn” folder created in the “App_Config\Include” folder which contains Unicorn-related config files.

There is one more config file “Rainbow.config” which can be found outside the Unicorn folder which also contains few settings referred to in unicorn serialization.

Rename the file Unicorn.UI.IdentityServer.config.disabled to Unicorn.UI.IdentityServer.config if you are using Sitecore 9.1 or higher.

Build and confirm the project gets compiled successfully.

Configuration of Unicorn

After installing, it’s time to configure the Unicorn and manage your Sitecore items in serialized files.

Create a folder inside the “Include” folder of “App_Config” & name it as “zzz.Foundation”

Create a “SourceFolder.config” file under zzz.Foundation folder and copy-paste below configuration – update the source folder path as per your requirement. (can be root of your solution i.e “src” folder path). Unicorn will serialize Sitecore items as .yml files in this folder path.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0"?>
 
  <sitecore>
    <sc.variable name="sourceFolder" value="D:\Logistico Project\CodeBase\src" />
    <sites role:require="Standalone">
      <site name="website">
        <patch:attribute name="database">master</patch:attribute>
      </site>
    </sites>
 
    <!--Enable Unicorn login -->
    <pipelines>
      <owin.cookieAuthentication.validateIdentity>
        <processor type="Sitecore.Owin.Authentication.Pipelines.CookieAuthentication.ValidateIdentity.ValidateSiteNeutralPaths, Sitecore.Owin.Authentication">
          <siteNeutralPaths hint="list">
            <path hint="unicorn">/unicorn.aspx</path>
          </siteNeutralPaths>
        </processor>
      </owin.cookieAuthentication.validateIdentity>
    </pipelines>
     
  </sitecore>
</configuration>

Create a new config file with the name Foundation.Serialization.config, which will contain the information/configuration for Sitecore items like settings, facets, placeholder settings, etc. Comment/Uncomment the settings as per your needs.

Sample code for Foundation.Serialization.config –

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<!--
    See Unicorn.config for commentary on how configurations operate, or https://github.com/kamsar/Unicorn/blob/master/README.md
-->
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <configuration name="Foundation.Serialization" description="Sitecore.Solution.Framework Root items" extends="Helix.Base">
          <targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization" useDataCache="false" singleInstance="true"/>
          <predicate>
 
            <!--
                Each include can also exclude specific subitems by path:
                <exclude path="/sitecore/content"/>
 
                Excludes may also exclude all children at once by adding a trailing slash, e.g. this would include the root /sitecore item but no children
                <include path="/sitecore">
                    <exclude path="/sitecore/" />
                </include>
 
                NOTE: after changing what is included or excluded, you should reserialize all items, or at least the added items
                NOTE: the "name" attribute controls the folder name the items will go into (for SFS). If unspecified, the last path segment is used. Names must be unique across the configuration.
            -->
            <!-- Settings
            <include name="Settings.Feature" database="master" path="/sitecore/system/Settings/Feature">
              <exclude children="true" />
            </include>
            <include name="Settings.Foundation" database="master" path="/sitecore/system/Settings/Foundation">
              <exclude children="true" />
            </include>
            <include name="Settings.Project" database="master" path="/sitecore/system/Settings/Project">
              <exclude children="true" />
            </include>-->
 
            <!-- Facets
            <include name="Facets.Feature" database="master" path="/sitecore/system/Settings/Buckets/Facets/Feature">
              <exclude children="true" />
            </include>
            <include name="Facets.Foundation" database="master" path="/sitecore/system/Settings/Buckets/Facets/Foundation">
              <exclude children="true" />
            </include>
            <include name="Facets.Project" database="master" path="/sitecore/system/Settings/Buckets/Facets/Project">
              <exclude children="true" />
            </include>-->
 
            <!-- Templates -->
            <include name="Templates.Feature" database="master" path="/sitecore/templates/Feature">
              <exclude children="true" />
            </include>
            <include name="Templates.Foundation" database="master" path="/sitecore/templates/Foundation">
              <exclude children="true" />
            </include>
            <include name="Templates.Project" database="master" path="/sitecore/templates/Project">
              <exclude children="true" />
            </include>
 
            <!-- Branches -->
            <include name="Branches.Foundation" database="master" path="/sitecore/templates/branches/Foundation">
              <exclude children="true" />
            </include>
            <include name="Branches.Feature" database="master" path="/sitecore/templates/branches/Feature">
              <exclude children="true" />
            </include>
            <include name="Branches.Project" database="master" path="/sitecore/templates/branches/Project">
              <exclude children="true" />
            </include>
 
            <!-- Renderings -->
            <include name="Renderings.Feature" database="master" path="/sitecore/layout/renderings/Feature">
              <exclude children="true" />
            </include>
            <include name="Renderings.Foundation" database="master" path="/sitecore/layout/renderings/Foundation">
              <exclude children="true" />
            </include>
            <include name="Renderings.Project" database="master" path="/sitecore/layout/renderings/Project">
              <exclude children="true" />
            </include>
 
            <!-- Layouts -->
            <include name="Layouts.Feature" database="master" path="/sitecore/layout/layouts/Feature">
              <exclude children="true" />
            </include>
            <include name="Layouts.Foundation" database="master" path="/sitecore/layout/layouts/Foundation">
              <exclude children="true" />
            </include>
            <include name="Layouts.Project" database="master" path="/sitecore/layout/layouts/Project">
              <exclude children="true" />
            </include>
 
            <!-- Placeholder Settings -->
            <include name="PlaceholderSettings.Feature" database="master" path="/sitecore/layout/placeholder settings/Feature">
              <exclude children="true" />
            </include>
            <include name="PlaceholderSettings.Foundation" database="master" path="/sitecore/layout/placeholder settings/Foundation">
              <exclude children="true" />
            </include>
            <include name="PlaceholderSettings.Project" database="master" path="/sitecore/layout/placeholder settings/Project">
              <exclude children="true" />
            </include>
 
            <!-- Models -->
            <include name="Models.Feature" database="master" path="/sitecore/layout/models/Feature">
              <exclude children="true" />
            </include>
            <include name="Models.Foundation" database="master" path="/sitecore/layout/models/Foundation">
              <exclude children="true" />
            </include>
            <include name="Models.Project" database="master" path="/sitecore/layout/models/Project">
              <exclude children="true" />
            </include>
 
            <!-- Media -->
            <include name="Media.Website" database="master" path="/sitecore/media library/Logistico">
              <exclude children="true" />
            </include>
            <!--<include name="Media.Project" database="master" path="/sitecore/media library/Project">
              <exclude children="true" />
            </include>-->
 
            <!-- Core templates
            <include name="Core.Templates.Feature" database="core" path="/sitecore/templates/Feature">
              <exclude children="true" />
            </include>
            <include name="Core.Templates.Foundation" database="core" path="/sitecore/templates/Foundation">
              <exclude children="true" />
            </include>
            <include name="Core.Templates.Project" database="core" path="/sitecore/templates/Project">
              <exclude children="true" />
            </include>-->
          </predicate>
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

You can then override the Rainbow configuration with the new configuration file (e.g. Foundation.Serialization.Settings.config) as below-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  <sitecore>
    <settings>
      <!--  Rainbow - SERIALIZATION FOLDER PATH MAX LENGTH
              In Windows, there is 248 characters limit on the lenght of file system paths. To avoid exceeding the maximum path length, Rainbow will loop
                      long paths back to the root. This setting specifies the maximum length of the path to the serialization root path,
                      which determines how long item paths can be before they are looped.
              Important: The value of this setting must be the same on all Sitecore instances accessing the serialized data.
              Important: When changing this value, you must reserialize all configurations!
              Example: A value of "90" for this setting will mean that item paths longer than 150 characters will be shortened, since Sitecore
              reserves 8 characters (and 248 - 8 - 90 = 150).
              Default value: 90
            -->
 
      <setting name="Rainbow.SFS.SerializationFolderPathMaxLength" value="150" />
      <setting name="Rainbow.SFS.MaxItemNameLengthBeforeTruncation" value="50" />
    </settings>
  </sitecore>
</configuration>

We need to configure standard configurations for modules in all layers, it can be done with a new configuration file created in “App_Config\Include” folder say Unicorn.Helix.config.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <!-- Base configuration for all modules -->
        <configuration name="Helix.Base" abstract="true">
          <predicate type="Unicorn.Predicates.SerializationPresetPredicate, Unicorn" singleInstance="true" />
 
          <targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization" useDataCache="false" singleInstance="true" />
          <!--<roleDataStore type="Unicorn.Roles.Data.FilesystemRoleDataStore, Unicorn.Roles" physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization\Roles" singleInstance="true" />
          <rolePredicate type="Unicorn.Roles.RolePredicates.ConfigurationRolePredicate, Unicorn.Roles" singleInstance="true">-->
            <!-- Include an invalid predicate or all roles will be synced -->
           <!-- <include domain="invaliddomain" pattern="none" />
          </rolePredicate>-->
        </configuration>
 
        <!-- Foundation modules -->
        <configuration name="Helix.Foundation" abstract="true" extends="Helix.Base">
          <predicate>
            <include name="Templates" database="master" path="/sitecore/templates/$(layer)/$(module)" />
          </predicate>
        </configuration>
 
        <!-- Feature modules -->
        <configuration name="Helix.Feature" abstract="true" extends="Helix.Base">
          <predicate>
            <include name="Templates" database="master" path="/sitecore/templates/$(layer)/$(module)" />
            <include name="Renderings" database="master" path="/sitecore/layout/renderings/$(layer)/$(module)" />
            <include name="Media" database="master" path="/sitecore/media library/$(layer)/$(module)" />
          </predicate>
        </configuration>
 
        <!-- Project modules -->
        <configuration name="Helix.Project" abstract="true" extends="Helix.Base">
          <predicate>
            <include name="Templates" database="master" path="/sitecore/templates/$(layer)/$(module)" />
            <include name="Renderings" database="master" path="/sitecore/layout/renderings/$(layer)/$(module)" />
          </predicate>
        </configuration>
        <syncConfiguration type="Unicorn.Loader.DefaultSyncConfiguration, Unicorn" singleInstance="true" updateLinkDatabase="true" updateSearchIndex="true" maxConcurrency="1" />
        <userDataStore type="Unicorn.Users.Data.FilesystemUserDataStore, Unicorn.Users" physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization\Users\" singleInstance="true" />
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

So you will need to create the below files under zzz.Foundation in order to configure the Unicorn.

Sample folder structure will be like below-

App_Config – folder

                Include – Folder

                                Unicorn – folder

                                zzz.Foundation – Folder

                                                Foundation.Serialization.config

                                                Foundation.Serialization.Settings.config

                                                SourceFolder.config

                                Rainbow.config

                                Unicorn.Helix.config

Once we are done with Unicorn configuration in the Foundation layer, we also need to do configuration while implementing the requirements under different layers like Project, Feature, and Foundation wherever required.

e.g. Sample configuration from Project Layer serialization config. Comment/Uncomment based on your requirement. Here you will need to specify the dependencies also i.e. Foundation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    <sitecore role:require="Standalone or ContentManagement">
        <unicorn>
            <configurations>
                <configuration name="Project.Logistico.Website" description="Logistico content" dependencies="Foundation.*,Feature.*" extends="Helix.Project">
                  <targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization" useDataCache="false" singleInstance="true"/>
                    <predicate>
                        <include name="Layouts" database="master" path="/sitecore/layout/layouts/Project/Logistico" />
                      <!--<include name="ProjectRenderings" database="master" path="/sitecore/layout/Renderings/Project/Logistico" />-->
                        <include name="PlaceholderSettings" database="master" path="/sitecore/layout/placeholder settings/Project/Logistico" />
                        <include name="Models" database="master" path="/sitecore/layout/models/Project/Logistico" />
                        <!--<include name="Languages.Danish" database="master" path="/sitecore/system/Languages/da" />
                        <include name="Languages.Japanese" database="master" path="/sitecore/system/Languages/ja-JP" />-->
 
                        <include name="Content" database="master" path="/sitecore/content/Home" />
                        <include name="GlobalSettings" database="master" path="/sitecore/content/Global" />
                        <!--<include name="Media" database="master" path="/sitecore/media library/Logistico" />-->
                        <!--<include name="Metadata" database="master" path="/sitecore/system/settings/feature/metadata/Habitat" />
 
                        <include name="Profiling" database="master" path="/sitecore/system/Marketing Control Panel/Profiles/Habitat" />
                        <include name="Outcomes" database="master" path="/sitecore/system/Marketing Control Panel/Outcomes/Habitat" />
                        <include name="Campaigns" database="master" path="/sitecore/system/Marketing Control Panel/Campaigns/Habitat" />
                        <include name="Goals" database="master" path="/sitecore/system/Marketing Control Panel/Goals/Habitat" />-->
                       
                    </predicate>
                    <!--<rolePredicate>
                        <include domain="extranet" pattern="^Project Logistico .*$" />
                    </rolePredicate>-->
<!--
                    TODO: Bug in Unicorn with SC v9?
                    <userPredicate type="Unicorn.Users.UserPredicates.ConfigurationUserPredicate, Unicorn.Users" singleInstance="true">
                        <include domain="extranet" pattern="^((?!Anonymous).)*$" />
                    </userPredicate>
                    <userSyncConfiguration defaultPassword="b" minPasswordLength="1" />
-->
                </configuration>
            </configurations>
        </unicorn>
    </sitecore>
</configuration>

Similarly, you will need a Unicorn configuration at each Feature for serializing the Feature related items.

Eg. Sample configuration from Feature Layer serialization config.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    <sitecore role:require="Standalone or ContentManagement">
        <unicorn>
            <configurations>
                <configuration name="Feature.Identity" description="Identity content" dependencies="Foundation.*">
                  <targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization" useDataCache="false" singleInstance="true"/>
                    <predicate>
                      <include name="Identity.Renderings" database="master" path="/sitecore/layout/Renderings/Feature/Identity" />
                      <include name="Identity.Templates" database="master" path="/sitecore/templates/Feature/Identity" />
                      <include name="Identity.Media" database="master" path="/sitecore/media library/Logistico/Identity" />
                      <!--<include name="Identity.PlaceholderSettings" database="master" path="/sitecore/layout/placeholder settings/Project/Logistico" />-->
                    </predicate>
                </configuration>
            </configurations>
        </unicorn>
    </sitecore>
</configuration>

After making all these configurations, publish your changes to the website folder (Sitecore instance folder under wwwroot) so that site will use these configurations and Unicorn can be accessed.

Once installation and configuration are completed, Unicorn can be accessed with the below URL

https://yoursiteurl/unicorn.aspx

Before accessing this URL, make sure that you have been logged in to the Sitecore with administrator user credentials, else you will not be able to see the unicorn admin page instead you will get an Access Denied message.

If you have loaded the Unicorn for the first time after publishing the Config files, then you will need to perform that initial serialization.

In this blog, we learned about Installing and Configuring the Unicorn for the Helix-based Sitecore Project.

Thank you... Keep Learning... Keep Sitecoring...

Comments