Class WinRMClient

java.lang.Object
org.metricshub.winrm.WinRMClient
All Implemented Interfaces:
AutoCloseable

public final class WinRMClient extends Object implements AutoCloseable
The fluent entry point of the library: a reusable WinRM connection to one host, created with a builder and closed with try-with-resources. One client authenticates once and can run any number of WQL queries and commands over the same connection.

 try (
 	WinRMClient client = WinRMClient.builder("server01.acme.com")
 		.credentials("ACME\\admin", password)
 		.timeout(Duration.ofSeconds(30))
 		.build()) {

 	WqlResult services = client.wql("SELECT Name, State FROM Win32_Service").execute();
 	for (WqlRow row : services) {
 		System.out.println(row.string("Name") + " is " + row.string("State"));
 	}

 	CommandResult result = client.command("ipconfig /all").execute();
 	System.out.println(result.stdout());
 }
 

Besides the blocking execute() terminals, both operations can stream: WqlRequest.stream() yields WQL rows lazily page by page, and CommandRequest.start() returns a RemoteProcess whose output is consumed while the command is still running.

Thread-safety: a client may be shared between threads, but a WinRM connection is a serial channel — concurrent operations are executed one at a time, and an open stream or process holds the connection until it is closed.

Failures are reported through the unchecked WinRMClientException hierarchy; the legacy static helpers (WinRMWqlExecutor, WinRMCommandExecutor) and their checked exceptions are unaffected.

  • Method Details

    • builder

      public static WinRMClient.Builder builder(String hostname)
      Start building a client for the given host.
      Parameters:
      hostname - the host to connect to (mandatory; for Kerberos, use the FQDN the KDC knows)
      Returns:
      a new WinRMClient.Builder
    • wql

      public WqlRequest wql(String query)
      Prepare a WQL query. Nothing is sent until WqlRequest.execute() is called.
      Parameters:
      query - the WQL query, e.g. SELECT Name, State FROM Win32_Service
      Returns:
      the request, to configure and execute
    • command

      public CommandRequest command(String commandLine)
      Prepare a command execution. Nothing is sent until CommandRequest.execute() is called.
      Parameters:
      commandLine - the command line to execute (run through cmd.exe by the remote shell)
      Returns:
      the request, to configure and execute
    • powerShell

      public CommandRequest powerShell(String script)
      Prepare a PowerShell script execution. Nothing is sent until CommandRequest.execute() is called.

      The script travels base64-encoded (powershell.exe -NoProfile -NonInteractive -EncodedCommand ...), so it needs no quoting or escaping whatsoever: quotes, pipes, newlines, and $variables reach PowerShell exactly as written.

      
       CommandResult result = client.powerShell(
       	"Get-Service | Where-Object { $_.Status -eq 'Running' } | Select-Object -First 5 Name"
       ).execute();
       
      The returned request is the same as for command(String): every option and both terminals apply unchanged — including CommandRequest.upload(Path...), whose path rewriting happens on the script text before it is encoded, so a script referencing an uploaded file runs against the remote copy. powershell.exe exits with 0 on success and 1 when the script ends with a terminating error; call exit <n> in the script for a specific exit code.

      There is no practical script size limit. A script whose encoded invocation would not fit the remote shell's command line (roughly 3000 characters of script) is automatically transferred as a temporary .ps1 file — through the WinRM connection itself, exactly like CommandRequest.upload(Path...) — and its content run as a dot-sourced script block, which keeps the script behaving like the encoded form: pathless ($PSScriptRoot and $MyInvocation.MyCommand.Path stay empty either way), top-level scope and param(...) intact, and out of reach of the host's execution policy (which only governs script files). Only $MyInvocation's own metadata (InvocationName, Line) reflects the wrapper invocation for a transferred script. The remote copy is content-addressed, so re-running an identical script skips the transfer. Like any request with uploads, the transfer commands are then what creates the remote shell, so the shell-scoped CommandRequest.workingDirectory(String) does not apply.

      Parameters:
      script - the PowerShell script to execute, verbatim
      Returns:
      the request, to configure and execute
      Throws:
      IllegalArgumentException - when the script is blank
    • uploadFile

      public void uploadFile(Path localFile, String remoteFile)
      Copy a local file to an explicit path on the remote host, through the WinRM connection itself (no SMB, no extra port). The transfer is digest-verified and skipped when the destination already has identical content; the destination directory is created when needed. The client's timeout applies.
      Parameters:
      localFile - the local file to copy
      remoteFile - the absolute destination path on the remote host, e.g. C:\Windows\Temp\collect.ps1
      Throws:
      WinRMTimeoutException - when the timeout elapses first
      WinRMClientException - for any other failure
    • hostname

      public String hostname()
      Get the hostname this client connects to.
      Returns:
      the hostname
    • close

      public void close()
      Close the client and release its connection. Idempotent; operations attempted after closing throw IllegalStateException.
      Specified by:
      close in interface AutoCloseable