awinrm
Asynchronous Python library for Windows Remote Management (WinRM)
awinrm is a fully asynchronous Python client for Windows Remote Management (WinRM). It allows you to execute commands, run PowerShell scripts, transfer files, and maintain interactive shell sessions on remote Windows machines.
🙏 Acknowledgements
This project is based on the excellent pywinrm library by Alexey Diyan and contributors. While pywinrm uses synchronous requests and external authentication libraries, awinrm was rewritten to be fully async with httpx and uses asyauth for authentication.
✨ Features
- Fully Asynchronous - Built with
asyncioandhttpxfor non-blocking I/O - Pure Python Authentication - NTLM, Kerberos, SPNEGO, and CredSSP are all implemented in pure Python via
asyauthandminikerberos(no system dependencies likekrb5orsspirequired!) - Interactive Shells - Create persistent CMD or PowerShell sessions with keepalive support
- File Transfer - Upload and download files via base64-encoded PowerShell commands
- Message Encryption - Automatic GSSAPI message encryption for secure communication over HTTP
- Graceful Termination - Clean handling of shell exits and session timeouts
📦 Installation
pip install awinrm
Dependencies
All dependencies are pure Python:
httpx- Async HTTP clientasyauth- Pure Python implementation of NTLM, Kerberos, SPNEGO, CredSSPunicrypto- Cryptographic primitives
🔗 URL Format
awinrm uses URL-based credential specification (from asyauth). The format is:
<transport>+<auth_type>-<auth_method>://<username>:<password>@<host>:<port>/<path>?<options>
Components
| Component | Description | Examples |
|---|---|---|
transport |
HTTP or HTTPS | http, https |
auth_type |
Authentication protocol | ntlm, kerberos, spnego |
auth_method |
Credential type | password, nt, aes, ccache |
username |
User (with optional domain) | user, DOMAIN\user, user@domain.com |
password |
Password or hash | plaintext password or NT hash |
host |
Target hostname or IP | 192.168.1.100, server.domain.com |
port |
WinRM port (optional) | 5985 (HTTP), 5986 (HTTPS) |
options |
Query parameters | dc=, proxytype=, etc. |
URL Examples
# NTLM with password (local account)
"http+ntlm-password://administrator:Password123@192.168.1.100"
# NTLM with domain account
"http+ntlm-password://MYDOMAIN\\admin:Password123@server.mydomain.com"
# Kerberos with password
"http+kerberos-password://NORTH\\vagrant:vagrant@winterfell.north.sevenkingdoms.local/?dc=192.168.56.11"
# Kerberos with ccache (ticket cache)
"http+kerberos-ccache://winterfell.north.sevenkingdoms.local/?ccache=/tmp/krb5cc_1000"
# NTLM with NT hash (pass-the-hash)
"http+ntlm-nt://administrator:aad3b435b51404eeaad3b435b51404ee@192.168.1.100"
# HTTPS with NTLM
"https+ntlm-password://admin:secret@secure-server.domain.com:5986"
Query Parameters
| Parameter | Description |
|---|---|
dc=<ip> |
Domain controller IP (for Kerberos) |
ccache=<path> |
Path to Kerberos credential cache |
proxytype=<type> |
Proxy type (socks5, http) |
proxyhost=<host> |
Proxy hostname |
proxyport=<port> |
Proxy port |
📖 API Usage
Basic Command Execution
import asyncio
from awinrm import Session
async def main():
url = "http+ntlm-password://administrator:Password123@192.168.1.100"
async with Session(url) as session:
# Run a command
stdout, stderr, return_code = await session.run_cmd('ipconfig', ('/all',))
print(stdout.decode())
# Run PowerShell
stdout, stderr, return_code = await session.run_ps('Get-Process | Select-Object -First 5')
print(stdout.decode())
asyncio.run(main())
Interactive Shell
import asyncio
from awinrm import Session, ShellTerminatedError
async def main():
url = "http+ntlm-password://administrator:Password123@192.168.1.100"
async with Session(url) as session:
# Create a CMD shell
async with session.create_shell(shell_type='cmd') as shell:
# Send commands
await shell.send_input(b'whoami\r\n')
# Read output
stdout = await shell.stdout.get()
print(stdout.decode())
# Check if shell is still running
if not shell.is_terminated:
await shell.send_input(b'exit\r\n')
asyncio.run(main())
PowerShell Shell
async with Session(url) as session:
# Create a PowerShell shell (recommended for most use cases)
async with session.create_shell(shell_type='powershell') as shell:
await shell.send_input(b'$PSVersionTable\r\n')
stdout = await shell.stdout.get()
print(stdout.decode())
File Transfer
async with Session(url) as session:
# Upload a file
await session.upload_file(
local_path='/tmp/script.ps1',
remote_path='C:\\temp\\script.ps1',
progress_callback=lambda sent, total: print(f'{sent}/{total} bytes')
)
# Download a file
await session.download_file(
remote_path='C:\\Windows\\System32\\drivers\\etc\\hosts',
local_path='/tmp/hosts'
)
# Check if file exists
exists = await session.file_exists('C:\\temp\\script.ps1')
# Get file size
size = await session.get_file_size('C:\\temp\\script.ps1')
Authentication Types
# SPNEGO (auto-negotiates NTLM or Kerberos)
async with Session(url, authtype='spnego') as session:
...
# CredSSP (allows credential delegation / double-hop)
async with Session(url, authtype='credssp') as session:
...
Shell Configuration
async with Session(url) as session:
shell = session.create_shell(
shell_type='powershell', # 'cmd', 'powershell', or 'pwsh'
working_directory='C:\\temp',
env_vars={'MY_VAR': 'value'},
codepage=65001, # UTF-8
keepalive_interval=60, # Seconds between keepalive pings
idle_timeout=300, # Server-side idle timeout
)
async with shell:
...
Custom HTTP Transport
For advanced use cases (proxies, custom SSL, etc.):
import httpx
transport = httpx.AsyncHTTPTransport(
verify=False,
http2=True,
)
async with Session(url, transport=transport, verify=False) as session:
...
Exception Handling
from awinrm import (
Session,
ShellTerminatedError,
ShellNotFoundError,
WinRMError,
WinRMTransportError,
AuthenticationError,
)
async with Session(url) as session:
async with session.create_shell() as shell:
try:
await shell.send_input(b'exit\r\n')
await shell.read_output()
except ShellTerminatedError as e:
print(f"Shell exited with code: {e.exit_code}")
except ShellNotFoundError:
print("Shell was closed by the server")
except AuthenticationError:
print("Authentication failed")
except WinRMTransportError as e:
print(f"HTTP error: {e.code}")
🛠️ Command-Line Tools
awinrm-runcmd
Execute a single command on a remote host:
# Basic usage
awinrm-runcmd 'http+ntlm-password://admin:pass@192.168.1.100' 'ipconfig /all'
# With verbose output
awinrm-runcmd -v 'http+ntlm-password://admin:pass@192.168.1.100' 'whoami'
# Using CredSSP
awinrm-runcmd -a credssp 'http+ntlm-password://admin:pass@192.168.1.100' 'hostname'
awinrm-cmdshell
Interactive remote shell:
# CMD shell
awinrm-cmdshell -s cmd 'http+ntlm-password://admin:pass@192.168.1.100'
# PowerShell shell (default)
awinrm-cmdshell 'http+ntlm-password://admin:pass@192.168.1.100'
# PowerShell Core
awinrm-cmdshell -s pwsh 'http+ntlm-password://admin:pass@192.168.1.100'
# With verbose logging
awinrm-cmdshell -v 'http+kerberos-password://DOMAIN\\user:pass@server/?dc=dc.domain.com'
awinrm-authcheck
Test authentication without executing commands:
# Test NTLM
awinrm-authcheck 'http+ntlm-password://admin:pass@192.168.1.100'
# Test Kerberos
awinrm-authcheck 'http+kerberos-password://DOMAIN\\user:pass@server/?dc=dc.domain.com'
# Verbose mode
awinrm-authcheck -v 'http+ntlm-password://admin:pass@192.168.1.100'
⚙️ WinRM Server Configuration
Enable WinRM (on the Windows target)
# Quick setup (HTTP + NTLM)
winrm quickconfig -q
# Enable CredSSP (for credential delegation)
Enable-WSManCredSSP -Role Server -Force
# Check current config
winrm get winrm/config
Firewall
WinRM uses:
- Port 5985 for HTTP
- Port 5986 for HTTPS
# Allow WinRM through firewall
New-NetFirewallRule -Name "WinRM-HTTP" -DisplayName "WinRM (HTTP)" -Enabled True -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow
🔐 Security Notes
- Use HTTPS in production - HTTP transmits credentials in a recoverable format
- Prefer Kerberos - More secure than NTLM, supports delegation
- Message encryption is automatic - Even over HTTP, NTLM/Kerberos encrypt message payloads
- CredSSP allows delegation - Use when you need to access network resources from the remote host
📚 API Reference
Session
| Method | Description |
|---|---|
run_cmd(command, args) |
Execute a command, return (stdout, stderr, rc) |
run_ps(script) |
Execute PowerShell script |
create_shell(shell_type, ...) |
Create interactive shell |
create_powershell(...) |
Convenience method for PowerShell shell |
upload_file(local, remote, callback) |
Upload file to remote |
download_file(remote, local, callback) |
Download file from remote |
file_exists(path) |
Check if remote file exists |
get_file_size(path) |
Get remote file size |
WinRMShell
| Property/Method | Description |
|---|---|
is_terminated |
True if shell has exited |
return_code |
Exit code (if terminated) |
send_input(data) |
Send bytes to shell stdin |
read_output() |
Read available stdout/stderr |
wait_for_termination() |
Block until shell exits |
close() |
Close the shell |
Exceptions
| Exception | Description |
|---|---|
WinRMError |
Base exception for all WinRM errors |
WinRMTransportError |
HTTP-level errors (4xx, 5xx) |
ShellTerminatedError |
Shell has exited (includes exit_code) |
ShellNotFoundError |
Shell no longer exists on server |
AuthenticationError |
Authentication failed (401) |
WinRMOperationTimeoutError |
Operation timed out (retriable) |
📄 License
MIT License - see LICENSE for details.
Metadata
Release files for awinrm 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| awinrm-0.1.2.tar.gz | 31.7 kB | Without provenance | Details for awinrm-0.1.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| awinrm-0.1.2-py3-none-any.whl | Python 3 | none | any | Details for awinrm-0.1.2-py3-none-any.whl |
Total release size: 62.7 kB
Release files / awinrm-0.1.2.tar.gz
| Download URL | awinrm-0.1.2.tar.gz |
|---|---|
| Size | 31.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3bc27616ae9492fa781ea33a3c10ef7d768d059c2ce85e0f8177441e151c691d
|
|
BLAKE2b-256 checksum How to use checksums |
7826689c0ee65f7842977ed8f0e2f3e145904f1babd78810d22e9a9973b3a517
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / awinrm-0.1.2-py3-none-any.whl
| Download URL | awinrm-0.1.2-py3-none-any.whl |
|---|---|
| Size | 31.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e20492a15cbc454bb53847550ecf18172b1ad9a46f5c6081e941a131e5fddd67
|
|
BLAKE2b-256 checksum How to use checksums |
83838fbf1304ff4ee4e0f0230f203a3b03218464a406c573e847e3a69adde544
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Security for awinrm 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.