Windows 作为 Ansible 节点的完整部署流程(含 Docker 部署 Ansible)
前言
在日常运维中,Windows 主机上往往存在大量的 bat、ps1 脚本,每台机器跑的脚本还不一样,有些甚至需要多台机器联动。手动维护既麻烦,又容易出错。
我的场景里,Windows 系统非常关键,不允许开发人员或普通用户随意访问,因此需要一个安全、统一的方式来管理这些脚本。
最开始考虑的方案是 Jenkins 或 Ansible:
- Jenkins 的优势在于能直观看到节点是否在线,但权限粒度不足:用户可以新建任意任务并选择指定节点执行,风险太大(比如误操作删除文件)。
目前开发同事工作需要登录 Jenkins 并创建 Job。
- Ansible 没有内置的节点在线监控,且 Windows 作为节点无需额外安装 agent 和开启 SSH 服务器,只要启用系统自带的 WinRM 功能即可。
Ansible 目前完全由 IT 管理,不用担心权限泄露问题。
综合考虑后,还是选择了 Ansible 来统一管理 Windows 脚本。下面记录下从原理到完整落地的全过程。
一、工作原理
Windows 作为 Ansible 节点的核心机制,可以用一句话概括:
控制端发送 PowerShell/命令 → Windows 执行 → 返回 JSON 结果 → 控制端解析
具体分为几个部分:
-
三要素
- 控制节点(Linux 上的 Ansible)
- 被管理节点(Windows,不需要安装 Ansible)
- 通信协议(WinRM,默认端口 5985/5986)
-
连接与认证
- 控制端通过
pywinrm
库和 Windows WinRM 服务通信 - 常见认证:basic、NTLM、Kerberos
- 内网环境可以用 basic+HTTP,生产推荐 Kerberos/HTTPS
- 控制端通过
-
模块执行流程
- 控制端选择目标主机
- 建立 WinRM 会话
- 传输 PowerShell 模块/命令到 Windows
- Windows 执行并产出 JSON 结果
- 结果返回控制端解析
-
文件与脚本管理
win_copy
:复制文件win_command
/win_shell
:执行 bat/命令win_powershell
:执行 PowerShell 脚本
-
幂等性
- 尽量使用模块(如
win_feature
、win_package
),而不是裸命令 - 自写脚本需处理“检查-执行”逻辑
- 尽量使用模块(如
-
权限
- 通常用管理员账号或
become_method: runas
- 通常用管理员账号或
-
离线环境
- Windows 开启 WinRM 不需要联网
- 控制端 Linux 如需安装
pywinrm
,可提前下载 wheel 包离线安装
二、Ansible 版本要求
-
2.8 及以上版本原生支持 Windows 节点
-
推荐使用最新稳定版,兼容性更好
-
查看版本:
ansible --version
-
检查支持的模块:
ansible-doc -l | grep win
如果版本过旧,可以通过 pip install --upgrade ansible
升级(离线环境提前下载好包)。
三、环境部署
这里给一个完整的部署流程。
Ansible 部署(Docker)
- 1、Ansible 官方 镜像不更新了
- 2、用 Python 镜像部署 Ansible 更加轻量级,更方便自定义
所以下面使用 Python 镜像进行 Ansible 部署。
Ansible 容器镜像
docker pull python:3.9.23-trixie
拉取 python:3.9.23-trixie 容器镜像
编写 Dockerfile 并构建镜像
vim DockerfileFROM python:3.9.23-trixie
ARG http_proxy="http://IP:PORT"
ARG https_proxy="http://IP:PORT" # 代理,用不上可以不加
RUN apt update && apt install -y sshpass git
RUN pip install ansible pywinrmdocker build -t python:3.9.23-trixie-ansible .
测试
docker run --rm python:3.9.23-trixie /bin/bash -c "ansible --version && ansible-doc -l | grep win"
显示 Ansible 版本号和 win 支持模块即容器镜像完成构建。
启动 Ansible
docker run -it --name ansible -v /root/ansible/ansible:/etc/ansible python:3.9.23-trixie /bin/bash
把本地 /root/ansible/ansible 目录映射到容器的 /etc/ansible 目录
不用 -p 映射端口出来,Ansible 能访问 Win 节点端口就行,Win 节点不用访问 Ansible。
三、Windows 节点配置
为方便测试,后续连接 Wiundows 均使用以下账号密码:
- 账号:lian
- 密码:lian
且 Windows 的 IP 为 192.168.62.148
开启 WinRM
- Ansible 官方在 Github 上有个开启 WinRM 脚本的 Powershell 脚本
https://github.com/ansible/ansible-documentation/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
但官方不建议使用这个脚本(安全问题),建议最好自己敲 PowerShell 命令去开启服务,如果非要用的话建议删除并只保留自己需要的命令。
感兴趣的可以试一下上面 ps1 脚本,开启 WinRM 总共就两条命令,这里我就手敲执行好了。
管理员 PowerShell 执行:
# 启用 WinRM(管理员)
winrm quickconfig -q# 允许 Basic 认证 & 明文传输(建议仅限内网/测试用)
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
# 执行这一句的时候会要求网络类型改为‘域’或‘专用’(默认‘公共’)
# 在‘网络设置’→‘以太网’→‘网络’→点击勾选‘专用’,再执行命令即可生效# 放行防火墙
New-NetFirewallRule -Name "WinRM" -DisplayName "WinRM" -Protocol TCP -LocalPort 5985 -Action Allow# 授权用户可通过 WinRM 远程执行命令(Remote Management Users 是 Windows 用户组)
net localgroup "Remote Management Users" ansible /add# 检查监听器
winrm e winrm/config/listener
四、配置 Ansible Inventory
在 Ansible 的 /etc/ansible/hosts
内写入:
[windows]
192.168.62.148[windows:vars]
ansible_user=lian
ansible_password=lian
ansible_connection=winrm
ansible_winrm_transport=basic
ansible_port=5985
⚠ 注意:如果用 Docker 容器跑的 Ansible,
192.168.62.148
必须能从宿主机/容器访问。
五、测试连通性
在 Ansible 容器里执行:
ansible windows -m win_ping
成功输出:
win01 | SUCCESS => {"changed": false,"ping": "pong"
}
六、执行脚本示例
把 Windows 上的脚本都放到 Ansible 控制端,然后在执行时传输到 Windows 节点再运行,这样 更好管理 脚本。
先准备个测试用的 BAT 批处理脚本(test.bat):
@echo off
chcp 65001 >nulecho ================================
echo 当前路径信息
echo ================================
echo 当前路径是:%cd%
echo.
echo 当前目录下的文件和文件夹:
dir
echo.echo ================================
echo 当前时间和时区信息
echo ================================
echo 当前日期和时间:%date% %time%
echo.
echo 当前时区:
where tzutil >nul 2>&1 && tzutil /g
echo.pause
PowerShell 脚本(test.ps1):
# 获取当前时间
$time = Get-Date -Format "yyyy-MM-dd HH:mm:ss"# 获取计算机名
$computer = $env:COMPUTERNAME# 获取当前用户名
$user = $env:USERNAME# 输出结果
Write-Output "当前时间: $time"
Write-Output "计算机名: $computer"
Write-Output "当前用户名: $user"
这两个脚本都放到
Ansible
的 /etc/ansible/file 目录下。
在 Ansible 上调用 Windows bat 脚本,有三种解决方法:
方法 1:win_copy
+ 执行
先把脚本从控制节点复制到 Windows,再执行。
vim test1.yml
- name: Run scripts on Windowshosts: windowstasks:- name: Copy bat file to Windowsansible.windows.win_copy:src: /etc/ansible/file/test.batdest: C:\Users\lian\Desktop\script\test.bat- name: Run bat fileansible.windows.win_command: C:\Users\lian\Desktop\script\test.bat- name: Copy ps1 script to Windowsansible.windows.win_copy:src: /etc/ansible/file/test.ps1dest: C:\Users\lian\Desktop\script\test.ps1- name: Run ps1 fileansible.windows.win_powershell:script: |& "C:\Users\lian\Desktop\script\test.ps1"
ansible-playbook test1.yml
这样 Windows 脚本统一放在 Ansible 项目里(比如
/etc/ansible/file/
目录),方便版本管理。
如果要打印脚本输出的,playbook 脚本改为如下:
- name: Run scripts on Windowshosts: windowstasks:- name: Copy bat file to Windowsansible.windows.win_copy:src: /etc/ansible/file/test.batdest: C:\Users\lian\Desktop\script\test.bat- name: Run bat file and show outputansible.windows.win_command: C:\Users\lian\Desktop\script\test.batregister: bat_result- name: Display bat outputdebug:var: bat_result.stdout_lines- name: Copy ps1 script to Windowsansible.windows.win_copy:src: /etc/ansible/file/test.ps1dest: C:\Users\lian\Desktop\script\test.ps1- name: Run ps1 file and show outputansible.windows.win_shell: powershell.exe -ExecutionPolicy Bypass -File C:\Users\lian\Desktop\script\test.ps1register: ps1_result- name: Display ps1 outputdebug:var: ps1_result.stdout_lines
另外会发现,Windows 执行这些脚本只有返回到 Ansilble 只有执行和打印输出信息,如果脚本本身就有问题,Ansible 执行过程是不会中断、也不会有脚本以外的报错等信息。
这样可以结合输出,追加脚本打印输出,跟据 Windows 执行脚本时返回的信息,判断输出结果符不符合预期(如:Success),符合的话就是正确执行。
方法 2:直接传脚本内容(小脚本用)
如果脚本不大,可以直接嵌到 Playbook,用 win_powershell
/ win_shell
的 inline script 方式:
- name: Run scripts on Windows (inline)hosts: windowstasks:- name: Run bat inlineansible.windows.win_shell: |if not exist "C:\Users\lian\Desktop\script\test2.bat" echo. > "C:\Users\lian\Desktop\script\test2.bat"echo echo Hello from BAT > "C:\Users\lian\Desktop\script\test2.bat""C:\Users\lian\Desktop\script\test2.bat"- name: Run ps1 inlineansible.windows.win_powershell:script: |Write-Output "Hello from PowerShell"
缺点:脚本内容写在 YAML 里,不好维护,适合临时命令。
方法 3:Ansible roles + 模板化(更适合大规模)
和 方法 1 差不多。
把 bat/ps1 脚本放到 /etc/ansible/file/
目录下,用 win_copy
统一分发并执行,方便团队协作和版本控制。
vim test3.yml
- name: Copy and run BAThosts: windowstasks:- block:- name: Copy BAT filewin_copy:src: /etc/ansible/file/test.batdest: C:\Users\lian\Desktop\script\test.bat- name: Run BAT filewin_command: C:\Users\lian\Desktop\script\test.battags:- bat- name: Copy and run PS1hosts: windowstasks:- block:- name: Copy PS1 filewin_copy:src: /etc/ansible/file/test.ps1dest: C:\Users\lian\Desktop\script\test.ps1- name: Run PS1 filewin_powershell:script: C:\Users\lian\Desktop\script\test.ps1tags:- ps1
执行:
ansible-playbook -i windows test3.yml -t bat
注意事项
测试过程中,发现 Windows LTSC 版阉 割太多,好多事情都做不了,一个简单的 CMD
、简单的 bat 脚本
,都会把变量里的内容打印并识别成执行语句。
Ansible 里的 win_xx
等命令就更不用说了,Windows LTSC 上很多依赖调用不了,执行会报错。
当然,用 Windows Home 版 作为 Ansbile 节点也不行,因为它无法开启 WinRM 服务。
所以实际上,只能用 Server 版 和 专业/企业版 作为 Ansible 节点。
好在我实际使用场景都是 Window Server 版,所以不用担心功能严格问题。
总结
- Ansible 控制端(Docker 内)通过 Python 的
pywinrm
库发送 WinRM 请求。 - Windows 节点 WinRM 服务接收请求 → 验证用户身份 → 调用 PowerShell 引擎执行命令。
- 返回结果 通过 WinRM 协议传回 Ansible。
- Ansible 汇总输出,显示在控制端。
虽说 Windows 的 bat/ps1 脚本也可以存放到 Windows 节点上,但不好管理。
Windows 的脚本都放到 Ansible 上,并且 Playbook 也在 Ansible 上,再加上 Git 仓库,这样便于对整套 Playbook 和 Windows 脚本进行版本管理。可实现,一个平台(Ansible)上就可以完成脚本编写 + 下发了。