Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
Experience Sitecore! | April 2018

# Experience Sitecore!

### Martin Miles on Sitecore

This is an exercise resulting from a blog post by Rob Ahnemann and I will cover Install-SitecoreConfiguration in more details on an example of installing Sitecore modules SPE and SXA as a part of a Sitecore installation by SIF.

Install-SitecoreConfiguration is one of the most what actually SIF is. In my Sitecore installation script I have created a step called Install-Packages. Let's look at how it works:

function Install-Packages {
Install-SitecoreConfiguration -Path .\build\PostInstall\install-sitecore-package.json -SiteName "$SolutionPrefix.$SitePostFix"
}


This installs a configuration called install-sitecore-package.json Opening it looks like below:

{
"Parameters": {
"SiteName": {
"Type": "string",
"DefaultValue": "Sitecore",
"Description": "The name of the site to be deployed."
},
"InstallDirectory": {
"Type": "string",
"DefaultValue": "c:\\inetpub\\wwwroot",
"Description": "Base folder to where website is deployed."
}
},
"Variables": {
// The sites full path on disk
"Site.PhysicalPath": "[joinpath(parameter('InstallDirectory'), parameter('SiteName'))]",
"Site.Url": "[concat('http://', parameter('SiteName'))]"

},
"InstallPackages":{
"Type": "InstallPackage",
"Params": [
{
"SiteFolder": "[variable('Site.PhysicalPath')]",
"SiteUrl": "[variable('Site.Url')]",
"PackagePath": ".\\build\\assets\\Modules\\Sitecore PowerShell Extensions-4.7.2 for Sitecore 8.zip"
},
{
"SiteFolder": "[variable('Site.PhysicalPath')]",
"SiteUrl": "[variable('Site.Url')]",
"PackagePath": ".\\build\\assets\\Modules\\Sitecore Experience Accelerator 1.6 rev. 180103 for 9.0.zip"
}
]
}
},
"Modules":[
]
}

Parameters are values that may be passed when Install-SitecoreConfiguration is called. Parameters must declare a Type and may declare a DefaultValue and Description. Parameters with no DefaultValue are required when Install-SitecoreConfiguration is called.

Variables are values calculated in a configuration. They can reference Parameters, other Variables, and config functions.

Tasks are separate units of work in a configuration. Each task is an action that will be completed when Install-SitecoreConfiguration is called. By default, tasks are applied in the order they are declared. Tasks may reference Parameters, Variables, and config functions.

Finally, the last line is actually referencing the actual task - a PowerShell module script (Invoke-InstallPackageTask.psm1) that will be run with given parameters:

Set-StrictMode -Version 2.0

[CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$true)]
[string]$SiteFolder, [Parameter(Mandatory=$true)]
[string]$SiteUrl, [Parameter(Mandatory=$true)]
[string]$PackagePath ) Write-TaskInfo "Installing Package$PackagePath" -Tag 'PackageInstall'

#Generate a random 10 digit folder name. For security
$folderKey = -join ((97..122) | Get-Random -Count 10 | % {[char]$_})

#Generate a Access Key (hi there TDS)
$accessKey = New-Guid Write-TaskInfo "Folder Key =$folderKey" -Tag 'PackageInstall'
Write-TaskInfo "Access Guid = $accessKey" -Tag 'PackageInstall' #The path to the source Agent. Should be in the same folder as I'm running$sourceAgentPath = Resolve-Path "PackageInstaller.asmx"

#The folder on the Server where the Sitecore PackageInstaller folder is to be created
$packageInstallPath = [IO.Path]::Combine($SiteFolder, 'sitecore', 'PackageInstaller')

#The folder where the actuall install happens
$destPath = [IO.Path]::Combine($SiteFolder, 'sitecore', 'PackageInstaller', $folderKey) #Full path including the installer name$fullFileDestPath = Join-Path $destPath "PackageInstaller.asmx" Write-TaskInfo "Source Agent [$sourceAgentPath]" -Tag 'PackageInstall'
Write-TaskInfo "Dest AgentPath [$destPath]" -Tag 'PackageInstall' #Forcibly cread the folder New-Item -ItemType Directory -Force -Path$destPath

#Read contents of the file, and embed the security token
(Get-Content $sourceAgentPath).replace('[TOKEN]',$accessKey) | Set-Content $fullFileDestPath #How do we get to Sitecore? This URL!$webURI= "$siteURL/sitecore/PackageInstaller/$folderKey/packageinstaller.asmx?WSDL"

Write-TaskInfo "Url $webURI" -Tag 'PackageInstall' #Do the install here$proxy = New-WebServiceProxy -uri $webURI$proxy.Timeout = 1800000

#Invoke our proxy
$proxy.InstallZipPackage($PackagePath, $accessKey) #Remove the folderKey Remove-Item$packageInstallPath -Recurse
}


What this task does is locates ASMX file, which is an actual handler and copies it into temp random folder within your Sitecore instance allowing you to execute package installer APIs that itself does require Sitecore context.

Obviously, before running .\install-xp0.ps1 please make sure you have both installers for modules by their paths as per configuration parameters in PackagePath (from example above, they are):
.\build\assets\Modules\Sitecore PowerShell Extensions-4.7.2 for Sitecore 8.zip
.\build\assets\Modules\Sitecore Experience Accelerator 1.6 rev. 180103 for 9.0.zip

So, that's how we added new SitecoreConfiguration in order to achieve automated package installation of SPE and SXA by SIF. Since now new locally installed Sitecore instance already has both SPE and SXA pre-installed and ready to use. This approach also allows installing any other modules as you may need them pre-installed.

Finally, would highly recommend watching a great video by Thomas Eldblom about using SIF configurations:

UPDATE: there is another way of doing this by new built-in SIF functionality, please read the blog post here.

Proud to be among those very first to be able to deliver a Sitecore 9, Azure PaaS and Helix powered website into a production! To my greatest regret, I am not allowed disclose the client's name, that's why instead better share the spec.

Technologically, the solution contains many of things discovered previously in my blog and is the following:

• Sitecore 9.0 update 1
• Helix principles
• Glass mapper with T4 model interface code generation
• VSTS for everything - CI / CD tool, code repository, productivity tracking solution
• WebDeploy for deploying the assets to Azure PaaS
• Unicorn for serialization
• Three XP-Single, four XP-Scaled as well as DEV Environments
• Relies on PowerShell for automation and management
It is a good time to steam out for a while and then keep a focus on my projects such as Sitecore.Link, Sitecore Discussion Club, Onero and of course - blogging here.

If you decide to use Habitat as a bootstrap platform for your Helix solution, while setting up workflows for your solution, you may come across a situation described below. By this blog post I will try to explain what happens, why is it so, and how to make things work.

Symptoms: you are about to set up workflows for the solution and have created a role for the content editors. Then you give read / write permissions for that role to the site content (likely to be /Home and /Global nodes under your site definition item, recursively). When logging as a user having Content Editor role mention above, you are able to Lock and Edit and later to Check-In an item, but the fields for that item are disabled. Weird. But doing the same on other items outside your website works well (for instance - Home item coming with Sitecore initial installation). Why is it so?

There are few of StackOverflow questions trying to sort this out: one and two. I have left few comments there helping other to solve the situation.

Explanation: Habitat uses an "intersection" of feature-or-foundation-level permissions (also knows as Functional roles) with project-level permissions (also knows as organisational rights). Most of the Habitat modules have such a functional role coming as a part of the module, is in the following format: modules\Feature XXX Admin or modules\Foundation XXX Admin.

What habitat does - it denies write access for the inheritance for all the fields by default and then explicitly allows writing permission for that particular Functional Role within a module. That is briefly explained in the official Helix documentation but two images below would be more descriptive:

Solution: two potential ways of sorting this out. The first option is when you decide to keep Functional roles as a part of your solution. In that case, you need to make sure your Content Editor roles also inherits from these Functional roles (or from an umbrella role inheriting a combination of Functional roles).

Another way will be if you decide to drop these Functional roles. In that case, you'll need to remove them from serialization config and source control, and also perform the following for each field affected:

2. Click Security tab, then Assign. You'll see at least two roles available - Everyone and a Functional role for that module.

3. Selecting Everyone, remove Inheritance denial for both Item and Descendants by clicking both red crosses, then save (OK).

4. Repeat that for each field of each template for each of the Feature / Foundation layers.

Then users from Content Editors role will be able to edit all the fields.

Hope this helps!

From time to time working with Sitecore I have to rely on automation (especially when working with CI / CD) so just decided to store some snippets for myself that use occasionally. This list will update with time.

1. Run MsBuild from a console
Building solution outside of Visual Studio or alternative IDE requires a manual call of MsBuild. In the very simple call you need to pass just two parameters - a solution itself and target, that can be Clean, Build etc.:
"c:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe" c:\Projects\Platform\Platform.sln  /t:Build


2. Config transform
Calling config transform described in more details by this link, so there is just a snippet below:
"C:\Program Files (x86)\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:\Projects\Platform\src\Project\YourWebsite\code\web.config.xdt
/property:FileToTransform=web.config
/target:ApplyTransform
/toolsversion:15.0
/verbosity:minimal
C:\Projects\Platform\scripts\applytransform.targets


3. Test SQL connectivity from PowerShell
The easiest way to test connectivity between a custom machine running PowerShell and the desired SQL Server instance:
Invoke-Sqlcmd -ServerInstance 'hostname-and-instance-and-optionally-port'
-Username 'sa' 
-Query 'SELECT GETDATE() AS TimeOfQuery'


4. Archive a folder a place it into a specific location using PowerShell only
When you need to archive a folder you need to rely on an external tool such as zip, which brings another dependency into your pipeline. But that is not a case anymore when using PowerShell as it has entire power of .NET and that in turn has zip support within the namespaceSystem.Net.Compression. So why not to rely on PowerShell and .NET to do the entire job?
IF EXIST output.zip DEL /F output.zip
powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::CreateFromDirectory('c:\Projects\Platform\build\output\', 'output.zip'); }"


5. Unzip a folder from an archive using PowerShell only
The reverse procedure of unzipping an archive into a folder can also be performed with .NET and PowerShell in the same manner:
powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('c:\Projects\SomeArchive.zip', 'c:\inetpub\wwwroot\TargetFolder'); }"

6. Install a NuGet package using CLI
When you need to use NuGet from a console, you will be likely using NuGet CL. An example below shows installing a NuGet package into a folder passed as a parameter. Please keep in mind that package repositories should be mentioned in the accompanying configuration for nuget.exe:
nuget install xunit.runner.console -OutputDirectory c:\Projects\!\NuGet\

7 Run xUnit tests
After installing xUnit running unit tests from a console, the rest is easy as than simply passing a library containing tests as a parameter:
c:\Projects\!\NuGet\xunit.runner.console.2.3.1\tools\net452\xunit.console.exe c:\Projects\Platform\src\Foundation\Dictionary\tests\bin\Debug\Sitecore.Foundation.Dictionary.Tests.dll


8. Push a NuGet package into a repository on an example of Octopus with API secret key
When you create a versioned package and may want to push that into NuGet repository, you will rely on nuget push command. A snippet shown below demonstrates that on the example of Octopus Deploy, passing it API key. A code below use to substitute current site context, pretty easy:
NuGet.exe push Platform.68.0.0.nupkg -ApiKey API-UZYKODSIIRJZQF25QP2T7WFWG -Source http://winbuildserver.local:8080/nuget/packages


9. Upload a file to FTP using WebClient via PowerShell
One more trick to avoid using system dependencies by calling .NET commands via PowerShell. This time it is for sending a file over FTP to the remote server. Quite a disadvantage is storing the details open-text, including a password. That should be parametrised, of course:
$File = "c:\Projects\archive.zip"$ftp = "ftp://hostname-and-port\username:Pa55w0rd@domain/path/more-folder/archive.zip"

"ftp url: $ftp"$webclient = New-Object System.Net.WebClient
#$uri = New-Object System.Uri($ftp)

$uri = [uri]::EscapeUriString($ftp)

"Uploading $File..."$webclient.UploadFile($uri,$File)


10. Transform of web.config settings for "role" and "localenv" variables
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.
I have a separate blog post dedicated to this task.

11. Deserialize Unicorn from a PowerShell
If you are using Unicorn in a Continuous Delivery pipeline, you need to make unicorn deserialise (sync) items into Sitecore from a console. Luckily, Unicorn has support for doing that by calling sync.ps1 that uses MicroCHAP.dll and supporting script Unicorn.psm1, passing Unicorn URL and a secret key as a parameter. That secret key can be configured at Unicorn.SharedSecret.config. Make sure there is unrestricted execution policy. Usage is pretty easy:
sync.ps1 -secret 749CABBC85EAD20CE55E2C6066F1BE375D2115696C8A8B24DB6ED1FD60613086 -url http://platform.dev.local/unicorn.aspx


12. Create new IIS hostname winding for existing website
When installing Sitecore with SIF, it makes sense also to add all your additional custom domain names bindings into IIS website, that has been just created by SIF, ideally should be done for both HTTP on port 80 and HTTPS on 443. The last one also requires creating a self-signed certificate for given hostname. So you may create a step having this command at the very end of installation PowerShell script:
	$Hostname = "YourSiteCustomHostname.dev.local"$SiteNameHere = "$SolutionPrefix.$SitePostFix" # "Platform.dev.local"

write-host "Adding IIS Hostname Binding for website (HTTP and HTTPS)"
write-host "Site name: $SiteNameHere" write-host "Hostname:$Hostname"

$cert=(Get-ChildItem cert:\LocalMachine\My | where-object {$_.Subject -match "CN=$Hostname" } | Select-Object -First 1) if ($cert  -eq $null) {$cert = New-SelfSignedCertificate -DnsName $Hostname -CertStoreLocation "Cert:\LocalMachine\My" }$binding = (Get-WebBinding -Name $SiteNameHere | where-object {$_.protocol -eq "https"})
if($binding -ne$null) {
try{
Remove-WebBinding -Name $SiteNameHere -Port 80 -Protocol "http" -HostHeader$Hostname
Remove-WebBinding -Name $SiteNameHere -Port 443 -Protocol "https" -HostHeader$Hostname
}
catch{
write-host "$SiteNameHere yet does not have a binding for$Hostname"
}
}

New-WebBinding -Name $SiteNameHere -IPAddress "*" -Port 80 -HostHeader$Hostname
New-WebBinding -Name $SiteNameHere -Port 443 -Protocol https -HostHeader$Hostname
(Get-WebBinding -Name $SiteNameHere -Port 443 -Protocol "https" -HostHeader$Hostname).AddSslCertificate($cert.Thumbprint, "my") 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.

The vast majority of my readers are already familiar with Unicorn (if not - please take your time to familiarize with it by this link) and understand the principles of how it works. In few words, it serializes your Sitecore items into text files so that you can store them under source control along with the rest of your solution code.Then you can sync these files back into Sitecore, either manually from its admin page, or automatically by PowerShell or deployment script on CI / CD pipeline.  If the item has was changed, Unicorn automatically modifies its serialization file (so that changed file goes to source control to all other developers) and the reverse - newer items from source control will update those in Sitecore. So far, so good.

Problem: your solution infrastructure has numerous environments from lower (Dev) to higher (Prod) where the actual site is running. But not just the site itself - Sitecore also is there. Your content editors will be using Sitecore on prod, bringing plenty changes. But with next deployment all that new content will be automatically overwritten by older serialized items. What is good - now we have Helix principles, that classifies two types of Sitecore items - definition items to be passed along with the rest of solution code and actual content item. For Content items source of truth is Production, while for definition items source of truth is Development. But how do we actually define and set up content items?

Solution: the best automated approach also comes with Unicorn, however almost not documented and very little people know about it. It is called NewItemsEvaluator. Let's take a look at how it works:

By default, Unicorn predicate runs with Master Evaluator, which always overwrites Sitecore item with whatever comes from serialization. Because of Master Evaluator, content items on production will be overwritten by serialization with each deployment's sync unless actions are taken. NewItemsEvaluator also creates an item in Sitecore, but only if that item does not (yet) exist in the target environment. But if the item with such ID already exists there - it won't be affected and overwritten.

Firstly, we need to define what content items are. That typically will be all the items underneath /sitecore/YourProjectName/Home including Home item itself. Also, that will be all items within /sitecore/media library/Project/YourProjectName and items within /sitecore/content/YourProjectName/Global including Global item itself and its folder-based children that have insert options assigned.

Secondly, we need to create a new unicorn predicate for those content items identified. That predicate should be driven by New Item Evaluator:

<evaluator type="Unicorn.Evaluators.NewItemOnlyEvaluator, Unicorn" singleInstance="true" />

Thirdly, we need to exclude content items from the default website predicate driven by Master Evaluator. Luckily, Unicorn comes with handy exclusion syntax (more examples can be found at this link):
<include name="Some children" database="master" path="/sitecore/content/YourWebsite">
<exclude children="true">
<except name="Settings" />
<except name="Global" />
</exclude>
</include>


Finally, your website project layer serialization configuration file will have two predicate configurations similar to one below:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:localenv="http://www.sitecore.net/xmlconfig/localenv/">
<sitecore role:require="Standalone or ContentManagement">
<unicorn>
<configurations>
<configuration
name="Project.YourProjectName.Website"
description="YourProjectName content"                     dependencies="Foundation.*,Feature.*,Project.Common"
extends="Helix.Project">

<predicate>
<include name="Project.YourProjectName.Layouts" database="master" path="/sitecore/layout/layouts/Project/YourProjectName" />                        <include name="Project.YourProjectName.PlaceholderSettings" database="master" path="/sitecore/layout/placeholder settings/Project/YourProjectName" />
<include name="Project.YourProjectName.Content" database="master" path="/sitecore/content/YourProjectName">                            <exclude path="/sitecore/content/YourProjectName/Home" />                            <exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Button Styles" />                            <exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Font Styles" />                            <exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Image Container Css" />                            <exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Landing Css Classes" />                            <exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Links/Footer Menu" />                            <exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Links/Header Menu" />                            <exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Links/Social Media" />                        </include>

<include name="Project.YourProjectName.Media" database="master" path="/sitecore/media library/Project/YourProjectName">                            <exclude children="true" />
</include>

<include name="Project.YourProjectName.ExperienceEditor" database="core" path="/sitecore/client/Applications/ExperienceEditor/Pipelines/InitializePageEdit" />                    </predicate>

<!-- roles and user data stores skipped here -->
</configuration>

<configuration name="Project.YourProjectName.Website.AuthoredContent"                     description="Sample site content (New Item Provider)"
dependencies="Foundation.*,Feature.*,Project.YourProjectNamev.Website"                     patch:after="configuration[@name='Project.YourProjectName.Website']"                     extends="Helix.Project">

<targetDataStore
physicalRootPath="$(sourceFolder)\$(layer)\\$(module)\serialization\Content"
type="Rainbow.Storage.SerializationFileSystemDataStore, Rainbow"
useDataCache="false"
singleInstance="true" />

<evaluator type="Unicorn.Evaluators.NewItemOnlyEvaluator, Unicorn" singleInstance="true" />

<dataProviderConfiguration enableTransparentSync="false"
type="Unicorn.Data.DataProvider.DefaultUnicornDataProviderConfiguration, Unicorn" />

<predicate type="Unicorn.Predicates.SerializationPresetPredicate, Unicorn" singleInstance="true">
<include name="Project.YourProjectName.Home" database="master" path="/sitecore/content/YourProjectName/Home" />
`