We had an issue with the Secure Store Service on SharePoint 2016 when we tried to create a new instance in partition mode for a multi-tenancy farm.
The main issue, it was impossible to generate a master key from powershell by using Update-SPSecureStoreMasterKey
command - no error, and some times
Didn't perform that operation...
or via the central administration
"Failed to generate/refresh key due to error: bla bla blaaa.
For your information, the account used to perform those operations is Farm Admin and follow Microsoft guidelines in terms of permissions, then the farm topology contains 2 WFE, 2 Apps & 2 SQL Servers.
After a lot of investigation, for an unknown reason, the method
"ChangeKey" in the "Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplicationProxy" doesn't work as expected.
So, to fix that first part you have to execute
"ChangeMasterSecretKey" from the instance of "Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplication".
Well, you think it's fine you can can see from the central admin that the key is defined, nooooope a new error will occur when you tries to add a target application:
Unable to get the master key.
It's a joke, absolutely not...
Fine, if you try now to "refresh the master key"
by the central admin with the same passphrase, yataaa, the secure store works as expected.
By now, how to replicate those actiona to automate that for the next deployment ?
Have a look on the class definition "Microsoft.Office.SharePoint.ClientExtensions.SecureStoreAdministration.ChangeDBKey"
at the method "**OkButton_Click**"
, you'll see activities performed to generate or refresh a master key are not the same performed by PowerShell commands (**Update-SPSecureStoreMasterKey**/Update-**SPSecureStoreApplicationServerKey**
).
Finally, the powershell code bellow could save your day. Enjoy.
$sa = Get-SPServiceApplication <Your_SercureStoreApplication_Identity>;
$proxy = Get-SPServiceApplicationProxy <Your_SercureStoreApplicationProxy_Identity>;
$sp_secure_store_passpharse_new = "<Your_PassPhrase>";
try{
##BUG with Update-SPSecureStoreMasterKey => Didn't work properly
#HotFix: Reverse engineering on classes:
#-> Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplication
#-> Microsoft.Office.SecureStoreService.Server.SecureStoreServiceApplicationProxy
#-> Microsoft.Office.SecureStoreService.Server.KeyManagement.KeyManager
#-> Microsoft.Office.SecureStoreService.Server.CryptoHelper
$ass = $sa.GetType().Assembly
$CryptoHelperType = $ass.GetType("Microsoft.Office.SecureStoreService.Server.CryptoHelper")
$GetPassPhraseHashMethod=$CryptoHelperType.GetMethod("GetPassPhraseHash");
$proxyType = $proxy.GetType();
$IsMasterSecretKeyPopulated = $proxyType.GetMethod("IsMasterSecretKeyPopulated",[Reflection.BindingFlags]"NonPublic,Instance")
$SetChangeKeyPassphrase = $proxyType.GetMethod("SetChangeKeyPassphrase",[Reflection.BindingFlags]"NonPublic,Instance")
$SetKey = $proxyType.GetMethod("SetKey",[Reflection.BindingFlags]"NonPublic,Instance", $null, [type[]]@([string]), $null)
if(-not $IsMasterSecretKeyPopulated.invoke($proxy,$null)){
#ChangeKey with the proxy is buggy
#$proxy.ChangeKey( $proxy.GetChangeKeyToken(), $sp_secure_store_passpharse_new );
#Fallback by using the service application directly
$token = $sa.GetChangeMasterSecretKeyToken();
$sa.ChangeMasterSecretKey($token, $GetPassPhraseHashMethod.Invoke($null, $sp_secure_store_passpharse_new) );
$c=0;
while(-not $IsMasterSecretKeyPopulated.invoke($proxy,$null)){
$c++;
if($c -ge 20){
Write-Error $("The master key cannot be populated!");
}
sleep 1;
}
}
$SetChangeKeyPassphrase.invoke($proxy, @($sp_secure_store_passpharse_new));
$SetKey.invoke($proxy,$sp_secure_store_passpharse_new);
}catch{
Write-Error $("Cannot update the secure store master key.`n{0}" -f $_.Exception.Message);
}