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.

Exchange needs an RSA certificate

Exchange accepts RSA certificates only — it cannot use ECDSA. CertAutoPilot issues ECDSA P-256 by default, so a certificate destined for an Exchange target must be created with an RSA key (2048 bits or larger).

This is worth stating plainly because the failure is silent on the Exchange side: an ECDSA certificate imports into the Windows certificate store, private key and all, and the import command reports success — but Exchange never lists it and can never bind it to a service. CertAutoPilot therefore checks the key type before deploying and stops with unsupported key type, and the plan preview warns about it before you run anything.

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​

  • A certificate with an RSA key (see the note above).
  • 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).
  • If you turn service restarts on for all_org or explicit_list: CertAutoPilot restarts services by connecting to each Exchange server itself, so WinRM must be reachable from CertAutoPilot to every server in the organization, not only to the one the target names. Each server is dialled by the Active Directory FQDN the organization reports for it (Kerberos builds an HTTP/<fqdn> service principal and cannot use a short name or an IP — you may still list a server by its short name; the run looks the FQDN up), on the same port and scheme as the target. The service account needs permission to restart services on each server. No server-to-server access is required.
  • Edge Transport servers are not supported. They are not domain-joined and have no RBAC /PowerShell endpoint, so no management session can be opened on them. all_org, the health check's server list and the plan preview's "servers left out" warning all ignore them.
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).
# The principal is <username>@<realm> built from the credential and the target's domain:
$cred = New-Object PSCredential('svc-certdeploy@CORP.LOCAL', $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 Get-ExchangeServer,Get-ExchangeCertificate,`
Import-ExchangeCertificate,Enable-ExchangeCertificate,Remove-ExchangeCertificate -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, BEFORE any restart (restarting W3SVC tears down this session):
# Remove-ExchangeCertificate -Server $srv -Thumbprint <old> -Confirm:$false
# optional restart — on the connected server inside this script; for any other
# server over CertAutoPilot's own connection to the FQDN Get-ExchangeServer reports:
# Restart-Service W3SVC, WAS / MSExchangeTransport …
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.

Where the passwords go​

None of the passwords involved is ever written on a Windows command line. Every Exchange operation — a deployment, a dry run, a health check, and the server enumeration behind all_org — first uploads the service account's password to a temporary file on the target that only administrators can read, and the script reads it from there. A deployment adds a second such file for the certificate bundle's own password. Both are deleted when the operation ends, including when it is cancelled or times out.

This is why even a health check writes to the target: it is a probe, but not a read-only one. If the account cannot write to the target's temporary directory, operations fail with "uploading session credential" rather than an authentication error.

Authentication & Kerberos setup​

Exchange is Kerberos-only — auth_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). An IP address or a single-label short name is rejected when the target is saved.
  • 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\.
  • On plain HTTP (5985): the WinRM service on the Exchange server needs AllowUnencrypted=true — the client does not encrypt the WSMan body (applies to every auth type, Kerberos included). Not needed over the default HTTPS 5986.
  • Nothing to install on the CertAutoPilot host — no krb5.conf, no mounts. The Kerberos configuration is built in memory and the domain controller is discovered automatically via the AD DNS SRV record. If this host's DNS cannot resolve AD records, fill the optional KDC Address field on the target instead. Details: WinRM Kerberos.

The account must also have Exchange RBAC for the certificate cmdlets. An account with no Exchange role at all cannot open the /PowerShell session, which surfaces as EXCHANGE_EMS_UNAVAILABLE. An account whose role opens the session but does not expose Import-/Enable-ExchangeCertificate gets no proxy for those cmdlets: the deployment script fails on the missing command and the run reports EXCHANGE_IMPORT / EXCHANGE_ENABLE with the per-server message — and the plan preview warns up front that the cmdlet is not available in this session.

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), by its AD FQDN. An IP address or a single-label short name is rejected when the target is saved — Exchange targets authenticate with Kerberos, which needs the HTTP/<fqdn> service principal. A URL or path is refused too; a :port typed here moves into port, and TLS must match the port (5985 plain / 5986 TLS). An existing target is re-checked only when its hostname, port or TLS setting changes.
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.
kdc_hostOptional KDC pin (host or host:port). Empty = the KDC is discovered via the AD DNS SRV record. Set only when the CertAutoPilot host's DNS cannot resolve AD records.
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. A short name (EXCH02) or an FQDN (exch02.corp.local) both work; the picker stores the FQDN. With restart_services on, a server other than the connected one is restarted over a separate Kerberos-authenticated connection to the FQDN the organization reports for it (looked up with Get-ExchangeServer during the run); a typed FQDN is used only when the organization reports none. An IP address is never dialled — if no FQDN is known for a server, its restart is skipped and reported as a warning naming that server.
restart_servicesRestart the bound services after enabling (Restart-Service; iisreset is never used). Default off.
delete_old_certRemove the previous certificate after enabling. Default off. Only the certificate this target deployed for this certificate last time is removed, never anything else on the server. 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. A wildcard certificate does not work here. Exchange refuses to bind a certificate whose subject is not a fully qualified domain name to POP or IMAP — and it reports that refusal as a warning while still returning success, so the service quietly keeps serving the previous certificate. Either leave POP/IMAP out of the target's services, or name the service FQDN on the server with Set-PopSettings -X509CertificateName / Set-ImapSettings -X509CertificateName and restart the service. IIS and SMTP accept a wildcard normally.
A wildcard certificate with POP or IMAP selected

CertAutoPilot checks the certificate's subject before deploying and warns when this combination would be refused, naming the services and the cmdlet that fixes each one — in the plan preview and again on the run itself. It does not block the deployment: IIS and SMTP still bind correctly. Exchange's own warnings from the import and enable are also reported with the run, so a refused service is visible instead of being hidden behind a green result.

Choosing from the organization (live picker)​

On an Exchange target set to Explicit list, the Servers field can read the organization and offer what is there instead of asking you to type names. The list is what Get-ExchangeServer reports, minus Edge Transport servers (they are not domain-joined and cannot be reached this way), and each row shows the server's role and the full domain name the organization publishes for it.

A picked server is stored by that full domain name (the row is labelled with the short name). That is the name a service restart needs: a restart is a separate Kerberos-authenticated connection from CertAutoPilot to that server, and Kerberos cannot address a short name or an IP. Typed short names work as well — before restarting a server the run asks the organization for its full domain name — so the FQDN rule is: the server is restarted when the organization reports an FQDN for it, or when the entry you typed is one.

One thing on each row is worth reading before you pick: whether the server can be restarted. A server the organization reports no full domain name for takes the certificate but has its restart skipped, with a warning naming that server. The list says so up front.

Listing is read-only: it enumerates servers and touches no certificate. Every field still accepts a typed value, so an organization CertAutoPilot cannot reach never blocks the form. Browsing through a saved target is a project operator action; browsing from a target form you have not saved yet uses the connection details on screen and needs project admin, as does browsing a saved target after changing its host, port, TLS, realm or KDC.

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.

One failure is different: if the Exchange management session itself stops opening partway through (Exchange caps concurrent remote shells per user, so a leaked session can exhaust them mid-run), the remaining servers are not attempted — every one of them would open its session against the same box. The servers already deployed keep their certificate, and the ones never reached are listed by name as failures with the reason "not attempted", so the count you see always covers the whole server list. 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.

Time budget per server​

Each server gets its own budget — twice the command timeout plus 60 seconds for the service restart (660 s with the defaults) — shortened to what the job's execution timeout has left, minus a 15-second reserve so the run can still report. Before each server the run checks the time left: a server that could not get at least 45 seconds, and every server after it, is reported "not attempted (time budget)" (the target is partial with that reason) instead of being started and cut off halfway. The servers already finished keep their certificate and their recorded state.

A server's deploy script is re-run once after a short pause only when the request provably never reached the server (the name did not resolve, or the connection was refused or unroutable). It is never re-run after a timeout, a dropped connection or a server that accepted the request and went quiet — the first script may still be running there — nor when that script restarts services itself (the connected server with Restart services on), after a management-session failure, or when too little time is left. The job log carries one line per server with how long its script took and, when there was one, its service restart.

When services were restarted, the post-deploy validation waits 30 seconds and retries more patiently before it judges the endpoint; if it still cannot confirm the certificate, the distribution is unverified and re-checked automatically five minutes later — the usual case for the connected server, which is restarted last.

Deploy order​

Servers are deployed one at a time, and the order is chosen for you — there is nothing to configure and no dependency between servers to get right.

The rule that matters: the server CertAutoPilot connects to is always deployed last. Every server's Exchange session is opened through that server's /PowerShell endpoint, which IIS hosts. If it were deployed first and restarted W3SVC, the endpoint would go down underneath the servers still waiting, and they would be reported as never attempted. Deploying it last means its restart can only affect work that is already finished. The remaining servers run in a stable alphabetical order, so two runs can be compared line by line. Under explicit_list this overrides the order you typed — for the connected server only.

If a server joins the organization later​

apply_mode defaults to This server only, which is the right default for a standalone box and the wrong one for a DAG that has just grown. A second Exchange server is then simply never deployed to, and its certificate expires without any run ever failing.

CertAutoPilot enumerates the organization during every plan preview, so it compares the roster against what the target would actually reach and warns when servers would be left out, naming them. The plan's own target list shows only the servers the run will touch — not the whole organization.

Set apply_mode: all_org and this takes care of itself: the organization is re-resolved on every run, so a server added after the target was created is picked up automatically. Use explicit_list only when you deliberately want a subset — and remember that list is yours to maintain.

Service restarts are handled per server. On the server CertAutoPilot is connected to, the deployment script restarts the services itself. For any other server, CertAutoPilot opens its own WinRM connection to that server and restarts them there.

That second connection is why the prerequisites ask for WinRM reachability to every server. Earlier releases tried to reach across from the connected server with Get-Service -ComputerName, which cannot work: that call runs outside the Exchange session that solves the Kerberos double-hop, so it had no way to authenticate to the other machine — and the failure was invisible, because the error was swallowed and the deployment reported success with nothing restarted.

The other server is dialled by the FQDN the organization reports for it — the deployment script looks it up with Get-ExchangeServer inside the session it already holds — so a server listed by its short name is restarted too. A typed FQDN is used only when the organization reports none.

A restart that fails never fails the deployment: the certificate is already imported and enabled. It is reported as a warning naming the server, the service and the reason, and the next scheduled run re-executes so the restart is attempted again. If no fully-qualified name is known for a server (the organization reports none and the entry you configured is not one), CertAutoPilot says so, naming the server, rather than attempting a connection it cannot authenticate.

Exchange services with no matching Windows service (UM, UMCallRouter, Federation) are reported as such instead of being silently skipped.

The skip is verified on the server

When the stored fingerprint, thumbprint, services and binding all match, the run does not trust that record on its own. It opens one management session on the connected server and checks every server the record names: the certificate's thumbprint must still be on the server (on the connected server, the local certificate store also confirms the private key) and, when IIS is a requested service, the IIS :443 binding must still carry it. Other servers' IIS bindings are read over their own WinRM connection. Only then does it report success as unchanged ("verified on N server(s)").

If a certificate was removed or re-bound by hand, a binding cannot be read, or the check cannot connect, the run redeploys instead and warns with the reason. SMTP, POP and IMAP bindings cannot be read remotely, so drift there is still invisible; a tls_fingerprint validation endpoint catches it. An unchanged run therefore costs one extra management session, plus one WinRM connection per additional server when IIS is bound.

With restart_services on, the redeploy restarts services only when a mismatch was actually observed (the certificate is gone or present without its private key, or IIS serves another one). When the check merely could not complete — a connection failure or a binding that cannot be read — the certificate is re-enabled but no service is restarted, and the run says so; restart by hand if a server really drifted.

On servers other than the connected one, the private key of an already-present certificate cannot be seen. A public-only copy with the same thumbprint pre-staged on such a server skips the import there, and the enable then fails on that server with Exchange's own error.

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. A repeat rollback to a version the server still holds is skipped only after the server confirms it, and a rollback to a version still in the store re-enables it without importing it again.

Troubleshooting​

ErrorMeaning
EXCHANGE_CONNECTWinRM unreachable, or a Kerberos domain controller that could not be reached (a KDC that rejects the login is EXCHANGE_AUTH instead) — the message names the layer (nothing is listening on …, TLS on/off mismatched with the listener, DNS) and the fix; see WinRM troubleshooting (retried).
EXCHANGE_AUTHThe server refused the WinRM request (HTTP 401/403 — the message quotes the auth schemes the listener offered), or a TLS-verify failure (an untrusted or mismatched listener certificate) / Kerberos misconfiguration (terminal, not retried). Exchange targets are Kerberos-only: bare username, Realm, AD FQDN.
EXCHANGE_EMS_UNAVAILABLEThe Exchange /PowerShell RBAC session did not open (the reason is included) — not a real Exchange server (Mailbox/CAS), remote PowerShell disabled for the account, the account has no Exchange RBAC role at all, the per-user concurrent-session limit is exhausted (a leaked session from a killed run), or the endpoint is down while IIS restarts on the connected server. Retried: the last two clear on their own; a permanent cause simply fails again.
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; or the host refused an upload for its size (MaxEnvelopeSizekb too low — restore it to 500).
EXCHANGE_ENABLEEnable-ExchangeCertificate failed — services/thumbprint.
EXCHANGE_UNSUPPORTED_KEY_TYPEThe certificate does not use an RSA key. Exchange accepts RSA only; re-issue it with an RSA key.

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.

These situations are reported as warnings rather than failures, each naming the server and the reason: a service restart that failed, a server for which no fully-qualified name is known (the organization did not report one and the configured entry is not one, so it cannot be reached to restart services), and an Exchange service with no matching Windows service to restart.

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 — and, when restarts are on, opens a short-lived connection to each other server purely to restart services, which is outside that lock. 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​

Timeouts​

FieldDefaultNotes
connect_timeout_seconds30The TCP connect. Every WinRM request then waits at least 60 s (or this value, if longer) for an answer.
command_timeout_seconds300Applied per server, so an all_org fan-out multiplies it; a server's whole budget is twice this plus 60 s (details).

One WinRM shell on the connected server serves the whole deploy (fewer round trips on slow links); set CERTAUTOPILOT_WINRM_SHELL_REUSE=false in the backend's environment to go back to a shell per command.

A health check gives each Exchange target 60 seconds (the whole check is capped at 100 s), and checks up to five hosts at a time. A dry run checks several targets in parallel (one host at a time per host) with a per-target budget inside the 100-second dry-run limit; a target it had no time left for is reported "not checked". The live servers picker has 90 seconds, since opening the management session alone can take tens of seconds.

Automatic warnings​

On a run:

  • tls_skip_verify is on — "TLS verification disabled — insecure transport". Emitted on every run; the health check lists it as a warning on the target's row.
  • IIS is among the enabled services but restart_services is off — the new certificate will not serve on IIS until someone restarts it. Emitted on every successful run.
  • A requested service restart failed on a server, or could not be attempted: no FQDN is known for the server, the server could not be reached over WinRM, or an Exchange service has no matching Windows service (UM, UMCallRouter, Federation). The run still succeeds; the remote-state fingerprint is blanked so the next run re-attempts.
  • The previous certificate was not removed (delete_old_cert on, Remove-ExchangeCertificate failed) — remove it manually.
  • Some servers failed — "some servers failed: …" with each server's reason (the target result is partial).
  • Servers were not attempted because the management session stopped opening partway through the list — they are named separately.
  • A server recorded by the previous run was not addressed by this one (dropped from explicit_list) while still holding the previous certificate. Names are compared tolerant of short-name/FQDN form, so a single-server target and a list re-picked from short names to FQDNs do not warn.
  • Another certificate is also deployed to this target — see One certificate per target.
  • The certificate's subject is a wildcard while POP or IMAP is among the services — Exchange will refuse those two.
  • Exchange itself warned during the import or enable. Its warnings are reported verbatim, because that is how Exchange reports a service it declined to bind.

In the plan preview (dry run), in addition:

  • The certificate's key is not RSA (or is under 2048 bits) — the run would refuse it.
  • The organization reports servers this target would not reach — see If a server joins the organization later.
  • Which certificate currently serves a requested service on the connected server (from the IIS :443 binding; or, when no 443 binding carries a certificate, the first HTTPS binding that does), and that multi-server previews inspect only the connected server.
  • Import- or Enable-ExchangeCertificate is not available in this session — the account's RBAC role does not expose it and the run would fail.

One certificate per target​

A service is served by exactly one certificate, organization-wide. Enable-ExchangeCertificate hands the service to whichever certificate ran last, so two CertAutoPilot certificates pointed at the same Exchange target take the bindings away from each other on every deployment and every renewal.

This is unlike IIS, where two certificates occupy different bindings and coexist happily. On Exchange, give each certificate its own target — or, if you really need two, split the services between them (one target with IIS,SMTP, another with POP,IMAP) so they are not competing for the same slot.

CertAutoPilot detects the situation and warns on every run. It also stops trusting its own record while it lasts: normally an unchanged certificate is skipped on the strength of the stored deployment record, but a sibling certificate can take the bindings away without that record ever changing — so with more than one certificate on the target, every run re-imports and re-enables instead of skipping. The import and enable are idempotent, so this costs a little time and changes nothing else.