如何让Azure DevOps交互式代理启动的进程在流水线作业结束后持续运行?
问题
需要通过Azure DevOps流水线将一个长期运行的C#可执行文件部署到Windows Server 2019机器上,要求:
- 以提升权限运行
- 必须在UI会话中运行(需调用无法在会话0中运行的外部应用)
- 已使用交互式代理,但流水线作业结束后,可执行文件会被关闭,无法保持运行
复现流水线
以下YAML可复现该问题:
jobs: - deployment: environment: MyEnvironment.MyInteractiveAgent strategy: runOnce: deploy: steps: - powershell: Start-Process -Verb RunAs C:\Windows\notepad.exe
现象对比
- 手动在目标机器上以流水线用户身份执行
Start-Process -Verb RunAs C:\Windows\notepad.exe,关闭PowerShell窗口后记事本仍保持运行 - 通过流水线执行相同命令,流水线作业结束后记事本会被关闭
进程树分析
使用PowerShell Core脚本查看进程父级关系:
$process = Get-Process notepad; while ($process) { Write-Host "$($process.Id) - $($process.Name)"; $process = $process.Parent; }
结果对比:
| 手动启动进程树 | 代理启动进程树 |
|---|---|
| 9812 - notepad | 3940 - notepad |
| 3480 - powershell | 1676 - powershell |
| 5004 - explorer | 11388 - powershell |
| 1720 - Agent.Worker | |
| 15748 - Agent.Listener | |
| 14708 - cmd |
可见代理启动的记事本属于Agent进程树的子进程,流水线结束时Agent会终止整个子进程树,导致程序关闭。
解决方案
要让进程脱离Azure DevOps代理的进程树,避免被作业结束时终止,可采用以下几种方法:
1. 创建Windows计划任务
通过schtasks命令创建立即执行的计划任务,让进程以独立的父进程(taskeng.exe)启动,脱离代理进程树:
# 获取当前活跃的UI会话ID(以explorer进程所属会话为例) $sessionId = (Get-Process -Name explorer | Select-Object -First 1).SessionId # 创建并立即执行计划任务,启动目标程序(替换为你的C#程序路径) schtasks /create /tn "StartMyAppTask" /tr "C:\Windows\notepad.exe" /sc once /st 00:00 /ru "NT AUTHORITY\SYSTEM" /rl highest /f /sd 01/01/2000 schtasks /run /tn "StartMyAppTask" # 可选:执行完成后删除临时任务 schtasks /delete /tn "StartMyAppTask" /f
若需指定特定用户会话,可添加/s $env:COMPUTERNAME /u <用户名> /p <密码>参数。
2. 使用NSSM将程序注册为服务(适配UI会话)
NSSM(Non-Sucking Service Manager)可将普通程序注册为Windows服务,同时支持指定运行的UI会话ID,让程序脱离代理进程树并长期运行:
# 假设NSSM已下载至C:\tools\nssm.exe # 注册服务 C:\tools\nssm.exe install MyAppService "C:\Path\To\Your\CSharpApp.exe" # 设置服务以最高权限运行 C:\tools\nssm.exe set MyAppService ObjectName "NT AUTHORITY\SYSTEM" # 设置服务自动启动 C:\tools\nssm.exe set MyAppService Start SERVICE_AUTO_START # 指定在UI会话1中运行(根据实际会话ID调整) C:\tools\nssm.exe set MyAppService AppSessionId 1 # 启动服务 Start-Service MyAppService
此方法适合需要随系统自动启动的长期运行程序。
3. PowerShell 7+ 用-Detached参数启动进程
PowerShell 7及以上版本的Start-Process支持-Detached参数,可直接让进程脱离父进程树:
Start-Process -Verb RunAs -NoNewWindow -Detached C:\Windows\notepad.exe
需确保Azure DevOps代理使用的是PowerShell Core环境。
4. 使用PsExec在用户会话启动进程
通过PsExec工具在指定UI会话中启动进程,让进程归属到用户会话的进程树:
# 假设PsExec已下载至C:\tools\psexec.exe,目标会话ID为1 C:\tools\psexec.exe -i 1 -h C:\Windows\notepad.exe
-i指定会话ID,-h表示以最高权限运行。
内容的提问来源于stack exchange,提问作者Matthias Schuchardt
相关产品推荐
相关产品推荐

