Skip to content

Make WinRM service calls from NodeJS, interact and detect prompts using LLMs or matching patterns

License

Notifications You must be signed in to change notification settings

shide1989/winrm-client

 
 

Repository files navigation

winrm-client

npm version Build Status Build Status

⚠️ This is an updated fork of the original nodejs-winrm project that doesn't seem to be maintained anymore.

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.

⬆️ Migration from nodejs-winrm

Replace shell and command with Shell and Command.

// CommonJS
const { Shell, Command } = require('winrm-client');
// ES6
import { Shell, Command } from 'winrm-client';

Installation

# Using npm
npm install winrm-client

# Using pnpm
pnpm add winrm-client

# Using yarn
yarn add winrm-client

Features

  • 🔁 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 DEBUG environment variable (see Debug Logging)
  • 🧪 Supports testing (see Testing)

Supported NodeJS Versions

Supports NodeJS Version >= 16.0.0

Tested on NodeJS versions 16, 18, 20, and latest LTS.

Supported WinRM Versions

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

Remote Installation

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

Authentication

winrm-client supports two authentication methods that are automatically detected based on the username format:

Basic Authentication

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);

NTLM Authentication

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 Support

Username Format Auth Method Example
Local Basic Administrator
Domain prefix NTLM DOMAIN\user
UPN NTLM user@domain.com

Remote Host Configuration for NTLM

For NTLM authentication, ensure the remote WinRM service allows Negotiate authentication:

# Enable Negotiate authentication (includes NTLM)
> winrm set winrm/config/service/Auth '@{Negotiate="true"}'

Development Workflow

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:check

Debug Logging

To enable debug logging, set the DEBUG environment variable to winrm or winrm:*.

Possible values for namespace are:

  • * // Enable debug logging for all namespaces
  • http
  • shell
  • command
  • interactive
  • runCommand
  • runPowershell

To enable debug logging for all namespaces, set the DEBUG environment variable to winrm:*.

DEBUG=winrm:* node index.js

Examples

Run a Single Command

JavaScript

const 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
);

TypeScript

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();

Run Command with NTLM Authentication

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();

Interactive Commands

WinRM Client supports interactive commands that can automatically respond to prompts using three types of detection methods:

  1. Regex Patterns (traditional method)
  2. Custom Sync Detectors (new)
  3. Custom Async Detectors (new)

Basic Interactive Command

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
);

Custom Sync Detectors

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',
  },
];

Custom Async Detectors

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
  },
];

Detection Priority and Fallback

The detection methods are prioritized as follows:

  1. Async Detector (highest priority)
  2. Sync Detector
  3. Regex Pattern (fallback)

If a custom detector fails with an error, the system will automatically fall back to the regex pattern if available:

Error Handling

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

Testing

npm test

Test Server Setup (Windows)

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.ps1

This configures WinRM + AD DS with a test user. See WINRM_SETUP.md for details.

Maintainers

About

Make WinRM service calls from NodeJS, interact and detect prompts using LLMs or matching patterns

Topics

Resources

License

Stars

Watchers

Forks

Languages

  • TypeScript 84.7%
  • PowerShell 13.9%
  • JavaScript 1.4%