Reformatting config XML so it’s easier to diff

Every so often pretty much every developer ends up in a situation where they’re looking at a bug that manifests on one platform, but not on another. The sort of bug where you end up spending hours looking through log and config files for a subtle difference. I found myself looking into just this sort of bug recently, but on a site where (to my frustration) the config files were full of comments and whitespace differences across platforms that made diffing really hard**. Spotting that subtle bug-causing difference is pretty much impossible when your diff is full of noise… So how can we fix that? Continue reading

A little PowerShell hack for sending files to a remote machine

I was asked to do some configuration on a remote computer recently, and discovered that the security-concious network admins had locked down the ability for me to share my local computer’s files with the server via RDP and the ability to get to services like OneDrive. I had a collection of config files I had been asked to deploy, and manually creating each file on the server and copying over its contents seemed like a lot of hassle. So I tried a trick with PowerShell to make my life easier… Continue reading

Dealing with .tar.gz files on Windows Server

A couple of times recently, I’ve found myself needing to deploy files that come wrapped in a .tar.gz archive onto servers. On your desktop that’s not too much of a problem – you just run the installer for your preferred 3rd party tool, or maybe use the new Unixy shell and you get on with it. But on client servers security can be higher and you don’t always get the option to run any old installer. So I needed an alternative… Continue reading

Pasting multiple cells into Excel from PowerShell

Sometimes the learning point from working on a misbehaving Sitecore server isn’t related to the CMS. Recently I learned something useful about Excel while I was addressing some other issues. Not sure if this is “so trivially simple I’m just the last one to realise” or whether it’s a really useful bit of trivia – but just because someone else might benefit: Continue reading

What do you mean you can’t fetch that item by its path?

There are some things in Sitecore that you just take for granted will work. Loading items is a good example of this. I’ll admit that user error can get in your way, but usually if you can see an item in the content tree, you can write code that will load it without issues. So I’ll admit I was pretty confused when I came across a scenario recently where this did not appear to work correctly. In case anyone else hits this challenge, here’s what happened: Continue reading

Obscure looking SIF certificate errors

Continuing my voyage of obscure-error-discovery around Sitecore 9, this week SIF has been putting a lot of red into console windows for me. I’m not sure how many people will have this scenario, but if you have multiple people who all need to install their own Sitecore 9 instance onto one machine, this may be of interest:

My issue…

Working in a web agency we have multiple development teams who share development and test infrastructure. Up until now we’ve been happily installing multiple instances of Sitecore onto web servers without issues using SIM. But recently I was the second person to try and use some shared scripts to install a site via SIF onto one machine. And it was not happy.

The first step in the SIF process is to generate the certificate used to communicate with xConnect:

#install client certificate for xconnect 
$certParams = @{     
    Path = "$PSScriptRoot\xconnect-createcert.json"     
    CertificateName = "$prefix.xconnect_client" 
}
Install-SitecoreConfiguration @certParams -Verbose 

But that bombed out for me. The logs included:

[---------------------------------------- CreateSignedCert : NewSignedCertificate ------------------------------------]
VERBOSE: Resolving ConfigFunction extension 'GetCertificate'
VERBOSE: Resolved 'Invoke-GetCertificateConfigFunction'
VERBOSE: Invoke-GetCertificateConfigFunction
VERBOSE: Id: DO_NOT_TRUST_SitecoreRootCert
VERBOSE: CertStorePath: cert:\LocalMachine\Root
VERBOSE: Found Cert with thumbprint: 763C0AA20CB3A8038928AC09CDD6DF2F7CFB2EBA 27A5D8877F35151B0DA7A27B8BDFA44B0006CB47
**********************
Command start time: 20180710101333
**********************
PS>TerminatingError(New-SignedCertificate): "Cannot process argument transformation on parameter 'Signer'. Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Security.Cryptography.X509Certificates.X509Certificate2"."
Install-SitecoreConfiguration : Cannot process argument transformation on parameter 'Signer'. Cannot convert the 
"System.Object[]" value of type "System.Object[]" to type 
"System.Security.Cryptography.X509Certificates.X509Certificate2".
At C:\Configs\HabitatHome\s9install-web.ps1:19 char:1
+ Install-SitecoreConfiguration @certParams -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Install-SitecoreConfiguration
Install-SitecoreConfiguration : Cannot process argument transformation on parameter 'Signer'. Cannot convert the
"System.Object[]" value of type "System.Object[]" to type
"System.Security.Cryptography.X509Certificates.X509Certificate2".
At C:\Configs\HabitatHome\s9install-web.ps1:19 char:1
+ Install-SitecoreConfiguration @certParams -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Install-SitecoreConfiguration

Working towards a solution

A bit of googling for that error brought up a Stack Overflow post that explained the background: What Powershell really means is “I asked the cert store for a specific certifciate ID, and I got back more than one” – and you can’t use a list-of-certificates where the script expected a single certificate…

You can see this in the log output, because there are two cert thumbprints listed:

VERBOSE: Found Cert with thumbprint: 763C0AA20CB3A8038928AC09CDD6DF2F7CFB2EBA 27A5D8877F35151B0DA7A27B8BDFA44B0006CB47

Following the comments under the accepted answer, I deleted the “newer” of the two certificates, and tried to work out what was going on. The SIF script had created a new version of the “DO_NOT_TRUST_SitecoreRootCert” certificate – which didn’t entirely make sense to me. That should have already existed, since there was already an instance of Sitecore running happily on the machine. And looking at the certificate store for the local machine, it was indeed there:

Seeing that, the first idea that popped into my head looking at this was “ah – perhaps that needs to be in my store as well?” so I copied the root cert from the machine store to my store. That got me a different, and equally obscure error:

For the benefit of Google, the error this time was:

[---------------------------------------- CreateSignedCert : NewSignedCertificate ------------------------------------]
VERBOSE: Resolving ConfigFunction extension 'GetCertificate'
VERBOSE: Resolved 'Invoke-GetCertificateConfigFunction'
VERBOSE: Invoke-GetCertificateConfigFunction
VERBOSE: Id: DO_NOT_TRUST_SitecoreRootCert
VERBOSE: CertStorePath: cert:\LocalMachine\Root
VERBOSE: Found Cert with thumbprint: 27A5D8877F35151B0DA7A27B8BDFA44B0006CB47
VERBOSE: Performing the operation "New-SignedCertificate: New signed certificate for HabitatHome.xconnect_client" on target "C:\Certs".
VERBOSE: Searching certificates in cert:\LocalMachine\My for Name HabitatHome.xconnect_client
VERBOSE: Failed to find certificate with Name HabitatHome.xconnect_client
VERBOSE: New-SignedCertificate: Create a signed certificate for 'HabitatHome.xconnect_client'
VERBOSE: New-SignedCertificate: Using PKI parameters for Windows Server 2016 and Windows 10
**********************
Command start time: 20180710102049
**********************
PS>TerminatingError(New-SelfSignedCertificate): "The certificate does not have a property that references a private key. 0x8009200a (-2146885622 CRYPT_E_UNEXPECTED_MSG_TYPE) CertEnroll::CSignerCertificate::Initialize: Cannot find object or property. 0x80092004 (-2146885628 CRYPT_E_NOT_FOUND)"
>> TerminatingError(New-SelfSignedCertificate): "The certificate does not have a property that references a private key. 0x8009200a (-2146885622 CRYPT_E_UNEXPECTED_MSG_TYPE) CertEnroll::CSignerCertificate::Initialize: Cannot find object or property. 0x80092004 (-2146885628 CRYPT_E_NOT_FOUND)"
Install-SitecoreConfiguration : The certificate does not have a property that references a private key. 0x8009200a 
(-2146885622 CRYPT_E_UNEXPECTED_MSG_TYPE) CertEnroll::CSignerCertificate::Initialize: Cannot find object or property. 
0x80092004 (-2146885628 CRYPT_E_NOT_FOUND)
At C:\Configs\HabitatHome\s9install-web.ps1:19 char:1
+ Install-SitecoreConfiguration @certParams -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Install-SitecoreConfiguration
Install-SitecoreConfiguration : The certificate does not have a property that references a private key. 0x8009200a
(-2146885622 CRYPT_E_UNEXPECTED_MSG_TYPE) CertEnroll::CSignerCertificate::Initialize: Cannot find object or property.
0x80092004 (-2146885628 CRYPT_E_NOT_FOUND)
At C:\Configs\HabitatHome\s9install-web.ps1:19 char:1
+ Install-SitecoreConfiguration @certParams -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Install-SitecoreConfiguration

At least this time it’s only reporting a single certificate thumbprint – so it’s a step forward at least. Now, the important message this time seems to be “The certificate does not have a property that references a private key” – and if you go and look at the details of the root certificate in the trusted store, you’ll see that it does not have a private key attached:

That makes some sense – you can’t use the “root” certificate to sign anything if you don’t have its private key. Signing relies on the private part of a key for its security.

Checking with the person who’d done the first SIF install on this machine, their copy of this certificate did have a private key.

So the answer is…

…that if you’re part of a group of devs who are sharing some “standard” SIF scripts and you happen to be the second (or subsequent) Windows user to install a Sitecore instance on a machine using them, you need to ensure that you have a copy of the original root certificate, which includes the private key.

You can get that by asking the user who did the install first to export it for you, from their personal certificate store:

If they export the certificate including the key, and you import it into your store, then the SIF script will complete successfully.

Now I know where the error is coming from, I guess there are some other approaches that could be used to solve this. Whether these are relevant to you probably depends on your precise working patterns with multiple installs:

  • If you don’t rely on all the sites sharing the same root certificate, you could adjust the SIF scripts to generate a separate root certificate for each site (by ensuring the root certificate’s ID has the instance name in it perhaps?)
  • If you do want all the sites to share a root certificate, but you don’t want to have to mess about with moving certificates about, you may be able to adjust the SIF scripts to use the machine’s personal cert store rather than the user’s when generating and reading the shared root cert.
  • [Edited to add] And as pointed out in the comments below, just having a single user account that everyone shares for doing installs would solve the problem too…

But those are things to investigate another day…

A hack for role-based config in v8 deployments

It’s a pretty common requirement that deploying instances of Sitecore will require slightly different configuration on different servers. Different roles, like content management and content deployment, will require different settings and features to work. So it’s not surprising that there are a variety of approaches to how you manage this configuration in your projects.

In the past I’ve often made use of separate config files, where you have a file for “config changes needed on all servers” and then further files for “config changes needed for CM servers”, and even down to the level of “config changes needed on server CD01” if necessary. This works fine if your deployment process understands which files should go on which servers.

Recently, however, Sitecore have started to offer a “role based configuration” approach in the configuration of v9 – so you can deploy a single config file and the server can pick and choose sections of its configuration based on what role it is performing. But back in the real world, most of us are still supporting V8 (and older) sites, so is it possible for them to adopt something similar to this idea? Here’s one approach that achieves something similar: Continue reading