Skip to main content

Microsoft Exchange module

The Exchange module deploys a certificate to an on-premises Microsoft Exchange Server (2013 / 2016 / 2019 / SE) over WinRM using the Exchange Management Shell (EMS). It imports the PKCS#12, enables it for the selected Exchange services, optionally restarts them, and optionally removes the previous certificate.

Exchange identifies certificates by thumbprint (SHA-1), which is new on every renewal, so each deploy is import-new → enable-new → optional remove-old — there is no in-place update.

On-premises only

Exchange Online (Microsoft 365) is not supported. Microsoft owns and rotates the service TLS certificates for *.outlook.com / *.protection.outlook.com; the *-ExchangeCertificate cmdlets exist only in on-premises Exchange. An Exchange Online target is not a thing this module can serve.

Prerequisites

  • WinRM reachable on the Exchange server (5986 HTTPS by default, or 5985 HTTP).
  • The target is a domain-joined Exchange server (it needs its own Kerberos/KDC access — always true in a real deployment; this is how the module reaches AD, see below).
  • A service account enabled for remote PowerShell (Set-User -RemotePowerShellEnabled $true) with an Exchange RBAC role that grants Import-/Enable-/Remove-ExchangeCertificate (e.g. an Organization Management or a scoped role-group member).
  • For the all_org / remote-server restart paths, the account needs rights on each server.
How the module reaches Active Directory (the double-hop, handled for you)

Running the Exchange cmdlets through the local snap-in over a WinRM shell fails on any real domain box — every AD-touching cmdlet returns Active Directory operation failed … The supplied credential … is invalid (ADInvalidCredentialException), even for a Domain Admin. The WinRM session is a network logon with no credential it can present onward to a domain controller (the classic Kerberos double-hop), and constrained delegation does not fix it (the cmdlet process impersonates the user, not the machine).

CertAutoPilot handles this automatically: on the Exchange box it opens a fresh Kerberos implicit-remoting session to Exchange's own RBAC endpoint — New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://<fqdn>/PowerShell/ -Credential … — where the Exchange Trusted Subsystem does the AD work under its own identity. You do not need to configure CredSSP or Kerberos delegation. Because that RBAC endpoint only accepts Kerberos, the Exchange target is Kerberos-only (auth_type is locked to kerberos, and the domain realm is required); address the server by its AD FQDN so the KDC can resolve its HTTP/<host> SPN.

Native flow (implicit remoting to the Exchange RBAC endpoint)

The module connects over WinRM, then on the box opens the Exchange remoting session and imports the management cmdlets; per resolved server it runs:

# on the Exchange box — a fresh Kerberos session to the RBAC endpoint (no double-hop):
$cred = New-Object PSCredential('CORP\svc-certdeploy', $securePw)
$s = New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri ('http://' + ([Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName) + '/PowerShell/') `
-Authentication Kerberos -Credential $cred
Import-PSSession -Session $s -CommandName Import-ExchangeCertificate,Enable-ExchangeCertificate,`
Remove-ExchangeCertificate,Get-ExchangeCertificate,Get-ExchangeServer -AllowClobber | Out-Null

Import-ExchangeCertificate -Server $srv -FileData ([IO.File]::ReadAllBytes($pfx)) -Password $pw # → new Thumbprint
Enable-ExchangeCertificate -Server $srv -Thumbprint <new> -Services IIS,SMTP -Force
# optional: iisreset $srv / Restart-Service MSExchangeTransport …
# optional: Remove-ExchangeCertificate -Server $srv -Thumbprint <old> -Confirm:$false
Remove-PSSession $s

-Force suppresses the SMTP default-cert replacement prompt; -Confirm:$false suppresses the destructive remove prompt. -FileData (byte array) is used on all versions — the -FileName/UNC parameter was removed in Exchange 2016 CU23 / 2019 CU12. The inner session is always Kerberos — the Exchange /PowerShell RBAC endpoint does not accept NTLM.

Authentication & Kerberos setup

Exchange is Kerberos-onlyauth_type is locked to kerberos (the RBAC endpoint accepts nothing else, and the realm-based principal must be consistent across the outer and inner legs). Set it up as follows:

  • Address the server by its AD FQDN (never an IP — the Kerberos SPN HTTP/<fqdn> has no KDC entry for an IP).
  • domain = the uppercase Kerberos realm (e.g. CORP.LOCAL), required.
  • credential username = the bare account name (e.g. svc-certdeploy) — the module builds the principal as <username>@<realm>. Do not prefix DOMAIN\.
  • The CertAutoPilot host needs a krb5.conf, FQDN resolution, and KDC reachability — follow the full WinRM Kerberos setup (krb5.conf example, /etc/hosts, container mounts).

The account must also have Exchange RBAC for the certificate cmdlets; a missing role surfaces as EXCHANGE_EMS_UNAVAILABLE (the /PowerShell session opens but the cmdlets are not imported).

Create the credential

Settings → Distribution → Credentials → New, type Exchange WinRM:

{ "username": "svc-certdeploy", "password": "<password>" }

Use the bare username (no DOMAIN\ prefix) — Exchange is Kerberos-only, so the module builds <username>@<realm> from the target's domain (realm) field. One credential is reusable across targets.

Create the target

Settings → Distribution → Targets → New, type Microsoft Exchange:

FieldNotes
hostnameExchange server to connect to over WinRM (required).
portWinRM port. Default 5986 (HTTPS) / 5985 (HTTP).
use_tlsConnect over WinRM HTTPS (5986). Default on; off uses HTTP (5985).
tls_skip_verifySkip WinRM HTTPS listener-cert verification (self-signed listeners). Insecure — trusted networks only.
auth_typekerberos only (locked) — the Exchange RBAC endpoint accepts nothing else.
domainKerberos realm (uppercase, e.g. CORP.LOCAL). Required.
servicesAny of IIS,SMTP,POP,IMAP. Default IIS,SMTP. (UM, UMCallRouter, Federation are also accepted via API for Exchange 2013/2016 — UM was removed in 2019/SE.)
apply_modesingle (this server, default), explicit_list (a servers list), or all_org (every Get-ExchangeServer, for a DAG).
serversServer names — required for explicit_list.
restart_servicesRestart the bound services after enabling (iisreset / Restart-Service). Default off.
delete_old_certRemove the previous certificate after enabling. Default off. Does not affect rollback availability — rollback re-deploys from CertAutoPilot's artifact history.
do_not_require_sslPass -DoNotRequireSsl when enabling IIS.

Which services?

  • IIS — OWA, ECP/EAC, EWS, ActiveSync, OAB, MAPI/HTTP, Autodiscover. The common case. Microsoft recommends an iisreset afterwards, or OWA may keep serving the old certificate.
  • SMTP — external/edge SMTP TLS. Enabling replaces the default self-signed SMTP cert (handled with -Force); usually only needed for edge/external transport.
  • POP / IMAP — client TLS for those protocols. Don't assign a wildcard cert here — set the FQDN via Set-PopSettings / Set-ImapSettings.

Multi-server / DAG

Certificates are per-server (each server's local store). apply_mode: all_org enumerates every Get-ExchangeServer (Edge Transport servers are excluded — they aren't domain-joined and can't be reached via -Server) and applies the cert to each server in its own bounded operation, so one unreachable/offline server is reported (the target result is downgraded to partial with a warning) rather than aborting the others, and a large DAG can't blow a single command timeout. command_timeout_seconds is therefore per server, not for the whole batch. The thumbprint is identical across servers. Use explicit_list to target a subset, or one target per server composed via a target group. all_org re-resolves the org on every run, so a server added since the last deploy is picked up automatically.

Service restarts run against each server via Get-Service -ComputerName $s | Restart-Service (the Windows PowerShell 5.1 remote pattern; IIS = the W3SVC/WAS services), so restarting remote servers needs the RPC/SCM path reachable, not server-to-server WinRM.

Rollback

Supported via previous-version re-deploy: a rollback re-imports a previous retained certificate version and re-enables it on the bound services — the module's normal deploy path, fed older material. It no longer depends on delete_old_cert having been off: the previous version comes from CertAutoPilot's own artifact history, not from a certificate kept on the server. During a rollback run the delete_old_cert behavior is suppressed, so the newer certificate stays on the server and rolling forward again is instant. Eligibility, the version picker, and auto-rollback: Rollback.

If restart_services is on and a restart fails on some server, the module deliberately does not record the deployed fingerprint. The stored fingerprint is blanked, so the next run — a job retry, a repeat rollback, or the next deploy — re-executes instead of skipping as already-current, and re-attempts the restart. Import and enable are idempotent, so the repeat run is safe.

Troubleshooting

ErrorMeaning
EXCHANGE_CONNECTWinRM unreachable — host/port/network (retried).
EXCHANGE_AUTHWinRM authentication rejected — username/password/auth-type, or a TLS-verify / Kerberos misconfiguration (terminal, not retried).
EXCHANGE_EMS_UNAVAILABLEThe Exchange /PowerShell RBAC session did not open or its cmdlets did not import — not a real Exchange server (Mailbox/CAS), remote PowerShell disabled for the account, or the account lacks an Exchange RBAC role for the certificate cmdlets.
EXCHANGE_CREDENTIAL_MISSINGNo or malformed credential — the exchange_winrm username/password is missing.
EXCHANGE_PFX_FAILEDCould not read the certificate/key or build the PKCS#12 to import.
EXCHANGE_IMPORTImport-ExchangeCertificate failed — cert/key or permissions.
EXCHANGE_ENABLEEnable-ExchangeCertificate failed — services/thumbprint.

A failed removal of the previous certificate does not fail the deploy (the new certificate is already enabled) — it surfaces as a warning on the target result so you can remove the stale certificate manually.

Device lock scope

Concurrent deploys serialize on the connected server's WinRM lock (shared with the IIS and WinRM modules). all_org fans out to every Exchange server from that one connection — so two exchange targets pointed at different servers of the same organization do not serialize against each other. Don't schedule two such targets into the same maintenance window.

See also