winrm-client is a production-ready NodeJS client to access WinRM (Windows Remote Management) SOAP web service. It allows to execute commands on target windows machines. Please visit Microsoft's WinRM site for WINRM details.
Replace shell and command with Shell and Command.
// CommonJS
const { Shell, Command } = require('winrm-client');
// ES6
import { Shell, Command } from 'winrm-client';# Using npm
npm install winrm-client
# Using pnpm
pnpm add winrm-client
# Using yarn
yarn add winrm-client- 🔁 Supports both CommonJS and ES6 modules.
- 🏗️ Has types for all exported functions and interfaces.
- 🔐 Supports both Basic and NTLM authentication (auto-detected based on username format)
- 🔁 Supports interactive commands that can automatically respond to prompts using three types of detection methods: (see Interactive Commands)
- Regex Patterns (traditional method)
- Custom Sync Detectors (new)
- Custom Async Detectors (new)
- 🔍 Supports debug logging using the
DEBUGenvironment variable (see Debug Logging) - 🧪 Supports testing (see Testing)
Supports NodeJS Version >= 16.0.0
Tested on NodeJS versions 16, 18, 20, and latest LTS.
As of now Winrm Version 3 is tested.
> winrm id
IdentifyResponse
ProtocolVersion = http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor = Microsoft Corporation
ProductVersion = OS: 10.0.xxxx SP: 0.0 Stack: 3.0
On the remote host, a PowerShell prompt, using the Run as Administrator option and paste in the following lines:
> winrm quickconfig
y
> winrm set winrm/config/service/Auth '@{Basic="true"}'
> winrm set winrm/config/service '@{AllowUnencrypted="true"}'
> winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}'
On the client side where NodeJS is installed
npm install winrm-client
winrm-client supports two authentication methods that are automatically detected based on the username format:
Used for local usernames (no domain prefix). Requires enabling Basic auth on the WinRM service.
// Local username - uses Basic authentication
await runCommand('hostname', 'server', 'Administrator', 'password', 5985);Used for domain accounts. Automatically enabled when using domain-prefixed usernames.
// Domain prefix format (DOMAIN\user) - uses NTLM authentication
await runCommand(
'hostname',
'server',
'DOMAIN\\Administrator',
'password',
5985
);
// UPN format (user@domain.com) - uses NTLM authentication
await runCommand('hostname', 'server', 'admin@company.com', 'password', 5985);| Username Format | Auth Method | Example |
|---|---|---|
| Local | Basic | Administrator |
| Domain prefix | NTLM | DOMAIN\user |
| UPN | NTLM | user@domain.com |
For NTLM authentication, ensure the remote WinRM service allows Negotiate authentication:
# Enable Negotiate authentication (includes NTLM)
> winrm set winrm/config/service/Auth '@{Negotiate="true"}'For development with TypeScript:
# Install dependencies
npm install
# Build TypeScript to JavaScript
npm run build
# Watch mode for development
npm run build:watch
# Lint TypeScript files
npm run lint
# Format code with Prettier
npm run format
# Check formatting
npm run format:checkTo enable debug logging, set the DEBUG environment variable to winrm or winrm:*.
Possible values for namespace are:
*// Enable debug logging for all namespaceshttpshellcommandinteractiverunCommandrunPowershell
To enable debug logging for all namespaces, set the DEBUG environment variable to winrm:*.
DEBUG=winrm:* node index.jsconst winrm = require('winrm-client');
winrm.runCommand(
'mkdir D:\\winrmtest001',
'10.xxx.xxx.xxx',
'username',
'password',
5985
);
winrm.runCommand(
'ipconfig /all',
'10.xxx.xxx.xxx',
'username',
'password',
5985
);import { runCommand, runPowershell } from 'winrm-client';
async function executeCommand(): Promise<void> {
try {
const result = await runCommand(
'mkdir D:\\winrmtest001',
'10.xxx.xxx.xxx',
'username',
'password',
5985
);
console.log('Command result:', result);
const ipResult = await runCommand(
'ipconfig /all',
'10.xxx.xxx.xxx',
'username',
'password',
5985
);
console.log('IP Config:', ipResult);
} catch (error) {
console.error('Error executing command:', error);
}
}
executeCommand();For domain-joined machines or Azure AD accounts:
import { runCommand, runPowershell } from 'winrm-client';
async function executeDomainCommand(): Promise<void> {
try {
// Using DOMAIN\user format
const result = await runCommand(
'whoami',
'server.company.com',
'CORPORATE\\admin',
'password',
5985
);
console.log('Current user:', result);
// Using UPN format (user@domain.com)
const psResult = await runPowershell(
'Get-Process | Select-Object -First 5',
'server.company.com',
'admin@corporate.local',
'password',
5985
);
console.log('Processes:', psResult);
} catch (error) {
console.error('Error:', error);
}
}
executeDomainCommand();WinRM Client supports interactive commands that can automatically respond to prompts using three types of detection methods:
- Regex Patterns (traditional method)
- Custom Sync Detectors (new)
- Custom Async Detectors (new)
const winrm = require('winrm-client');
const prompts = [
{
pattern: /Enter your name:/i,
response: 'John Doe',
},
{
pattern: /Password:/i,
response: 'secret123',
isSecure: true, // Won't log the response
},
];
const result = await winrm.runInteractivePowershell(
'my-interactive-script.ps1',
'host',
'username',
'password',
5985,
prompts,
30000 // timeout in milliseconds
);Use custom synchronous functions for complex prompt detection logic:
const prompts = [
{
detector: (output) => {
// Custom logic for detecting prompts
const lines = output.split('\n');
return lines.some(
(line) =>
line.toLowerCase().includes('password') ||
line.toLowerCase().includes('passphrase')
);
},
response: 'myPassword123',
isSecure: true,
},
{
detector: (output) => {
// Multi-condition detection
return output.includes('Continue?') && output.includes('(y/n)');
},
response: 'y',
},
];Use async functions for detection that requires external API calls, LLM responses, database lookups, or other async operations:
const prompts = [
{
asyncDetector: async (output) => {
// Example: External API validation
const errorCodeMatch = output.match(/Error Code: (\d+)/);
if (errorCodeMatch) {
try {
// Make external API call
const response = await fetch(
`https://api.example.com/errors/${errorCodeMatch[1]}`
);
const data = await response.json();
return data.requiresConfirmation ? 'yes' : '';
} catch {
return 'no'; // Fallback to regex pattern if available
}
}
return 'no';
},
pattern: /Do you want to retry/i, // Fallback pattern
},
];The detection methods are prioritized as follows:
- Async Detector (highest priority)
- Sync Detector
- Regex Pattern (fallback)
If a custom detector fails with an error, the system will automatically fall back to the regex pattern if available:
Custom detectors are wrapped in try-catch blocks to prevent failures from breaking the interactive flow:
- If a custom detector throws an error, it falls back to the regex pattern
- If both custom detector and regex pattern fail, the prompt is skipped
- Errors are logged for debugging purposes
npm test
For e2e testing, use the included setup script on a Windows Server (AWS EC2, etc.):
# Via SSH
ssh Administrator@<IP> "powershell -ExecutionPolicy Bypass" < scripts/setup-winrm.ps1This configures WinRM + AD DS with a test user. See WINRM_SETUP.md for details.
- Sebastien Hideux (https://github.com/shide1989)