You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:35:00