Experience Sitecore ! | All posts tagged 'Configuration'

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Configuring role and localenv variables in Sitecore 9 - PowerShell way

In Sitecore 9, one can set up an instance into a specific 'role' that also takes predefined configurations. Further ahead, you may keep your numerous custom configurations next to each other targeting different 'roles' - that avoids clumsy config pathing and keeps settings functionally together in order to simplify maintenance. There is also 'localenv' setting that helps you to distinguish various groups of servers from the same role, but residing in the different environments. Not a nice way of changing role and adding environment by simply replacing a string occurrence:
(Get-Content Web.config).replace('    <add key="role:define" value="Standalone" />', '<add key="role:define" value="ContentManagement" /><add key="localenv:define" value="UAT" />') | Set-Content Web.config

Just a string replacement? Errrghh.... Not a nice solution! Let's make it better, by relying on XML namespace (thanks, Neil):
#$webConfigPath = "С:\path\to\your\Web.config"
#$localEnvName = "UAT"

$RptKeyFound=0;
$xml = (get-content $webConfigPath) -as [Xml];              # Create the XML Object and open the web.config file 
$root = $xml.get_DocumentElement();                         # Get the root element of the file

foreach($item in $root.appSettings.add)                     # loop through the child items in appsettings 
{ 
  if($item.key -eq "localenv:define")                       # If the desired element already exists 
    { 
      $RptKeyFound=1;                                       # Set the found flag 
    } 
}

if($RptKeyFound -eq 0)                                      # If the desired element does not exist 
{ 
    $newEl=$xml.CreateElement("add");                       # Create a new Element 
    $nameAtt1=$xml.CreateAttribute("key");                  # Create a new attribute "key" 
    $nameAtt1.psbase.value="localenv:define";               # Set the value of "key" attribute 
    $newEl.SetAttributeNode($nameAtt1);                     # Attach the "key" attribute 
    $nameAtt2=$xml.CreateAttribute("value");                # Create "value" attribute  
    $nameAtt2.psbase.value="$localEnvName";                 # Set the value of "value" attribute 
    $newEl.SetAttributeNode($nameAtt2);                     # Attach the "value" attribute 
    $xml.configuration["appSettings"].AppendChild($newEl);  # Add the newly created element to the right position
}

$xml.Save($webConfigPath)                                   # Save the web.config file
This same approach can be taken for any other XML-based transforms and replacements.

How Apply-Xml-Transform works in Helix / Habitat

I was recently investigating gulp file of Habitat for interesting goodies and came across taskApply-Xml-Transform, so decided to dig deeper into the one.

What it does?

It looks within all Foundation, Feature and Project layers for config transformations (*.xdt files) in order to run each of them and transform into target Sitecore web.config from the web root folder.

What is XDT?

In very simple, XDT is just an XML file with a set of rules of what and how to transform within web.comfig. We may use them in cases when we need to somehow transform web.config outside of <sitecore> node of configuration so that we can't rely on config paths that only apply within the <sitecore> node. XDT structure corresponds to the structure of target web.config file with additional commands coming from XML-Document-Transform XML namespace. Below is an example of such XDT file, that adds Microsoft.Codedom compiler references into web.config in case they don't exist:

<?xml version="1.0" encoding="utf-8"?>
<configuration  xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <system.codedom xdt:Transform="InsertIfMissing">
    <compilers xdt:Transform="InsertIfMissing">
      <compiler xdt:Transform="InsertIfMissing" xdt:Locator="Match(language)" language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
      <compiler xdt:Transform="InsertIfMissing" xdt:Locator="Match(language)" language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>
</configuration>

I wanted to see how exactly it's being triggered, so running script in a verbose mode, brought me to the following conclusion:

How does it run?

So, configuration transform relies on msbuild to do this job. But instead of Debug, Release or Clean targets, it uses a target calledApplyTransform It accepts numerous parameters, among those we have XDT file to transform, target folder, target configuration file to be transformed and few other parameters. Entire call extracted from a batch looks like below:

C:\Program Files\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe 
/nologo /maxcpucount /nodeReuse:False 
/property:Configuration=Debug 
/property:Platform="Any CPU" 
/property:WebConfigToTransform=C:\inetpub\wwwroot\Platform.dev.local\ 
/property:TransformFile=C:\Platform\src\Feature\Accounts\code\App_Config\Web.config.xdt 
/property:FileToTransform=App_Config\Security\Domains.config 
/target:ApplyTransform 
/toolsversion:15.0 
/verbosity:diagnostic 
C:\Projects\Platform\scripts\applytransform.targets

You may modify the code above and run it on your own casual day-to-day activities outside of gulp, Habitat and other tools.

Hope this helps!

StackOverflow 2 questions on Multi-Site configuration: Multisite Best Practice for setting up Visual Studio Project and Manage web.config in multisite solution

Got to answer two question about multi-site configuration working with Sitecore:

1. Sitecore Multisite Best Practice for setting up Visual Studio Project (link to original question)

I'm seeking an advise on best practice that has worked for creating a asp.net MVC visual studio solution that supports multisite (multi tenant). One thing we would like to do is minimize the regression defects so that developer don't modify the wrong website code base etc.
That the solution needs to support more than 8 web sites.

2. Manage web.config in sitecore multisite solution (link to original question)

We have a multisite solution with individual visual studio solutions for each websites. Then we have master solution to build/deploy all websites.
Firstly, not sure whether it's a best practice to include web.config in Visual Studio solution. But I think all the nuget packages needs web.config to add their settings.
As a result, we have web.config for each solution. However when we deploy from master web.config gets overwritten by each sites. Could someone please suggest how this issue can be fixed?

Answers:

I have got one more alternative to address that. Currently working on a huge project, with dozens of developers and quite bureaucratic process of deployments, we have introduced the following approach:

  1. Project consists of multiple logical subparts, which in fact are individual websites.
  2. Each of those websites we set up as an MVC Area with own controllers, views etc
  3. Most important - we set up MVC Areas as individually pluggable DLLs. So each of that websites is a separate project under solution, with its own resources and statics; on build everything is copied under their required paths and DLL goes to the \bin folder.
  4. One more class library for shared (by all websites) resources, it is being referenced only by those sites, that need that functionality
  5. In Sitecore, we have the same strict principles - the content, layouts, renderings are isolated as higher as possible, no individual content may be re-used (unless from shared resources folder).

This approach serves us almost a year already and has proven for its agility, much easier deployment and fairly less code merge conflicts. If you are working under just one site - you don't need to re-compile and re-deploy everything - just replace one dll at bin folder.

Thus, combining that with your question, Approach 1 would be an answer.

References (more to read):

Regarding managing configuration, I would advise for each website (under its individual project) to create it's own App_Config/Include folder and create _project_name_.config within that folder in order to keep all site-specific settings there (for further merge into resulting config).

On build you set up (for each individual project) that file to be copied into main SITECORE_INSTANCE_WEB_ROOT\App_config\Include folder along with the rest of include patch config files.

Hope someone finds that helpful!

How to check your license in Sitecore

From time to time you may need to obtain your licensing information, for instance when you need to submit support ticket.

License information is displayed right at the loging screen in Sitecore 6.X - 7.X (as per screenshot below):


Once you need more details about current license, (for example to see which modules you are licensed to) you may find that from two menus (Licenses and About) under Sitecore button in Content Editor:



However Sitecore 8 login screen does not show this information by default anymore. To enable displaying licensing info, you need to change one configuration file switch:

<setting name=”Login.DisableLicenseInfo” value=”false” />

Then you'll get a button that reveals your licensing information, including the License key:


Sitecore button in the left top corner of Content Editor has now become "hamburger" button, but provides all the same functionality:



For me (being a developer), the quickest option to remind the license number is just looking up directly at License.xml file:


Hope this helps!

How to host several sites within the same Sitecore instance without specifying a hostname, just on different ports

Challenge: I have got a test server running, where I usually deploy early builds and proofs of concept for business users to acknowledge. Since recent solution has grown to several websites, so I want users to be able to access all of them. The problem occurs from that users do not have administrative permissions to their PCs and are not able to edit hosts file in order to set multiple host names for my server IP (most of them also are not aware how to do that). The good news is that despite being geographically distributed and being in different virtual networks, they do have access to the server IP address.

Unfortunately, I was not able to specify multiple host names (or IP addresses) to the server as a part of infrastructure configuration, so it became obvious that users should access those websites by IP address, moreover the same IP address to all sites. So what came first into my mind was to distinguish websites by ports within same IIS. Sounds good, but how to do that? I definitely knew that the it resolves website by the host name, not the port, as set within <sites> node of config file. I tried googling a solution but did not find anything...


Investigation: Thus, armed with Reflector and dotPeek tools I started investigating and debugging original code from Sitecore.Kernel.dll. Since a while I came across SiteResolver class, that serves a processor for httpRequestBegin pipeline:


    ...

    ...

So far, so good. The method doing resolving job is called ResolveSiteContext, so I already morally prepared to inherit from SiteResolver class and override that method, implementing site resolution by port.

But what was my excitement, when I notices that it calls SiteContextFactory.GetSiteContext passing hostname, file path and port! So it already supposes port coming from config file, doesn't it? Let's go and inspect this method:

    public static SiteContext GetSiteContext(string hostName, string fullPath, int portNumber)
    {
      fullPath = fullPath.ToLowerInvariant();
      foreach (SiteInfo info in SiteContextFactory.Sites)
      {
        if (info.Matches(hostName, fullPath, portNumber))
          return new SiteContext(info);
      }
      return (SiteContext) null;
    }

and Matches() method follows as:

    public bool Matches(string host, string folder, int portNumber)
    {
      return this.MatchesHost(host) && this.MatchesPort(portNumber) && this.MatchesFolder(folder);
    }

After looking at MatchesPort(portNumber) I chased portNumber and where it comes from. It occurred that parser expects attribute with name "port" and takes the value out of it, or sets default value(0).

Right, so in fact that proves that port number can be solely used for resolving site within same Sitecore instance. But why there is lack of references or documentation about that? In any case this blog post fills the gap, I hope.


Testing: Now it's time to test my assumption. I have created two bindings for my sitecore instance, for 80 and 8080 ports. Notice that there's no hostnames assigned:

Challenge: I have got a test server running, where I usually deploy early builds and proofs of concept for business users to acknowledge. Since recent solution has grown to several websites, so I want users to be able to access all of them. The problem occurs from that users do not have administrative permissions to their PCs and are not able to edit hosts file in order to set multiple host names for my server IP (most of them also are not aware how to do that). The good news is that despite being geographically distributed and being in different virtual networks, they do have access to the server IP address.

Unfortunately, I was not able to specify multiple host names (or IP addresses) to the server as a part of infrastructure configuration, so it became obvious that users should access those websites by IP address, moreover the same IP address to all sites. So what came first into my mind was to distinguish websites by ports within same IIS. Sounds good, but how to do that? I definitely knew that the it resolves website by the host name, not the port, as set within <sites> node of config file. I tried googling a solution but did not find anything...


Investigation: Thus, armed with Reflector and dotPeek tools I started investigating and debugging original code from Sitecore.Kernel.dll. Since a while I came across SiteResolver class, that serves a processor for httpRequestBegin pipeline:


    ...

    ...

So far, so good. The method doing resolving job is called ResolveSiteContext, so I already morally prepared to inherit from SiteResolver class and override that method, implementing site resolution by port.

But what was my excitement, when I notices that it calls SiteContextFactory.GetSiteContext passing hostname, file path and port! So it already supposes port coming from config file, doesn't it? Let's go and inspect this method:

    public static SiteContext GetSiteContext(string hostName, string fullPath, int portNumber)
    {
      fullPath = fullPath.ToLowerInvariant();
      foreach (SiteInfo info in SiteContextFactory.Sites)
      {
        if (info.Matches(hostName, fullPath, portNumber))
          return new SiteContext(info);
      }
      return (SiteContext) null;
    }

and Matches() method follows as:

    public bool Matches(string host, string folder, int portNumber)
    {
      return this.MatchesHost(host) && this.MatchesPort(portNumber) && this.MatchesFolder(folder);
    }

After looking at MatchesPort(portNumber) I chased portNumber and where it comes from. It occurred that parser expects attribute with name "port" and takes the value out of it, or sets default value(0).

Right, so in fact that proves that port number can be solely used for resolving site within same Sitecore instance. But why there is lack of references or documentation about that? In any case this blog post fills the gap, I hope.


Testing: Now it's time to test my assumption. I have created two bindings for my sitecore instance, for 80 and 8080 ports. Notice that there's no hostnames assigned:



Voila! Hope this hapens to be helpful and may save some efforts for you in future!

Within Sitecore Desktop I created two website landing pages for each of sites.
Here is te content for Primary website sitting on default 80 port:

and below is the same for Secondary website on 8080:


Finally, assign them in the config file. Notice, there is no hostname defined again. Only start item and port number:


Ok, now publish both sites to web database and try accessing them. The first one (primary) opens on default port 80 by simply entering IP address. Expected behavior! It shows content exactly as configured earlier:


Just aplyint port number (8080) to the same IP address we got routed to secondary website. As expected, again:


Voila! Hope this hapens to be helpful and may save some efforts for you in future!

Know your tools: SIM - Sitecore Instance Manager

Sitecore Instance Manager (SIM) - the must-have tool for all Sitecore professionals and platform enthusiasts. It is a "Swiss army knife" for all types of activities related to installation and configuring Sitecore instances. So, what it does?

As it is obvious from its title, SIM simplifies installation of Sitecore, minimizing it to just few very intuitive clicks. SIM supports all versions of Sitecore, developers work tightly with platform vendor, so since recent they tend to synchronize SIM updates with new Sitecore releases. Oh, nearly forgot to mention, SIM has auto-update module that can update the program silently in background, or with a prompt, or just leave user alone once he prefers getting updates donу manually.

Here is the main screen of Sitecore Instance Manager:


You have all available instances listed, you can install new or remove existing, do some configuration changes and much more. SIM operates "web-folder" installation archives as they came form Sitecore, one can download zip and manually place it into specific folder (that is configurable in program settings) or can download and store any platform version directly from SDN. In that case he/she might need to type in SDN credentials and pick up exact Sitecore version from options drop-down. As soon as zip is downloaded, it can be installed.

The installation process occurs in few clicks and is show on several screenshots below. First of all, we select which version we are going to install from the list of stored in local repository. Also there are fields to specify instance name, hostname and the installation folder.


The program accurately installs files, restores database and sets appropriate SQL permissions, configures Application Pool and create config files with correct values.

SIM is great in that it allows not just install Sitecore itself, but also you may specify which modules you would like to install straight away, just by simply checking them from the list of available.



Apart from modules you may also install certain custom packages, likewise you may have a fully working website - both items and file system substructure packed within a package, so it may become available straight after the installation. As another example, I always install useful Sitecore adjustments with SIM in order to benefit out of them straight away.



Not only custom packages can be auto-installed, but also such called configuration presets. These are certain configuration patches, each addressing small but important setting, will be placed into App_Config/Include folder.



The installation itself does not take much time. Sitecore 8 takes approximately 1 minute in virtual machine on my MacBookPro. Significantly faster comparing with time spent on default installer.




SIM also have multiple useful shortcuts at one place, like links to important Sitecore folders, configuration tools, hosts editor, IIS recycle an many many more.




I would award SIM with the highest rate and highly advise to download and play with it, even if you do not regularly play with installation and instances.

Download: SIM on Sitecore Marketplace


How websites are resolved with Sitecore - the essentials

What actually happens when you type website URL in browser and how is your request factually served in sitecore. We are going to use www.site.com as the URL for our example. This is simplified version where only most important steps are covered.

First of all, as you hit this URL in browser's address bar, it retrieves IP address by the host name from DNS server. Then it creates request to IP address resolved with host name (www.site.com) in HTTP header. There may be multiple traffic managers / load balancers on the route to the server, but eventually request gets to that IP address to the specified port (as we use normal HTTP, then the port is default 80).

On the server computer there should be specific software running and listening to that port (80) otherwise request will fail. In our scenario, that is the Microsoft IIS web server. Below are two most important IS configuration screens:


On the first screenshot you see Site Bindings screen that binds exact website within current IIS instance to specific port and hostname (if set). There can be multiple website hosted within same IIS instance, so you we usually vary them by hostname / port combinations. The example above shows that all requests to port 80 with the hostname www.site.com would be served by current website. Second record shows that all requests to port 443 (which is default to HTTP) would be served by the same site as well.


The second screenshot assigns our website www.site.com to a folder on a disk drive which becomes a web root for our site.

Root of the website should have a configuration file called web.config, that may be split and overridden in subfolders and that itself overrides global web.config and machine.config files with default settings. Every sitecore-based website contains <sitecore> node in the configuration file, that is where all sitecore settings are defined, including <sites> node that specifies all the websites per current Sitecore instance.


Important to note that sites are determined by "first match" principle, so order is critical. If you look at our www.site.com record - you'll notice that it specifies hostname - all requests matching that name will be served by this site. Other setting set what database is used for particular site (name should match database name from connection string), what is starting sitecore tree node within that database - factual page item that is being returned. There are also html caching setting, sitecore domain name for the site, etc.

The rest of request to current Sitecore instance, that do no math our host name pattern will be served by site called "website", it does not have hostName specified, so it will serve everything else and return /sitecore/content/home item.

Let's assume the site has been published from master database to web database, as per configuration. Here's below how sitecore tree looks like in Sitecore Content Editor:


Our website's home item (selected) is called SiteCom and it will return the page with "www.site.com"in title. Let's see the browser:


Here we done. That is our www.site.com landing page loaded!


Advanced topic: How to host several sites within the same Sitecore instance without specifying a hostname, just on different ports


SwitchMasterToWeb.config for Sitecore 8

I have just tested the one on my Sitecore 8.0 Update 4 instance - it works pretty well!

For those who has never met this config before - SwitchMasterToWeb is a configuration patch file, that aims to be placed into App_Config\Include folder on content delivery server (CD) in order to remove references to master database, that should not present there by design.



	
		
			
			
			
			
			
			
			
		
		
			
			
			
				
			
		
		
			
		
		
			
				
			
		
		
			
				
					
						
							
								
							
						
					
				
			
		
		
			
				
					
						
					
				
			
			
				
			
			
				
			
			
				core
				/sitecore/system/tasks/schedules
				true
			
			
				
			
			
				
					
						sitecore_master_index
					
				
			
			
				
			
			
				
			
		
		
			
				
					
						
					
					
						
					
					
					
						
					
					
					
						
					
					
					
						
					
					
					
						
					
					
					
						
					
					
					
						
					
				
			
			
			
				
					
						
					
					
						
					
				
			
		
		
		
			
				
			
		
		
		
			
				
					web
				
			
		
		
		
			
				
					
						
							web
						
					
					
						
							web
						
					
					
						
							web
						
					
				
			
		
		
		
			
				
					
						web
					
				
			
			
				
					
						web
						sitecore_marketing_asset_index_web
					
				
			
			
				
					
						web
					
				
			
			
				
					
						web
					
				
			
		

		
		
			web