PowerShell脚本手动运行正常,Ansible启动时创建word.application失败
问题描述
通过Ansible的community.windows.win_psexec模块执行PowerShell脚本,任务配置如下:
- name: Running my-script.ps1 community.windows.win_psexec: executable: D:\Repos\my-repo\ansible\roles\PsExec\files\PsExec.exe command: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy ByPass -File "D:\Repos\my-repo\my-script.ps1" -param1 {{ param1_value }} -param2 {{ param2_value }} -switchParam elevated: yes interactive: yes no_log: false
脚本执行到创建Word COM对象时抛出错误:
TerminatingError(New-Object): "Retrieving the COM class factory for component with CLSID {000209FF-0000-0000-C000-000000000046} failed due to the following error: 80080005 Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE))." New-Object : Retrieving the COM class factory for component with CLSID {000209FF-0000-0000-C000-000000000046} failed due to the following error: 80080005 Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).
手动登录目标主机运行脚本一切正常,但通过Ansible执行时失败。已尝试以下操作但无效:
- 添加
Add-Type -AssemblyName Microsoft.Office.Interop.Word(注:原提问中拼写错误为Officer,已修正) - 将创建对象代码改为
$word = [Microsoft.Office.Interop.Word.ApplicationClass]::new()
问题原因
- COM组件的桌面会话依赖:Microsoft Word的COM对象属于需要用户桌面环境的交互式组件,无法在无桌面的非交互式会话中运行。
- PsExec的默认会话问题:即使设置了
interactive: yes,win_psexec默认会在Windows的会话0(系统服务专用会话)中执行,该会话没有用户桌面;而手动登录属于用户交互式会话(通常为会话1及以上),具备完整桌面环境。 - 权限上下文差异:Ansible通过win_psexec执行时的用户上下文,未关联到当前登录用户的桌面会话,导致COM对象初始化时无法获取必要的系统资源。
解决办法
方法1:指定PsExec运行到用户交互式会话
修改Ansible任务,通过win_psexec的session参数指定目标主机当前登录用户的会话ID,或直接指定console会话:
- name: Running my-script.ps1 community.windows.win_psexec: executable: D:\Repos\my-repo\ansible\roles\PsExec\files\PsExec.exe command: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy ByPass -File "D:\Repos\my-repo\my-script.ps1" -param1 {{ param1_value }} -param2 {{ param2_value }} -switchParam elevated: yes interactive: yes # 指定会话ID,可通过目标主机执行query session获取当前登录用户的会话ID session: 1 # 或直接指定console会话(对应当前登录的控制台会话) # session: console no_log: false
若不确定会话ID,可先通过Ansible任务自动获取:
- name: Get active user session ID ansible.windows.win_command: query session register: session_output - name: Extract session ID set_fact: target_session_id: "{{ session_output.stdout | regex_search('>(\\d+)\\s+Active') | regex_replace('>(\\d+)\\s+Active', '\\1') }}"
方法2:改用无需桌面的Word操作方案
如果业务允许,建议使用Open XML SDK(如DocumentFormat.OpenXml NuGet包)代替Word COM对象操作文档,该方案无需依赖桌面会话,更适合自动化脚本场景:
# 示例:使用PSWriteWord模块(基于Open XML SDK)操作Word文档 Install-PackageProvider -Name NuGet -Force Install-Module -Name PSWriteWord -Force $doc = Get-WordDocument -Path "D:\test.docx" # 后续文档操作...
方法3:调整Windows组策略(不推荐,存在安全风险)
修改组策略允许服务与桌面交互:
- 打开
gpedit.msc - 导航到计算机配置>管理模板>系统>服务
- 启用允许服务与桌面交互
内容的提问来源于stack exchange,提问作者jerdub1993

