psrp-rs
Async PowerShell Remoting Protocol (MS-PSRP) client for Rust.
use ;
use ;
async
Features
- Typed PowerShell objects -- Output stream returns
PsValue/PsObjectwith properties, not raw strings - All 7 PSRP streams -- Output, Error, Warning, Verbose, Debug, Information, Progress, each isolated
- Pipeline builder -- compose multi-command pipelines with named parameters, positional arguments, and switches
- Persistent runspace pool -- keeps a
powershell.exeprocess alive across many pipelines - Cancellation --
CancellationToken-based abort for long-running scripts - Session-key cryptography -- RSA key exchange + AES-256-CBC for
SecureStringencrypt/decrypt - Host call dispatch -- pluggable
PsHosttrait for interactive prompts (Read-Host,Write-Host, etc.) - Command metadata --
Get-Commandintrospection viaget_command_metadata - Shared pool --
SharedRunspacePoolfor multi-task access behindArc<Mutex<_>> - Blocking wrapper -- synchronous API for CLI tools and scripts
- SSH transport -- feature-gated (
--features ssh) alternative to WinRM viarussh - Pure Rust -- no C dependencies,
#![forbid(unsafe_code)]
Installation
# For SSH transport:
# For serde support on PsValue/PsObject:
Usage
Run a script and collect output
use ;
use ;
let client = new?;
let = build_creation_fragments?;
let transport = open.await?;
let mut pool = open_from_transport.await?;
let objects = pool
.run_script
.await?;
for obj in objects
pool.close.await?;
Pipeline builder with parameters
use ;
let result = empty
.add_command
.add_command
.run_all_streams
.await?;
for obj in &result.output
for err in result.typed_errors
for warn in result.typed_warnings
Capture all streams
use Pipeline;
let result = new
.run_all_streams
.await?;
println!;
println!;
println!;
println!;
println!;
println!;
println!;
Cancel a long-running script
use CancellationToken;
use PsrpError;
let cancel = new;
let token = cancel.clone;
spawn;
match pool.run_script_with_cancel.await
Blocking API
use blocking;
use ;
let client = new?;
let objects = run_script?;
println!;
SSH transport
// Requires: cargo add psrp-rs --features ssh
use ;
let transport = connect.await?;
let mut pool = open_with_transport.await?;
let result = pool.run_script.await?;
pool.close.await?;
Shared pool for concurrent tasks
use SharedRunspacePool;
let shared = new;
let s1 = shared.clone;
let t1 = spawn;
let s2 = shared.clone;
let t2 = spawn;
let = join!;
shared.close.await?;
Configuration
psrp-rs reuses WinrmConfig
from winrm-rs for transport-level settings (auth method, TLS, timeouts, proxy).
See the winrm-rs documentation for the full list of config fields.
Pool-level parameters:
| Parameter | Default | Description |
|---|---|---|
min_runspaces |
1 |
Minimum number of runspaces the server should maintain |
max_runspaces |
1 |
Maximum concurrent runspaces (controls server-side parallelism) |
Set via RunspacePool::open_with_options(transport, min, max) or
build_creation_fragments(min, max) + open_from_transport(...).
Roadmap
| Version | Milestone | Status |
|---|---|---|
| v1.0 | Full PSRP: CLIXML, fragments, runspace pool, pipeline builder, all 7 streams, typed records, host calls, session-key crypto, SSH transport, blocking API, shared pool, cancellation | Current |
| v1.x | Pipeline input streaming, disconnect/reconnect pool, CLIXML <Ref> round-tripping |
Planned |
Comparison
| psrp-rs | pypsrp (Python) | PowerShell SDK (.NET) | |
|---|---|---|---|
| Language | Rust | Python | C# |
| Async | native async/await | no | Task-based |
| Typed output | PsValue / PsObject |
dict-based | PSObject |
| All 7 streams | yes | yes | yes |
| Pipeline builder | yes | yes | yes |
| Host callbacks | pluggable PsHost trait |
no | PSHost |
| Session-key crypto | RSA + AES (pure Rust) | yes | built-in |
| SSH transport | russh (feature-gated) |
yes | built-in |
| Auth methods | NTLMv2, Basic, Kerberos, Certificate (via winrm-rs) | NTLM, Basic, Kerberos, CredSSP | all |
| TLS backend | rustls (pure Rust) | OpenSSL | SChannel / OpenSSL |
| Binary size | single static binary | interpreter | runtime |
| C dependencies | none | OpenSSL | CLR |
Contributing
Contributions are welcome. Please open an issue to discuss larger changes before submitting a PR.
Integration tests
Unit tests and end-to-end tests run against a mock transport and need no
external setup. The file tests/integration_real.rs
targets a real Windows host and is ignored by default. To run it, set the
following environment variables and use --ignored:
| Variable | Required | Default | Description |
|---|---|---|---|
PSRP_INTEGRATION_HOST |
yes | -- | Hostname or IP of the target Windows box |
PSRP_INTEGRATION_USER |
no | vagrant |
Username |
PSRP_INTEGRATION_PASS |
no | vagrant |
Password |
A Vagrantfile is provided to spin up a disposable Windows
Server 2025 Hyper-V VM with WinRM + PSRP pre-configured:
PSRP_INTEGRATION_HOST=<ip> \
PSRP_INTEGRATION_USER=vagrant \
PSRP_INTEGRATION_PASS=vagrant \
Cargo features
| Feature | Default | Description |
|---|---|---|
| (default) | -- | WinRM transport with NTLMv2/Basic/Kerberos/Certificate auth (via winrm-rs) |
ssh |
no | SSH transport via russh |
serde |
no | Serialize/Deserialize derives on PsValue / PsObject |
License
Licensed under either of Apache License, Version 2.0 or MIT License at your option.