Run commands on multiple remote servers in parallel — Windows and Linux.
Quick Start · API · Prerequisites · Troubleshooting
One command. Any number of servers. All at the same time.
PodeRemoteRunner is a lightweight PowerShell HTTP server that executes commands on remote Windows servers via WinRM and Linux servers via SSH in parallel. It exposes a clean REST API and an optional web UI — no agents, no dependencies on the target machines.
Execution
- Parallel execution across unlimited servers simultaneously
- WinRM over HTTPS (port 5986) for Windows — uses your current Windows identity, no passwords
- SSH key authentication for Linux/Unix — passwords never accepted or stored
- Auto-retry with up to 2 attempts per server before marking it as failed
Observability
- Unique TraceId per request — returned in the
X-Request-Idresponse header - Per-execution log file (JSONL) — command, output, and timing saved for every run
- Structured logging everywhere — JSON Lines with UTC ISO-8601 timestamps and INFO/WARN/ERROR levels, daily-rotated
Security
- Binds to
localhostonly by default — no authentication layer (read Security before exposing it anywhere) - Host/server names validated against strict patterns; SSH invoked via an argument array, not a shell string
- OWASP security headers on every response (CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
- Rate limiting — 60 requests per minute per IP (Pode built-in)
Requirements: PowerShell 7.1+ on the host machine (the SSH path uses APIs not available in Windows PowerShell 5.1), plus the Pode module.
1. Install Pode
Install-Module -Name Pode -Scope CurrentUser2. Run setup — checks requirements and creates the logs/ folder
.\scripts\setup.ps13. Start the server
# Foreground (recommended for first run)
.\server.ps1
# Background
.\start-background.ps14. Open your browser
http://localhost:8080
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Server status page |
GET |
/health |
Health check |
GET |
/winrm |
WinRM web UI |
POST |
/winrm/run |
Execute PowerShell on Windows servers |
GET |
/ssh |
SSH web UI |
POST |
/ssh/run |
Execute commands on Linux servers |
Every response includes
X-Request-Id: <traceId>. Use this ID to locate the matching execution log file instantly.
POST /winrm/run — request and response
// Request
{
"servers": ["SERVER01", "SERVER02"],
"command": "Get-Service W3SVC | Select-Object Name, Status"
}// Response
{
"success": true,
"executionId": "a1b2c3d4",
"logFile": "logs/winrm/execution_2025-08-28_14-30-15_a1b2c3d4.jsonl",
"results": [
{
"server": "SERVER01",
"success": true,
"output": "Name Status\n---- ------\nW3SVC Running",
"error": "",
"executionTime": 2.1
}
]
}POST /ssh/run — request and response
// Request
{
"hosts": ["ubuntu-01.example.com", "192.168.1.20"],
"username": "admin",
"command": "df -h"
}// Response
{
"success": true,
"executionId": "a1b2c3d4",
"results": [
{
"host": "ubuntu-01.example.com",
"success": true,
"output": "Filesystem Size Used Avail Use% Mounted on\n/dev/sda1 50G 12G 36G 25% /",
"error": "",
"executionTime": 1.23
}
]
}Enable WinRM HTTPS on each target server:
winrm quickconfig -transport:httpsAdd the account that runs PodeRemoteRunner to the Remote Management Users group on each target. Then verify connectivity:
Test-WSMan -ComputerName "YOUR-SERVER" -UseSSLOpenSSH client must be available on the machine running PodeRemoteRunner:
Get-Command ssh.exe
# If missing:
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0Copy your public key to each target host:
ssh-copy-id admin@your-linux-hostThe key path defaults to
~/.ssh/id_rsa. Set$SSH_KEY_PATHat the top ofroutes/ssh.ps1to use a different key.
PodeRemoteRunner/
│
├── server.ps1 # HTTP server entry point
├── start-background.ps1 # Run server as a background job
│
├── lib/
│ └── RrLogging.psm1 # Centralized structured logging (UTC ISO-8601, JSONL)
│
├── routes/
│ ├── health.ps1 # GET /health
│ ├── winrm.ps1 # GET /winrm POST /winrm/run
│ └── ssh.ps1 # GET /ssh POST /ssh/run
│
├── scripts/
│ └── setup.ps1 # Requirements check and first-run setup
│
└── logs/
├── server-YYYY-MM-DD.jsonl # Server lifecycle
├── requests-YYYY-MM-DD.jsonl # One structured line per HTTP request
├── winrm/ # One JSONL file per WinRM execution
└── ssh/ # One JSONL file per SSH execution
All log files are JSON Lines (one JSON object per line) with UTC ISO-8601 timestamps and INFO/WARN/ERROR levels — parse them with Get-Content file.jsonl | ConvertFrom-Json.
Find any request by TraceId across all logs:
Get-ChildItem "logs\" -Recurse -Filter "*.jsonl" | Select-String "a1b2c3d4"WinRM
| Problem | Likely cause | Solution |
|---|---|---|
| Connection refused | WinRM HTTPS not enabled | winrm quickconfig -transport:https on target |
| Access denied | Missing permissions | Add user to Remote Management Users on target |
| SSL / certificate error | Listener misconfigured | winrm enumerate winrm/config/listener |
| Timeout | Firewall blocking port 5986 | Open port 5986 on target firewall |
SSH
| Problem | Likely cause | Solution |
|---|---|---|
ssh.exe not found |
OpenSSH not installed | Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0 |
| Permission denied | Key not authorized on target | ssh-copy-id user@host from the server machine |
| Connection timed out | Port 22 blocked or wrong host | Verify port 22 and host reachability |
General
| Problem | Solution |
|---|---|
| Port 8080 already in use | Stop the existing process or change the port in server.ps1 |
| Pode module not found | Install-Module -Name Pode -Scope CurrentUser |
PodeRemoteRunner has no authentication. Anyone who can reach the listening port can run arbitrary commands on every reachable target, using the server's Windows identity (WinRM) and SSH key (SSH). It binds to
localhostonly by design — do not change the bind address or expose it to a network without first putting authentication and HTTPS in front of it. Access to the API is equivalent to administrative access to every target.
- Arbitrary execution is the feature — the
commandfield runs as-is on the targets. Input validation covers host/server names, not the command itself - No credentials stored — WinRM inherits the current Windows session identity; SSH uses private key files only
- SSH hardening — invoked through an argument array (not a shell string), key-based auth only,
BatchMode=yes - Rate limiting — 60 requests per minute per IP, enforced by Pode
- OWASP headers — every response includes CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy
- Logs may contain sensitive data — command text and full command output are written to disk in clear text under
logs/. Restrict access to that directory and define a retention policy
WinRM connections use -SkipCACheck -SkipCNCheck, which disables certificate validation — convenient with self-signed certificates, but vulnerable to man-in-the-middle within the network. Use a trusted certificate where that matters.
MIT — see LICENSE Built on Pode (MIT) · Requires PowerShell 7.1+ · Host runs on Windows
