Asp.Net Core Blazor调用PowerShell脚本在Windows Server 2019无结果排查
Let's break down why your Blazor app works on your dev machine but returns empty results on Windows Server 2019—this is almost always a mix of subtle code gaps and server-specific configuration differences, not a hard bug in your core logic.
First, Check for Code Defects
Your current code has a few blind spots that could cause silent failures even if the PowerShell command runs:
Cmdlet Spelling Typo
You wroteget-Hotfixein your script, but the official Windows PowerShell cmdlet isGet-HotFix(no trailing 'e'). If your dev machine has a custom alias for the misspelled version but the server doesn't, this would explain the empty result. Even if you meant to call an ECM-specific cmdlet (like something for ConfigMgr), double-check the spelling—PowerShell will silently return nothing if it can't find a cmdlet with that name (no error thrown unless you set$ErrorActionPreference = Stop).PSObject ToString() Limitation
When you callitem.ToString()on results fromPowerShell.Invoke(), you're only getting the type name of thePSObject(likeSystem.Management.Automation.PSCustomObject) instead of actual data. This means even if the cmdlet returns valid output, your code will append empty strings to theStringBuilder. Fix this by either accessing PSObject properties directly or converting output to JSON in the script:// Option 1: Access PSObject properties explicitly foreach (var item in result) { foreach (var prop in item.Properties) { sb.AppendLine($"{prop.Name}: {prop.Value}"); } sb.AppendLine("---"); } // Option 2: Modify script to return JSON for easy parsing string script = "Get-HotFix | ConvertTo-Json";Incomplete Error Handling
Your code only checksStreams.Error, but PowerShell might generate non-terminating errors that don't populate this stream. Add checks for warning/verbose streams and force non-terminating errors to become terminating ones with$ErrorActionPreference = 'Stop':string script = "$ErrorActionPreference = 'Stop'; Get-HotFix";Missing Module Imports
If you're calling ConfigMgr-specific cmdlets (not justGet-HotFix), your dev machine might auto-load the Configuration Manager module because you've run the console, but the server's runspace is a clean session. Explicitly import the module in your script:string script = @" $ErrorActionPreference = 'Stop' # Load ConfigMgr module (adjust path if needed) Import-Module $env:SMS_ADMIN_UI_PATH\..\ConfigurationManager.psd1 -Force # Replace with your actual ConfigMgr cmdlet, e.g., Get-CMDevice ";
Next, Server-Specific Configuration Checks
Even with fixed code, Windows Server 2019 has key differences from Windows 10 that could cause this issue:
Application Pool Identity Mismatch
You said the same user can run the command interactively, but Blazor apps hosted on IIS run under the application pool identity by default (e.g.,IIS AppPool\DefaultAppPool), not your user account. To fix this:- Open IIS Manager, navigate to your app's application pool
- Under "Advanced Settings", set "Identity" to the user account that can run the PowerShell command successfully
- Ensure this user has permissions to access ConfigMgr and run scripts on the server
Non-Interactive Session Environment Variables
TheSMS_ADMIN_UI_PATHvariable (used to load the ConfigMgr module) is only set when you launch the ConfigMgr console interactively. In a non-interactive runspace (like your Blazor app uses), this variable doesn't exist. Hardcode the module path instead:string script = @" $ErrorActionPreference = 'Stop' # Hardcode ConfigMgr module path (adjust to your server's path) Import-Module 'C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1' -Force # Your cmdlet here ";32-bit vs 64-bit Mismatch
If your Blazor app runs in a 32-bit application pool (check IIS app pool settings: "Enable 32-bit Applications" set to True), it will launch a 32-bit PowerShell runspace. The ConfigMgr module is 64-bit only, so this will silently fail with no results. Set "Enable 32-bit Applications" to False to use 64-bit PowerShell.Execution Policy Restrictions
While you can run the command interactively, non-interactive sessions might have a stricter execution policy. Check the server's policy withGet-ExecutionPolicy -List, and if needed, set a policy that allows script execution for the app's user (e.g.,Set-ExecutionPolicy RemoteSigned -Scope CurrentUser).
Fixed Code Example
Here's a revised version of your code with these fixes applied:
string script = @" $ErrorActionPreference = 'Stop' # Load ConfigMgr module (adjust path as needed) Import-Module 'C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1' -Force # Replace with your actual ConfigMgr cmdlet, e.g., Get-CMDevice Get-HotFix | ConvertTo-Json "; using (PowerShell powerShell = PowerShell.Create()) using (Runspace runspace = RunspaceFactory.CreateRunspace()) { runspace.Open(); powerShell.Runspace = runspace; powerShell.AddScript(script); var result = powerShell.Invoke(); // Check all error/warning streams if (powerShell.Streams.Error.Count > 0) { var errors = string.Join(Environment.NewLine, powerShell.Streams.Error.Select(e => e.ToString())); return $"Error: {errors}"; } if (powerShell.Streams.Warning.Count > 0) { var warnings = string.Join(Environment.NewLine, powerShell.Streams.Warning.Select(w => w.ToString())); return $"Warning: {warnings}"; } if (result.Count == 0) { return "No results returned from PowerShell command."; } // Since we used ConvertTo-Json, the result is a single string return $"{result[0].ToString()} Success!"; }
内容的提问来源于stack exchange,提问作者TheBlackOne

