Skip to content

Configurable user-profile loading: loadUserProfile() and --profile (#139) - #184

Merged
bertysentry merged 1 commit into
mainfrom
139-configurable-user-profile-loading-winrs_noprofile-is-hardcoded-winrs--noprofile
Sep 27, 2026
Merged

bertysentry merged 1 commit into
mainfrom
139-configurable-user-profile-loading-winrs_noprofile-is-hardcoded-winrs--noprofile

Conversation

@bertysentry

Copy link
Copy Markdown
Contributor

Closes #139.

What

Load the user profile in the remote command shell, the opposite of winrs -noprofile. The default is unchanged: without the option, the client still sends WINRS_NOPROFILE=TRUE.

try (WinRMClient client = WinRMClient.builder("server.example.net")
        .credentials("DOMAIN\\user", password)
        .loadUserProfile()                       // every shell this client creates
        .build()) {
    client.command("reg query HKCU\\Software\\Vendor").execute();
}

CLI: --profile, valid with command and shell, like -d and --env.

Design: a client setting, not a per-command one

The issue proposed client.command(...).loadUserProfile(), pinned by the client's first command like workingDirectory(...). This PR makes it a builder setting instead (decided while implementing), next to consoleCodePage(...), whose WINRS_CODEPAGE travels in the same Create OptionSet:

Verified live

On Windows Server 2008 R2, with a local account that has no session on the host:

Without the profile (default) With --profile
%USERPROFILE%, starting directory C:\Users\Default C:\Users\<user>
%APPDATA%, %LOCALAPPDATA% not set the user's own
%TEMP% C:\Windows\Temp the user's own
HKEY_CURRENT_USER not the user's hive (a marker written with the profile is invisible) the user's hive

The CLI shell subcommand shows the same difference in its prompt: C:\Users\Default> without the option, C:\Users\<user>> with it.

On the Server 2016, 2019 and 2022 test hosts both modes behave the same, because those accounts' profiles were already loaded: interactive or disconnected sessions, and on the 2016 host a hive that the User Profile Service log shows loaded since 2026-09-04. The docs state this: without the option, a command sees the profile only if something else already loaded it.

Not verified: the failure for an account that is not a local administrator, because every test account is an administrator. The docs cite Microsoft's winrs documentation for it; the failure surfaces as a WinRMFaultException, the path every Create fault takes.

Tests

  • WinRMClientTest.expiredCachedShellIsRecreatedAndTheCommandRetried: with loadUserProfile(), both Create requests carry WINRS_NOPROFILE=FALSE, the original shell's and the one recreated after the reap.
  • WinRmCliTest: the full-stack exec and shell tests pass --profile and assert it on the wire (the shell session opens its own connection). The help test covers the new line.
  • CliArgumentsTest: off by default; --profile is rejected outside command and shell.
  • WsmanProtocolTest already asserted the default TRUE.
  • mvn clean verify site is green on JDK 25: 319 tests, and checkstyle, PMD and SpotBugs report nothing. CI runs it on JDK 17.

Docs

  • Remote Commands: a new Loading the user profile section.
  • CLI manual: the --profile row, and a fix to the -d default, which is C:\Users\Default when the profile is not loaded.
  • Migrating from winrm4j: winrm4j hardcodes WINRS_NOPROFILE=FALSE, so migrated commands lose the profile unless the client calls loadUserProfile().

Found along the way

#183: piping input into a command that exits without reading it fails the run with fault 232 and loses the output. It is a pre-existing race, unrelated to this change.

🤖 Generated with Claude Code

)

The shell Create request hardcoded WINRS_NOPROFILE=TRUE, so commands
could never get the user's profile. Unless something else had already
loaded it on the host (an interactive session, for example), they ran
with the Default profile: USERPROFILE and the starting directory were
C:\Users\Default, APPDATA was unset, and HKEY_CURRENT_USER was not the
user's hive.

- WinRMClient.Builder.loadUserProfile() sends WINRS_NOPROFILE=FALSE on
  every shell the client creates: commands, file transfers, remote file
  operations, and a shell recreated after the server reaped the
  previous one. Not loading the profile stays the default.
- It is a client setting, next to consoleCodePage (WINRS_CODEPAGE rides
  the same OptionSet), rather than the per-command option the issue
  proposed. With a per-command option pinned by the first command, a
  long-lived client would get whatever its first command happened to
  ask for, and the option would have had to be threaded through the
  WindowsRemoteExecutor SPI and every file-transfer leg.
- The unreleased LightWinRMService.createInstance overload added by
  #141 takes the new parameter; no further overload.
- CLI: --profile, for the command and shell subcommands.
- Docs: a "Loading the user profile" section in commands.md, the cli.md
  options table (and the -d default, which is C:\Users\Default when the
  profile is not loaded), and the winrm4j migration page: winrm4j
  always loads the profile.

Verified live on Windows Server 2008 R2 with an account that has no
session: without the profile, USERPROFILE and the starting directory
are C:\Users\Default, APPDATA is unset and an HKCU marker is invisible;
with --profile, all of them are the user's own. On hosts where the
account's profile was already loaded, both modes behave the same.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T22:58:44.216823Z 5ada4cf PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ada4cf891

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main/java/org/metricshub/winrm/light/LightWinRMService.java
@bertysentry
bertysentry merged commit 399b91c into main Sep 27, 2026
5 checks passed
@bertysentry
bertysentry deleted the 139-configurable-user-profile-loading-winrs_noprofile-is-hardcoded-winrs--noprofile branch September 27, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Configurable user-profile loading: WINRS_NOPROFILE is hardcoded (winrs -noprofile)

1 participant