I’m facing an issue with deployment of certificate with Echange 2019 CU14 services.
The tool deploy correctly the certificate on IIS default web site and it appears to be deployed successfully in Exchange Admin Console.
However I would like to get this certificate bind to SMTP, POP and IMAP services so I tried to use the out of the box Exchange script provided by Certify The web tool.
But when I try it, I get this error
2024-06-04 22:52:22.837 +02:00 [INF] ---- Beginning Request [Exchange_2019_IIS] ----
2024-06-04 22:52:22.837 +02:00 [INF] Certify/6.0.18.0 (Windows; Microsoft Windows NT 10.0.20348.0)
2024-06-04 22:52:22.840 +02:00 [INF] Beginning certificate request process: Exchange_2019_IIS using ACME provider Anvil
2024-06-04 22:52:22.840 +02:00 [INF] The selected Certificate Authority is: Let’s Encrypt
2024-06-04 22:52:22.840 +02:00 [INF] Requested identifiers to include on certificate: xxxxx [dns];xxxxxxxxx [dns]
2024-06-04 22:52:24.328 +02:00 [INF] Created ACME Order: https://acme-v02.api.letsencrypt.org/acme/order/xxxxxxx
2024-06-04 22:52:24.617 +02:00 [INF] Order is ready and valid. Auth challenges will not be re-attempted.
2024-06-04 22:52:24.617 +02:00 [INF] [Progress] Order authorizations already completed.
2024-06-04 22:52:24.629 +02:00 [INF] Resuming certificate request using CA: Let’s Encrypt
2024-06-04 22:52:24.629 +02:00 [INF] [Progress] Requesting certificate via Certificate Authority
2024-06-04 22:52:30.416 +02:00 [INF] [Progress] Completed certificate request.
2024-06-04 22:52:30.469 +02:00 [INF] [Progress] Performing automated certificate binding
2024-06-04 22:52:32.345 +02:00 [INF] Completed certificate request and automated binding updates
2024-06-04 22:52:32.345 +02:00 [INF] [Progress] New certificate received and standard deployment performed OK.
2024-06-04 22:52:32.346 +02:00 [INF] Performing Post-Request (Deployment) Tasks…
2024-06-04 22:52:32.349 +02:00 [INF] Task [Deploy to Exchange] :: Task is enabled and primary request was successful.
2024-06-04 22:52:32.364 +02:00 [INF] Executing command via PowerShell
2024-06-04 22:52:33.183 +02:00 [ERR] Powershell Task Completed.Error: Add-PSSnapin: Cannot load Windows PowerShell snap-in Microsoft.Exchange.Management.PowerShell.E2010 because of the following error: The type initializer for ‘Microsoft.Exchange.Data.Directory.Globals’ threw an exception.
At line:7 char:1
Error: Enable-ExchangeCertificate: The term ‘Enable-ExchangeCertificate’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:28 char:2
2024-06-04 22:52:33.183 +02:00 [ERR] Deploy to Exchange :: Powershell Task Completed.Error: Add-PSSnapin: Cannot load Windows PowerShell snap-in Microsoft.Exchange.Management.PowerShell.E2010 because of the following error: The type initializer for ‘Microsoft.Exchange.Data.Directory.Globals’ threw an exception.
At line:7 char:1
Error: Enable-ExchangeCertificate: The term ‘Enable-ExchangeCertificate’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:28 char:2
Thanks, I’m not an exchange expert but this error appears to be that there is some exception happening while it’s loading the powershell module for exchange. We haven’t had any similar reports but that doesn’t mean nobody else has encountered this.
From googling it looks similar to this problem nd may be fixed by ensure the latest updates to .net are installed:
I’d suggest reviewing whether the exchange machine running Certify The Web has all the usual admin tools installed.
Our built in task uses this script:
So it’s possible to adapt it and runit as your own script using the Run Powershell Script task. Scripting | Certify The Web Docs
Note that if you add/update a deployment task you don’t need to Request Certificate again to test it, you can save the changes and click the play button next to the task to just use the latest certificate with the task. Otherwise if you keep requesting the same cert the CA starts to rate limit you which isn’t ideal.
I have .NET 4.8.1 installed on that server as I’m running Exchange on windows 2022.
All Exchange admin tools are installed on that server too.
If I run as administrator each command separately in a PowerShell window, I’m able to load the Snapin, run the command Enable-ExchangeCertificate (even if I don’t add any parameters). At least they are recognised by the shell.
cmdlet Enable-ExchangeCertificate at command pipeline position 1
Supply values for the following parameters:
Thumbprint:
Services:
Enable-ExchangeCertificate : Cannot bind argument to parameter ‘Thumbprint’ because it is an empty string.
At line:1 char:1
to summarise, the script (Exchange.ps1 slightly adapted to get it more verbose helping troubleshooting) is executed using local service account on exchange server
to be able to add the Exchange snap-in, I need to tick “Launch new process” in task settings.
Enable-ExchangeCertificate command works, certificate is bind with desired services.
Now, just the cleanup is not working because running the command get-exchangecertificate as local system user trigger an access denied then the variable content is never filled in and cleanup fails.
I’m not a very good PowerShell expert, how can I pass exchange admin credentials within the script prior executing cleanup? Or maybe creating another task using Exchange admin credentials specifically for cleanup and executed after the deployment task.
So it sounds like the original problem is helped by running out of process with a new powershell session, that suggests a conflict or side effect with the DLLs already loaded by our own process.
You can run your script as an impersonated user (e.g. the Exchange Admin) that might help with the cleanup step. It may be work wrapping that in a try catch depending on how important the cleanup is, as cert imported in different ways by different users can have different private key permissions, which could the sticking point here: about Try Catch Finally - PowerShell | Microsoft Learn
I finally got it working. Helped by another script I found on this site.
Maybe not the most efficients script but they do what I need them to do.
Task under Certify the web tool is launched “as a new process” with local service account. AES key generation and password encryption need to be run only once (or when password change or if you want to change the AES key used for password encryption) and outside CTW tool.
=======================================================
3) Cleanup of certificates (separate script)
$ExchUser = "Domain\user"
$PasswordFile = "D:\Scripts\CTW\Password.txt"
$KeyFile = "D:\Scripts\CTW\AES.key"
$Key = Get-Content $KeyFile
$ExchPasswd = Get-Content $PasswordFile | ConvertTo-SecureString -Key $Key
$ExchCred = new-object -TypeName System.Management.Automation.PSCredential -ArgumentList $ExchUser,$Exchpasswd
$ServerSession = New-Pssession -ConfigurationName Microsoft.Exchange -ConnectionURI http://your server.domain/Powershell -Authentication Kerberos -Credential $ExchCred
Import-PSSession $ServerSession -DisableNameChecking -AllowClobber
if ($cleanupPreviousCerts -eq $true)
{
Write-Host ""
Write-Host "Cleaning up previous cert in Exchange"
Write-Host ""
Write-Host "========================================================================================================================================================="
# Find out how many matching certificates we have - the first one is always the one Exchange is currently using
$Count = (Get-ExchangeCertificate -Server $SourceServer -DomainName $SearchDomain).Thumbprint.Count
Write-Host "Number of certificates for"$SearchDomain ":"$Count
#Throw error if nothing is returned, grab the thumbprint if only 1 is found, grab the first entry in the array if more than 1 is found
Switch ($Count)
{
0 { "ERROR: No certificate found!"; Break }
1 { "ERROR: Only current certificate found!"; Break }
2 { $ThumbprintList = (Get-ExchangeCertificate -Server $SourceServer -DomainName $SearchDomain).Thumbprint[1] }
Default { $ThumbprintList = (Get-ExchangeCertificate -Server $SourceServer -DomainName $SearchDomain).Thumbprint[1] }
}
foreach ($Thumbprintitem in (Get-ExchangeCertificate -Server $SourceServer -DomainName $SearchDomain).Thumbprint)
{
Write-Host "Thumbprint for Exchange certificates on"$SourceServer" for "$SearchDomain": "$Thumbprintitem
}
Write-Host "Certificate to be removed has the following thumbprint:"$ThumbprintList
Remove-ExchangeCertificate -Thumbprint $ThumbprintList -Confirm:$false
}
Remove-PSSession $ServerSession
Thanks, glad you got it working. To be clear though there is something unusual going on in the first place as you shouldn’t have had to do any of this.
It would be useful if any other users who see this could confirm if they are seeing the same problem (error loading the exchange powershell modules) and in what configuration.