Certify post request PS script. Error when called by Certify


#1

My problem is I have made a script which starts a Exchange shell PSsession.
The scrips runs fine if I execute it line by line in PowerShell, or if I right click on it in explorer and run.
However, when it is called via certify after a new certificate is produced it fails.

Having just got into powershell I am not 100% that it is not an error in my script, however it seems odd that I can run it on its own and there are no errors. Yet when it is called by Certify there is an error.

Here is the section of the script causing the issues:

$password = Get-Content -Path 'c:\Certificate_Update\securepassword.txt'
$pw = ConvertTo-SecureString -String $password


$cred = New-Object System.Management.Automation.PSCredential ("Wookies-Domain\Administrator", $pw)
$uri = 'http://Exchange-Server/PowerShell/'
## Starts remote Exchange shell session
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $uri -Authentication Kerberos -Credential $Cred

## Imports remote Exchange shell session to this Machine
Import-PSSession $Session

The error I get is:

ConvertTo-SecureString : The system cannot find the path specified.

At C:\Certificate_Update\Update_Old_Cert.ps1:40 char:7
+ $pw = ConvertTo-SecureString -String $password
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [ConvertTo-SecureString], CryptographicException
    + FullyQualifiedErrorId :  ImportSecureString_InvalidArgument_CryptographicError,Microsoft.PowerShell.Commands.ConvertToSecureStringCommand

TerminatingError(New-Object): "Exception calling ".ctor" with "2" argument(s):
"Cannot process argument because the value of argument "password" is null.
Change the value of argument "password" to a non-null value.""

New-Object : Exception calling ".ctor" with "2" argument(s): "Cannot process
argument because the value of argument "password" is null. Change the value of
argument "password" to a non-null value."

It is saying $password is null, due to not being able to find the path for the text file with the encrypted standard string.
Can’t work out what I have done wrong. Is it maybe some permissions thing as the script is being run by certify?
Or maybe its just a simple newbe error in my script?
Just seems odd that I only get the error when the script is called by Certify.

Thanks for any help Dave


#2

My script was calling an file with a encrypted standard string used as a password.
This was encrypted as Admin.
Certify runs as a service set to Local system. So when the script tried to access the password file it failed due to wrong privileges.
Setting the service to run as admin cured the problem.


#3

Hi Dave,
Our scripting docs do now say that the background service runs as Local System, so hopefully others will see that.

The alternative would be to use psexec to run as local system when encrypting your file.

The current disadvantages of changing the sevice account user are:

  • updating the app will revert the service to local system, so you need to remember to set the account back.
  • weirdness can happen with private keys. Windows has a user specific store for private keys and this can affect whether IIS is able to access the key or not. Admin is ok, but obviously the password changes frequently :wink:

#4

Not only does the PowerShell script you’re calling need to invoke the Exchange cmdlets, but you need to run Certify as a user that has permission. Here’s how I did it.

  1. Create a managed service account for Certify by following these steps (as a domain admin or higher):

Import-Module ActiveDirectory
$serviceAccount = New-ADServiceAccount “Certify Exchange renewer” -SAMAccountName “CertifyExch$” -PassThru -RestrictToSingleComputer
Install-ADServiceAccount $serviceAccount
Test-ADServiceAccount $serviceAccount
Add-ADPrincipalGroupMembership -Identity $serviceAccount -MemberOf “Server Management”

  1. Go into Services, right-click the Certify service, and choose Properties. In the Run As field, type YOURDOMAIN\CertifyExch$ as the username, and leave the password blank.

  2. Finally, edit Certify’s MSExchangeServices.ps1 script. Right before the final line, add this:

Add-PSSnapIn Microsoft.Exchange.Management.PowerShell.E2010

Yes, that last line should say “E2010,” even on newer versions of Exchange. I tested this on Exchange 2016 and it successfully requested a certificate, installed it, and set it.

Note: If you’re still using Windows Server 2008 R2, remove the RestrictToSingleComputer switch from step one. 2008 R2’s New-ADServiceAccount creates MSA’s by default (as opposed to gMSA’s).

Finally, if you’ve been messing with Certify’s default settings, make sure that you’re using an RSA public key. Exchange 2016 and older do not support the newer, faster ECDSA certificates. Otherwise, this script will silently fail.