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…

Advertisements

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

Issues with Invoke-Webrequest and IE on servers

I’ve been doing some work with Release Management in Visual Studio Online recently. Overall it’s been a pretty positive experience, but there was one face-palm inducing moment I came across which needs writing down so I don’t fall into the same trap next time I have to do this. When you’re working with local release agents, you mustn’t forget the security settings that your agent’s server is configured with… Continue reading

Embedding resources in PowerShell scripts

A challenge I’ve come across a number of times while working with PowerShell scripts for Sitecore is that scripts sometimes have other files they depend on. Making sure that your scripts and their dependencies stay in sync can be a challenge at times, especally when scripts get reused across an organisation. So what approaches can you use for this? Continue reading

Downloading stuff from dev.sitecore.net

There’s been a bit of a theme in my recent posts about scripting stuff, and that continues this week. I’ve been looking at some ideas for automating tasks for developers recently, and one of the things I was interested in was being able to get stuff downloaded from dev.sitecore.net without having to do it manually. So, here’s some PowerShell that can help you with that… Continue reading

Low-effort Solr installs

I’m sure I’ve said before that any task you have to do more than once is worth automating. Well recently I’ve found myself needing to install Solr in a variety of places – so obviously my mind turned to automation. There are lots of ways this can be approached, and some people have already had a go at it for their own needs, but here’s my take. Continue reading