Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Otherwise, error could be raised due to inconsistency.
- [Affinity setting](#affinity-setting)
- [Path manipulation](#path-manipulation)
- [Additional info about specific Connection implementation](#additional-info-about-specific-connection-implementation)
- [RPyCConnection Windows run-as-user and user management](#rpycconnection-windows-run-as-user-and-user-management)
- [Execute command with sudo](#execute-command-with-sudo)
- [Execute background command with SSHConnection](#execute-background-command-with-sshconnection)
- [Recommendation for Connection choice](#recommendation-for-connection-choice)
Expand Down Expand Up @@ -745,6 +746,43 @@ Below some known issues correlated with this functionality:
* Linux: BgServingThread **must be enabled** to read process output
* Simics: There is knows issue with long connection time if BgServingThread is enabled, **recommend disabling it**

### RPyCConnection Windows run-as-user and user management

On Windows targets, `RPyCConnection` provides additional helpers for running a command as another user and for local user lifecycle operations.

Available methods:

* `execute_command_as_user(command: str, *, user: str, password: str, domain: str | None = None, cwd: str | None = None, timeout: int | None = None, env: dict[str, str] | None = None, expected_return_codes: Iterable | None = frozenset({0}), shell: bool = False, custom_exception: Type[CalledProcessError] | None = None, skip_logging: bool = False) -> ConnectionCompletedProcess`
* `create_user(username: str, password: str, *, expected_return_codes: Iterable | None = frozenset({0}), custom_exception: Type[CalledProcessError] | None = None, skip_logging: bool = False) -> ConnectionCompletedProcess`
* `delete_user(username: str, *, expected_return_codes: Iterable | None = frozenset({0}), custom_exception: Type[CalledProcessError] | None = None, skip_logging: bool = False) -> ConnectionCompletedProcess`

Notes:

* The methods above are supported only on Windows targets.
* `execute_command_as_user` uses a temporary WinAPI helper on the remote host (`CreateProcessWithLogonW`).
* For local accounts, use `domain=None` (or `domain="."`).

Example:

```python
from mfd_connect import RPyCConnection

conn = RPyCConnection(ip="10.10.10.10")

conn.create_user(username="mfd_temp_user", password="pass")
try:
result = conn.execute_command_as_user(
command="whoami",
user="mfd_temp_user",
password="pass",
domain=".",
timeout=60,
)
print(result.stdout)
finally:
conn.delete_user(username="mfd_temp_user")
```

#### PythonConnection/SSHConnection output redirection to file

For start process methods there are 2 parameters:
Expand Down
37 changes: 37 additions & 0 deletions examples/rpyc_run_as_user_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (C) 2026 Intel Corporation
# SPDX-License-Identifier: MIT

"""Example: Windows run-as-user and local account management over RPyCConnection."""

from mfd_connect import RPyCConnection

# Configure connection to Windows host with running RPyC server.
conn = RPyCConnection(ip="10.10.10.10")

# Temporary local account used for the demo.
username = "mfd_temp_user"
password = "pass"

try:
# Create local user.
create_result = conn.create_user(username=username, password=password)
print("CREATE USER")
print(f"return_code: {create_result.return_code}")

# Execute command as created user.
run_result = conn.execute_command_as_user(
command="whoami",
user=username,
password=password,
domain=".",
timeout=60,
)
print("RUN AS USER")
print(f"stdout: {run_result.stdout}")
print(f"stderr: {run_result.stderr}")
print(f"return_code: {run_result.return_code}")
finally:
# Delete temporary user even if command execution fails.
delete_result = conn.delete_user(username=username, expected_return_codes={0, 2})
print("DELETE USER")
print(f"return_code: {delete_result.return_code}")
4 changes: 4 additions & 0 deletions mfd_connect/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ class RPyCDeploymentException(ModuleFrameworkDesignError):
"""Handle exceptions when deployment found issue."""


class RunAsUserError(ModuleFrameworkDesignError):
"""Raised when run-as-user execution setup is invalid."""


class MissingPortablePythonOnServerException(RPyCDeploymentException):
"""Handle exceptions when not found portable python on server."""

Expand Down
Loading
Loading