Windows (WinRM) module

Deploy any file to any Windows path, then run PowerShell post-deploy scripts. The Windows sibling of the SSH module — same PathSet + ActionSet operator UX, different transport. Distinct from the IIS module, which is dedicated to IIS binding lifecycle.

Type: winrm Version: 1.0.0 Non-reversible

01When to pick this module

Pick winrm when you want to:

  • Drop a PEM / PFX / DER / arbitrary file at a known Windows path (C:\inetpub\certs\site.pfx, \\fileserver\share\...).
  • Run PowerShell after — restart a service, import into a Java keystore via keytool, run a custom hook script.
  • Cover Windows targets that aren't IIS: Tomcat, JBoss, custom .NET services, ad-hoc certificate stores.

If your only goal is to update an IIS HTTPS binding, the dedicated IIS module handles binding discovery, app-pool recycling, and old-cert cleanup automatically.

02Server-side prerequisites

Run on each Windows host (PowerShell as Administrator):

# Enable WinRM
Enable-PSRemoting -Force

# HTTPS listener (production: replace the self-signed cert with a real one)
winrm quickconfig -transport:https

# Firewall — open both ports if you intend to use both
New-NetFirewallRule -Name 'WinRM-HTTP'  -DisplayName 'WinRM HTTP'  `
  -Enabled True -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow
New-NetFirewallRule -Name 'WinRM-HTTPS' -DisplayName 'WinRM HTTPS' `
  -Enabled True -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow

# Optional: raise envelope size for large file transfers
Set-Item WSMan:\localhost\MaxEnvelopeSizekb 8192

03Authentication methods

NTLM (default)

Works with local accounts and domain accounts. Provide username as just svc-certdeploy (local) or DOMAIN\svc-certdeploy (domain), or set the Domain field on the target separately.

Basic

Username/password sent over the wire. Use only with TLS enabled — the target form warns when you mix Basic with plain HTTP.

Kerberos (first-class)

Set auth_type=kerberos + Domain field (the Kerberos realm, e.g. CORP.LOCAL). The CertAutoPilot host needs a valid krb5.conf for KDC discovery (typical Linux installer ships /etc/krb5.conf; symlink or set the KRB5_CONFIG environment variable for non-default paths). The library auto-derives the SPN as HTTP/<hostname>.

CredSSP (multi-hop delegation) is not supported in this version. Most domain-joined scenarios are covered by Kerberos.

04PathSet — where files go

Target OS must be Windows

The PathSet (and ActionSet) you bind to a WinRM target must be created with target_kind: "windows". The form's PathSet/ActionSet dropdowns filter to Windows-scoped resources only; the backend rejects mismatched bindings at create/update time. Reusing a Linux PathSet on a WinRM target (or vice versa) is not allowed — POSIX paths and owner/mode have no Windows equivalent.

Identical shape to the SSH module's PathSet. Each entry pairs a content source with an absolute Windows path. Sources:

  • cert — leaf certificate PEM
  • chain — chain (intermediates) PEM
  • fullchain — leaf + chain PEM
  • private_key — private key PEM
  • combined — leaf + chain + key in one PEM file

Format defaults to pem; der is supported for cert and private_key sources.

Path validation rejects: relative paths, .. traversal segments, null bytes, Windows reserved device names (CON, NUL, PRN, AUX, COM1–9, LPT1–9), forbidden filename characters (< > : " | ? *), and components with trailing dots or spaces (Windows strips them silently). Drive-letter and UNC paths are both accepted.

05ActionSet — what runs after

Two modes:

command mode

List of PowerShell commands. Run one-by-one. Optional allowed_commands regex allowlist (defence in depth — operator mistypes a destructive command and the regex blocks it).

script_inline mode

Multi-line script body. Uploaded to a remote temp path ($env:TEMP\certautopilot-XXXX.ps1) via the chunked WriteFile transport, ACL-restricted to the executing user, run with -NoProfile -NonInteractive -ExecutionPolicy Bypass -File, then removed (best-effort cleanup).

Operator picks the shell: powershell.exe (Windows PowerShell 5.1, default — present on every modern Windows Server) or pwsh.exe (PowerShell 7+, must be installed on the target).

06Variable substitution

Project variables expand via the ${{ NAME }} placeholder syntax. Values are PowerShell-escape-aware: a hostile project variable value $(Get-Process) is substituted as the literal string '$(Get-Process)' rather than an executable sub-expression. Same threat-model fix as the SSH module's shell-escape work; consult project variables for syntax details and escape semantics.

07Worked example: cert + Tomcat reload

PathSet:

  • C:\Tomcat\conf\ssl\fullchain.pem → source fullchain
  • C:\Tomcat\conf\ssl\privkey.pem → source private_key

ActionSet (mode script_inline, shell powershell.exe):

$ErrorActionPreference = 'Stop'

# Convert PEM → JKS for Tomcat
keytool -importkeystore `
    -srckeystore "C:\Tomcat\conf\ssl\fullchain.pem" `
    -srcstoretype PEM `
    -destkeystore "C:\Tomcat\conf\ssl\keystore.jks" `
    -deststoretype JKS `
    -alias tomcat `
    -storepass "${{ KEYSTORE_PASS }}"

Restart-Service -Name Tomcat9
Write-Output "tomcat reloaded with new cert"

08Rollback

Not supported by the framework. Same posture as the IIS, NetScaler, F5 BIG-IP, and Webhook modules. The module is generic by design — it cannot know what your ActionSet did (restart a service? reconfigure a binding? import into a Java keystore?), so it cannot reliably undo it. If you need a backout, embed it in your ActionSet: take a .bak with Copy-Item before overwrite, keep a Restore-Item path on hand, or roll service state forward through your existing ops tooling.

09Limits + performance

KnobDefaultNote
File transfer rate~60–300 KB/secWAN-RTT bound; chunked base64 + certutil decode.
Per-file size cap100 MBConfigurable on the target via max_file_size_bytes. Raise only if you genuinely need to push > 100 MB.
Per-script size cap~256 KB UTF-8 sourceUTF-16LE doubles bytes; we cap before WSMan envelope quota.
Per-target concurrency10Module-level; clamped on the upper end.
Per-target timeout5 mintarget_timeout_seconds on the module config.
Per-command timeout60 seccommand_timeout_seconds on the target; ActionSet timeout_seconds overrides per-command.
Output truncation16 KBPer-command stdout+stderr; PowerShell verbosity bounded.

10Error codes

The module emits structured WINRM_* error codes for retry classification:

  • WINRM_CONNECT — dial / DNS / TLS handshake failure (network class)
  • WINRM_AUTH — 401, NTLM/Kerberos rejection, "Access denied" (auth class)
  • WINRM_TIMEOUT — context deadline exceeded (transient — retried)
  • WINRM_EXEC — non-zero PowerShell exit (transient — retried)
  • WINRM_PS_SYNTAX — ParserError detected in stderr (validation — not retried)
  • WINRM_FILE — file transfer failure (transient — retried)
  • WINRM_VALIDATION — operator config error (path, run_as)
  • WINRM_QUOTA — MaxEnvelopeSize exceeded (permanent — operator must raise the quota)

11vs. the IIS module

Aspectwinrmiis
ScopeGeneric file deploy + scriptIIS binding lifecycle
PathSet / ActionSetYesNo (uses fixed IIS scripts)
ConcurrencyParallel (10 default)Sequential
AuthNTLM, Basic, KerberosNTLM, Basic
RollbackNo (operator scripts it in the ActionSet)No
Picks IIS bindings automaticallyNo (operator scripts it)Yes