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
AllowUnencryptedis 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-Servicefeature.
03Create the module credential
- Settings → Distribution → Credentials → New.
- Module type: IIS (WinRM).
- Username (in
DOMAIN\\useroruser@FQDNform), password. - Save.
04Create an IIS target
- Settings → Distribution → Targets → New. Module: IIS.
-
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) orbasic. - Credential: pick the one from the step above.
- Certificate store: usually
WebHostingorMy(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).
- Save → health check runs
Invoke-Command -ScriptBlock { hostname }over WinRM.
05Execution flow
- Backend builds a PKCS#12 bundle from cert + key + chain, password-wrapped with an ephemeral passphrase.
- Uploads the PKCS#12 to a temp file on the target and imports with
Import-PfxCertificateinto the configured store. - Replaces the HTTPS binding's SSL cert on the IIS site (
(Get-WebBinding).AddSslCertificate). - Deletes the temp file.
- Emits success. Old cert remains in the store untouched.
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.