.NET 6 Web应用RunImpersonated执行PowerShell脚本身份模拟失败
.NET 6 Web应用执行PowerShell脚本时身份模拟失效问题
我开发的.NET 6 Web应用需要执行PowerShell脚本以创建和管理AD账户,当前采用HangFire启动后台任务处理脚本执行逻辑。已实现ImpersonationService,使用WindowsIdentity.RunImpersonated和WindowsIdentity.RunImpersonatedAsync方法进行身份模拟。
身份模拟在API调用场景中正常生效,但执行PowerShell脚本时模拟身份失效,脚本仍以IIS APPPOOL\APP NAME账户运行。我的核心需求是使用具备AD操作权限的服务账户执行脚本。
经调研得知,PowerShell执行线程不会继承当前的模拟身份,尝试多种解决方法均未成功,若无法通过应用内模拟用户执行脚本,也可接受替代方案。
当前架构流程
HangFire后台任务入队
BackgroundJob.Enqueue(() => RunScripts(mac.Username, mac.Id, username));
RunScripts方法实现
该方法创建待传递至ImpersonationService的操作逻辑:
public async Task RunScripts(string username, int macId, string responsibleUser) { var parameters = new Dictionary<string, object>() { { "MACID", macId } }; var auditId = await _macAuditTrailService.InitiateScriptProgressTracking(macId, username, "PowerShell Script Execution", responsibleUser); var response = new PowerShellRunResponseDto { Response = new PSDataCollection<PSObject>() }; try { // Prepare the action to be run under impersonation Action runPowerShellScript = () => { var runningAs = WindowsIdentity.GetCurrent()?.Name; parameters.Add("RunningAs", runningAs); // Directly invoke the script as a synchronous method within the impersonated context response = _powerShellRunspaceService.RunScript(parameters, auditId, macId).Result; // Ensure RunScript can be called synchronously }; // Run the action under impersonated context _impersonationService.RunImpersonated(runPowerShellScript, false); } catch (Exception e) { // Any exceptions thrown by PowerShell Automation will be caught and logged here response.Errors = e.Message; } // Audit the script execution await _scriptProgressService.AuditPowerShellScripts(response, username, action: "PowerShell Script Execution", macId: macId, responsibleUser: responsibleUser, auditId: auditId); Console.WriteLine("Script execution completed."); }
测试的三种身份模拟方法
RunImpersonated
public void RunImpersonated(Action action, bool isAderantCall) { SafeAccessTokenHandle safeAccessTokenHandle; bool returnValue = isAderantCall ? LogonUser(username, "Domain", password, 9, 0, out safeAccessTokenHandle) : LogonUser(username, "Domain", password, 9, 0, out safeAccessTokenHandle); if (!returnValue) { int ret = Marshal.GetLastWin32Error(); safeAccessTokenHandle.Dispose(); throw new System.ComponentModel.Win32Exception(ret); } try { WindowsIdentity.RunImpersonated(safeAccessTokenHandle, action); } finally { safeAccessTokenHandle.Dispose(); } }
RunImpersonatedAsync
逻辑与RunImpersonated一致,使用WindowsIdentity.RunImpersonatedAsync异步版本实现。
RunImpersonatedThread
public void RunImpersonatedThread(Action action, bool isAderantCall) { Thread newThread = new Thread(() => { SafeAccessTokenHandle safeAccessTokenHandle; bool returnValue = isAderantCall ? LogonUser(username, "Domain", password, 9, 0, out safeAccessTokenHandle) : LogonUser(username, "Domain", password, 9, 0, out safeAccessTokenHandle); if (!returnValue) { int ret = Marshal.GetLastWin32Error(); safeAccessTokenHandle.Dispose(); throw new System.ComponentModel.Win32Exception(ret); } using (safeAccessTokenHandle) { var identity = new WindowsIdentity(safeAccessTokenHandle.DangerousGetHandle()); // Using the synchronous version of RunImpersonated WindowsIdentity.RunImpersonated(identity.AccessToken, () => { action(); // Execute the passed in action synchronously }); } }); newThread.Start(); newThread.Join(); // Optionally wait for the thread to complete }
RunScript函数实现
该函数负责创建Runspace并执行PowerShell脚本:
public async Task<PowerShellRunResponseDto> RunScript(Dictionary<string, object> scriptParameters, int auditId, int macId, string runningAs = "") { var dto = new PowerShellRunResponseDto(); InitialSessionState state = InitialSessionState.CreateDefault(); state.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted; using (Runspace runspace = RunspaceFactory.CreateRunspace(state)) { runspace.Open(); using (PowerShell ps = PowerShell.Create(runspace)) { ps.AddScript( "Write-Output 'Current User:'; Write-Output $([Security.Principal.WindowsIdentity]::GetCurrent().Name)"); ps.Invoke(); var scriptPath = _scriptExecutionSettings.BaseUrl + _scriptExecutionSettings.ScriptName; // Add the script to the PowerShell instance ps.AddCommand(scriptPath); // Append each parameter to the command foreach (var kvp in scriptParameters) { ps.AddParameter(kvp.Key, kvp.Value); } ps.Streams.Progress.DataAdded += async (sender, e) => { if (sender is PSDataCollection<ProgressRecord> progressRecords) { var progress = progressRecords[e.Index].Activity; Console.WriteLine("Progress: " + progress); // Update the database with the progress asynchronously try { await _scriptProgressService.UpdateScriptProgress(auditId, macId, progress, _ScriptName); } catch (Exception ex) { throw new Exception( $"An error occurred while updating powershell script progress: {ex.Message}"); } } }; // Execute the script and await the result. var pipelineObjects = await ps.InvokeAsync().ConfigureAwait(false); // Check for errors var errors = ""; if (ps.Streams.Error.Count > 0) { foreach (var error in ps.Streams.Error) { Console.WriteLine(error.ToString()); errors += error.ToString() + '\n'; } } // Print the resulting pipeline objects to the console. foreach (var item in pipelineObjects) { Console.WriteLine(item.BaseObject.ToString()); } // Assign output to PowerShellRunResponseDto for Auditing logs dto.Errors = errors; dto.Data = ps.Streams; dto.Response = pipelineObjects; } runspace.Close(); } return dto; }
当前架构已实现脚本实时进度反馈功能,仅需解决PowerShell执行时身份模拟失效的问题。
内容的提问来源于stack exchange,提问作者Wahab Chaudhry
相关产品推荐
相关产品推荐

