{"id":47898257,"url":"https://github.com/prowler-cloud/py-pwsh-session","last_synced_at":"2026-04-04T03:57:30.278Z","repository":{"id":343217175,"uuid":"1033082350","full_name":"prowler-cloud/py-pwsh-session","owner":"prowler-cloud","description":"Python module to manage persistent PowerShell sessions, with support for command execution, JSON result parsing, and secure error handling.","archived":false,"fork":false,"pushed_at":"2026-04-02T06:29:30.000Z","size":2703,"stargazers_count":3,"open_issues_count":9,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-04T03:57:26.243Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prowler-cloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-06T09:16:12.000Z","updated_at":"2026-03-26T12:38:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/prowler-cloud/py-pwsh-session","commit_stats":null,"previous_names":["prowler-cloud/py-pwsh-session"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/prowler-cloud/py-pwsh-session","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prowler-cloud%2Fpy-pwsh-session","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prowler-cloud%2Fpy-pwsh-session/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prowler-cloud%2Fpy-pwsh-session/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prowler-cloud%2Fpy-pwsh-session/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prowler-cloud","download_url":"https://codeload.github.com/prowler-cloud/py-pwsh-session/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prowler-cloud%2Fpy-pwsh-session/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31387024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T01:22:39.193Z","status":"online","status_checked_at":"2026-04-04T02:00:07.569Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T03:57:29.702Z","updated_at":"2026-04-04T03:57:30.273Z","avatar_url":"https://github.com/prowler-cloud.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"imgs/py-pwsh-session.png\" alt=\"py-pwsh-session logo\" height=\"300\" /\u003e\n\n  \u003ch1\u003epy-pwsh-session\u003c/h1\u003e\n\n  \u003cp\u003e\u003cem\u003ePython module to manage persistent PowerShell sessions, with support for command execution, JSON result parsing, and secure error handling.\u003c/em\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n---\n\n**py-pwsh-session** is a modern, lightweight Python wrapper that provides seamless integration with PowerShell sessions. Built for developers who need reliable, persistent PowerShell automation in their Python applications.\n\n## Key Features\n\n- **Persistent Sessions** - Maintain long-running PowerShell sessions for faster execution and stateful operations\n- **Persistent Authentication** - Stay logged in to PowerShell modules and tools across multiple commands\n- **Secure by Design** - Built-in input sanitization prevents command injection attacks\n- **Asynchronous Processing** - Non-blocking command execution with configurable timeout handling\n- **Clean Output** - Automatic removal of ANSI escape sequences for parseable results\n- **JSON Support** - Native JSON parsing from PowerShell `ConvertTo-Json` output\n- **Rich Logging** - Automatic error logging from PowerShell stderr during command execution\n\n## How It Works\n\n### Connection Lifecycle\n\nThe following diagram shows how `py-pwsh-session` manages the full lifecycle of a PowerShell session, from initialization to cleanup:\n\n```mermaid\nflowchart TD\n    A[Python Application] --\u003e|\"PowerShellSession()\"| B[Spawn pwsh subprocess]\n    B --\u003e|\"pwsh -NoExit -Command -\"| C[Persistent Session Ready]\n    C --\u003e|\"execute(cmd)\"| D[Write command to stdin]\n    D --\u003e E[Append END markers to stdout/stderr]\n    E --\u003e F[Read stdout in daemon thread]\n    E --\u003e G[Read stderr in daemon thread]\n    F --\u003e|\"Collect lines until END\"| H{json_parse?}\n    G --\u003e|\"Collect errors until END\"| I[Log errors via logger]\n    H --\u003e|Yes| J[Parse JSON from output]\n    H --\u003e|No| K[Return raw string]\n    J --\u003e L[Return dict/list]\n    K --\u003e L\n    L --\u003e|Next command| D\n    L --\u003e|Done| M[\"close() / __exit__()\"]\n    M --\u003e N[Send exit + terminate process]\n    N --\u003e O[Close stdin/stdout/stderr pipes]\n```\n\n### Command Execution Flow\n\nEach call to `execute()` follows this sequence internally:\n\n```mermaid\nsequenceDiagram\n    participant App as Python App\n    participant Session as PowerShellSession\n    participant PS as pwsh Process\n    participant T1 as stdout Thread\n    participant T2 as stderr Thread\n\n    App-\u003e\u003eSession: execute(\"Get-Process\", json_parse=True, timeout=10)\n    Session-\u003e\u003ePS: stdin: \"Get-Process | ConvertTo-Json\\n\"\n    Session-\u003e\u003ePS: stdin: \"Write-Output '\u003cEND\u003e'\\n\"\n    Session-\u003e\u003ePS: stdin: \"Write-Error '\u003cEND\u003e'\\n\"\n\n    Session-\u003e\u003eT1: Start daemon thread (read stdout)\n    Session-\u003e\u003eT2: Start daemon thread (read stderr)\n\n    PS--\u003e\u003eT1: stdout lines...\n    PS--\u003e\u003eT1: \"\u003cEND\u003e\"\n    T1--\u003e\u003eSession: Joined output via Queue\n\n    PS--\u003e\u003eT2: stderr lines...\n    PS--\u003e\u003eT2: \"Write-Error: \u003cEND\u003e\"\n    T2--\u003e\u003eSession: Joined errors via Queue\n\n    Session-\u003e\u003eSession: Log errors (if any)\n    Session-\u003e\u003eSession: json_parse_output(output)\n    Session--\u003e\u003eApp: Return parsed dict/list\n```\n\n### Persistent Sessions: Why They Matter\n\nUnlike running individual `subprocess.run([\"pwsh\", \"-Command\", \"...\"])` calls, `py-pwsh-session` keeps a **single PowerShell process alive** across multiple commands.\n\n```mermaid\nflowchart LR\n    subgraph without[\"Without py-pwsh-session\"]\n        direction TB\n        A1[\"subprocess.run(Connect-AzAccount)\"] --\u003e A2[\"Start pwsh → Auth ✅ → Exit ❌\"]\n        A3[\"subprocess.run(Get-AzVM)\"] --\u003e A4[\"Start pwsh → Not authenticated ❌\"]\n        A5[\"subprocess.run(Get-AzVM)\"] --\u003e A6[\"Start pwsh → Not authenticated ❌\"]\n    end\n```\n\nThis is critical when working with **Microsoft PowerShell modules** (Azure, Exchange Online, Microsoft Graph, etc.) that require authentication. With individual subprocess calls, each command spawns a new `pwsh` process that dies immediately after execution — the authenticated session dies with it, making it impossible to run follow-up queries. With `py-pwsh-session`, you authenticate once and the session stays alive, so all subsequent commands run in the same authenticated context.\n\n```mermaid\nflowchart LR\n    subgraph with[\"With py-pwsh-session\"]\n        direction LR\n        B1[\"PowerShellSession()\"] --\u003e B2[\"execute(Connect-AzAccount) ✅\"]\n        subgraph session[\"Authenticated Session\"]\n            B3[\"execute(Get-AzVM) ✅\"]\n            B4[\"execute(Get-AzStorageAccount) ✅\"]\n            B5[\"execute(...) ✅\"]\n        end\n        B2 --\u003e session\n        session --\u003e B6[\"close()\"]\n    end\n```\n\n| Aspect | `subprocess.run()` per command | `py-pwsh-session` session |\n|---|---|---|\n| Process startup | Every command | Once |\n| Authentication | Lost after each command | Persists across commands |\n| Variables/state | Lost between calls | Preserved |\n| Module queries | Fail (no auth context) | Work (same session) |\n| Performance | Slow (process spawn overhead) | Fast (reuse open process) |\n\n## Requirements\n\n- **Python 3.10+**\n- **PowerShell Core (pwsh)** - Must be installed and available in PATH\n- **Operating System**: Windows, macOS, or Linux with PowerShell Core\n\n### Installing PowerShell Core\n\n**Windows:**\n```powershell\nwinget install Microsoft.PowerShell\n```\n\n**macOS:**\n```bash\nbrew install powershell\n```\n\n**Linux (Ubuntu/Debian):**\n```bash\nsudo apt update\nsudo apt install -y powershell\n```\n\n## Quick Start\n\n### Installation\n\n```bash\npip install py-pwsh-session\n```\n\n### Basic Usage\n\n```python\nfrom py_powershell import PowerShellSession\n\n# Create a persistent PowerShell session\npwsh = PowerShellSession()\n\n# Execute a simple command (returns raw string)\nresult = pwsh.execute(\"Get-Process | Select-Object -First 5\")\nprint(result)\n\n# Execute with JSON parsing and custom timeout\ndata = pwsh.execute(\"Get-Service | Select-Object -First 3\", json_parse=True, timeout=15)\nprint(data)  # Returns a Python dict/list\n\n# Always close the session when done\npwsh.close()\n```\n\n### Context Manager (Recommended)\n\nThe context manager ensures the session is always properly closed, even if an exception occurs:\n\n```python\nfrom py_powershell import PowerShellSession\n\nwith PowerShellSession() as pwsh:\n    services = pwsh.execute(\"Get-Service\", json_parse=True, timeout=20)\n\n    for service in services:\n        print(f\"Service: {service['Name']} - Status: {service['Status']}\")\n# Session is automatically closed here\n```\n\n## Usage Examples\n\n### Leveraging Session Persistence\n\nVariables, modules, and authentication persist across commands within the same session:\n\n```python\nfrom py_powershell import PowerShellSession\n\nwith PowerShellSession() as pwsh:\n    # Set a variable in the session\n    pwsh.execute(\"$myList = @()\")\n\n    # Accumulate data across multiple commands — state is preserved\n    pwsh.execute(\"$myList += 'item1'\")\n    pwsh.execute(\"$myList += 'item2'\")\n    pwsh.execute(\"$myList += 'item3'\")\n\n    # Retrieve the accumulated result\n    result = pwsh.execute(\"$myList | ConvertTo-Json\", json_parse=True)\n    print(result)  # ['item1', 'item2', 'item3']\n```\n\n### Cloud Authentication (Azure Example)\n\nAuthenticate once, run multiple commands without re-authenticating:\n\n```python\nfrom py_powershell import PowerShellSession\n\nwith PowerShellSession() as pwsh:\n    # Authenticate once — session stays logged in\n    pwsh.execute(\"Connect-AzAccount\", timeout=30)\n\n    # All subsequent commands reuse the authenticated session\n    vms = pwsh.execute(\n        \"Get-AzVM | Select-Object Name, ResourceGroupName, Location\",\n        json_parse=True,\n        timeout=30,\n    )\n    for vm in vms:\n        print(f\"VM: {vm['Name']} in {vm['Location']}\")\n\n    # No need to re-authenticate\n    storage = pwsh.execute(\n        \"Get-AzStorageAccount | Select-Object StorageAccountName, Location\",\n        json_parse=True,\n        timeout=30,\n    )\n    for account in storage:\n        print(f\"Storage: {account['StorageAccountName']}\")\n```\n\n### Input Sanitization\n\nThe `sanitize()` method prevents command injection by filtering unsafe characters:\n\n```python\nfrom py_powershell import PowerShellSession\nimport os\n\nwith PowerShellSession() as pwsh:\n    # Sanitize user-provided input before using it in commands\n    raw_input = os.environ.get(\"USER_EMAIL\", \"\")\n    safe_input = pwsh.sanitize(raw_input)\n    # \"user@domain.com; rm -rf /\" → \"user@domain.com\"\n\n    result = pwsh.execute(f\"Get-ADUser -Filter {{EmailAddress -eq '{safe_input}'}}\", json_parse=True)\n```\n\n### JSON Parsing\n\nUse `json_parse=True` to automatically pipe output through `ConvertTo-Json` and parse it into Python objects:\n\n```python\nfrom py_powershell import PowerShellSession\n\nwith PowerShellSession() as pwsh:\n    # Returns a Python dict/list instead of a raw string\n    processes = pwsh.execute(\n        \"Get-Process | Select-Object Name, Id, CPU -First 5\",\n        json_parse=True,\n    )\n\n    # Work with structured data directly\n    for proc in processes:\n        print(f\"PID {proc['Id']}: {proc['Name']} (CPU: {proc.get('CPU', 'N/A')})\")\n\n    # Without json_parse, you get the raw PowerShell text output\n    raw = pwsh.execute(\"Get-Date\")\n    print(raw)  # \"Saturday, March 7, 2026 12:00:00 PM\"\n```\n\n### Timeout Handling\n\nConfigure timeouts per command based on expected execution time:\n\n```python\nfrom py_powershell import PowerShellSession\n\nwith PowerShellSession() as pwsh:\n    # Quick command — short timeout (default is 10s)\n    date = pwsh.execute(\"Get-Date\", timeout=5)\n\n    # Medium operation\n    services = pwsh.execute(\"Get-Service\", json_parse=True, timeout=20)\n\n    # Long-running operation (e.g., cloud API calls)\n    all_vms = pwsh.execute(\"Get-AzVM -Status\", json_parse=True, timeout=120)\n\n    # If a command exceeds the timeout, an empty string is returned\n    # and the session remains usable for subsequent commands\n```\n\n### Error Handling and Logging\n\nPowerShell errors are automatically captured from stderr and logged:\n\n```python\nimport logging\nfrom py_powershell import PowerShellSession\n\n# Configure logging to see PowerShell errors\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"%(asctime)s - %(name)s - %(levelname)s - %(message)s\",\n)\n\nwith PowerShellSession() as pwsh:\n    # If the command produces an error, it's logged automatically\n    result = pwsh.execute(\"Get-NonExistentCommand\", timeout=5)\n    # Log output: ERROR - py_powershell.powershell_session -\n    #   PowerShell error output: The term 'Get-NonExistentCommand' is not recognized...\n\n    if not result:\n        print(\"Command failed — check logs for details\")\n\n    # The session is still usable after an error\n    date = pwsh.execute(\"Get-Date\")\n    print(date)\n```\n\n### Extending PowerShellSession\n\nSubclass `PowerShellSession` to add custom initialization or error handling:\n\n```python\nfrom py_powershell import PowerShellSession\n\n\nclass AzurePowerShellSession(PowerShellSession):\n    \"\"\"A session that auto-connects to Azure on init.\"\"\"\n\n    def __init__(self, subscription_id: str):\n        super().__init__()\n        self.execute(f\"Connect-AzAccount -SubscriptionId '{self.sanitize(subscription_id)}'\", timeout=30)\n\n    def _process_error(self, error_result: str) -\u003e None:\n        \"\"\"Custom error handling for Azure-specific errors.\"\"\"\n        if \"AuthorizationFailed\" in error_result:\n            raise PermissionError(f\"Azure authorization failed: {error_result}\")\n        super()._process_error(error_result)\n\n\nwith AzurePowerShellSession(\"your-subscription-id\") as az:\n    vms = az.execute(\"Get-AzVM\", json_parse=True, timeout=30)\n```\n\n## Architecture\n\n### Class Hierarchy\n\n```\nPowerShellSession\n├── __init__()          # Spawn persistent pwsh subprocess\n├── execute()           # Execute commands with optional JSON parsing\n├── read_output()       # Read stdout/stderr with timeout via threads\n├── json_parse_output() # Extract and parse JSON from raw output\n├── sanitize()          # Filter input to prevent command injection\n├── remove_ansi()       # Strip ANSI escape sequences from output\n├── _process_error()    # Handle stderr output (overridable)\n├── close()             # Send exit, terminate process, close pipes\n├── __enter__()         # Context manager entry\n└── __exit__()          # Context manager exit with cleanup\n```\n\n## Contributing\n\nWe welcome contributions! Please submit pull requests or open issues on GitHub.\n\n## Support\n\n- **Documentation**: [Read the full documentation](https://docs.prowler.cloud)\n- **Issues**: [Report bugs or request features](https://github.com/prowler-cloud/py-pwsh-session/issues)\n- **Discussions**: [Join the community discussions](https://github.com/prowler-cloud/py-pwsh-session/discussions)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprowler-cloud%2Fpy-pwsh-session","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprowler-cloud%2Fpy-pwsh-session","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprowler-cloud%2Fpy-pwsh-session/lists"}