Microsoft IIS module

Deploys certificates to Windows Server IIS over WinRM: imports the PKCS#12 into the local machine certificate store, binds it to the requested site + port, and removes the HTTPS binding's old certificate reference. Works with Windows Server 2016+.

01Overview

  • Transport: WinRM (HTTP 5985 or HTTPS 5986).
  • Auth: NTLM (default) or Basic. Basic only works over HTTPS WinRM or when AllowUnencrypted is set on the target — usually unsuitable for production.
  • Artifact: PKCS#12 bundle (cert + private key + chain) generated on the fly, imported into Cert:\LocalMachine\<store>.
  • Binding: updates the IIS site's HTTPS binding to reference the new thumbprint; old thumbprint left in the store (not deleted).
  • Rollback: not supported. The old thumbprint remains in the store for manual re-bind if needed.

02Prerequisites

  • WinRM enabled on the target: Enable-PSRemoting -Force.
  • Firewall rule opening 5985 (HTTP WinRM) or 5986 (HTTPS WinRM) between the backend and the IIS host. For HTTPS WinRM make sure the WinRM listener has a valid cert bound:
    winrm quickconfig -transport:https
  • An administrative Windows account with permission to manage certificates + IIS bindings. Adding it to the Administrators group is easiest; a constrained JEA endpoint also works if the commands used by the module are on the allowlist.
  • IIS installed with the Web-Mgmt-Service feature.

03Create the module credential

  1. Settings → Distribution → CredentialsNew.
  2. Module type: IIS (WinRM).
  3. Username (in DOMAIN\\user or user@FQDN form), password.
  4. Save.

04Create an IIS target

  1. Settings → Distribution → TargetsNew. Module: IIS.
  2. Fields:
    • Hostname — reachable from the backend.
    • Port — defaults to 5986 (HTTPS) / 5985 (HTTP) based on the Use TLS toggle.
    • Use TLS — on for 5986. Strongly recommended.
    • TLS skip verify — accept self-signed WinRM listener cert. OK for initial bring-up, disable for prod.
    • Auth type: ntlm (default) or basic.
    • Credential: pick the one from the step above.
    • Certificate store: usually WebHosting or My (Personal).
    • Binding: site name + hostname + port (defaults to site Default Web Site, host empty, port 443). SNI-enabled if hostname is non-empty.
    • Connect timeout / Command timeout: reasonable defaults (30 s / 120 s).
  3. Save → health check runs Invoke-Command -ScriptBlock { hostname } over WinRM.

05Execution flow

  1. Backend builds a PKCS#12 bundle from cert + key + chain, password-wrapped with an ephemeral passphrase.
  2. Uploads the PKCS#12 to a temp file on the target and imports with Import-PfxCertificate into the configured store.
  3. Replaces the HTTPS binding's SSL cert on the IIS site ((Get-WebBinding).AddSslCertificate).
  4. Deletes the temp file.
  5. Emits success. Old cert remains in the store untouched.
No auto-cleanup of old certificates

The module does not delete old certificates from the store — they accumulate over time. This is deliberate: deleting them can break other applications on the same host. Run a scheduled cleanup of your own (e.g. Get-ChildItem Cert:\LocalMachine\WebHosting | Where NotAfter -lt (Get-Date) on a quarterly cadence).

06Rollback

Not supported. The distribution record's RollbackAvailable is false. To revert, open IIS Manager on the target and re-bind the site to the previous thumbprint (which is still in the store).

07Troubleshooting

"Failed to connect to WinRM"

Check winrm quickconfig on the target, confirm the HTTPS listener exists (winrm enumerate winrm/config/listener), and verify the firewall allows 5986. From the backend host: Test-NetConnection <host> -Port 5986.

"Access denied" during Import-PfxCertificate

The service account lacks permission to write to the cert store. Add it to local Administrators or grant store-specific ACL.

Binding update silently does nothing

The site name in the target config doesn't exactly match. IIS site names are case-sensitive and must include any default trailing spaces. Confirm with Get-IISSite on the target.