Ansible自定义模块开发的核心是用Python编写符合规范的独立脚本:输入必须通过stdin接收JSON参数,输出必须是含changed和msg字段的JSON到stdout,文件名即模块名且置于library/等正确路径。

Ansible 模块化开发的核心,是用 Python 写一个能被 Playbook 调用、在目标主机上独立运行的小程序。它不是写个 Shell 脚本再用 script 模块去跑,而是真正成为 Ansible 的“一等公民”——支持参数校验、幂等性、检查模式(check_mode)、结构化返回,还能和 Ansible 的错误处理、日志、回调机制无缝协同。
自定义模块必须满足的三个硬要求
不满足以下任意一条,模块就无法被 Ansible 正确识别或稳定运行:
- 输入统一走 stdin:Ansible 把任务参数序列化为 JSON 后,通过标准输入传给模块脚本,不能依赖命令行参数或环境变量传参
-
输出必须是合法 JSON 到 stdout:结果字典至少包含
changed(布尔值)和msg(字符串),推荐用AnsibleModule.exit_json()或fail_json()统一输出,避免手动print(json.dumps(...))出错 -
文件名即模块名,且放在正确路径:比如想用
- name: my_upload调用,模块文件就得叫my_upload.py,并放在library/目录下(项目级)或全局模块路径(如/usr/share/ansible/modules/)
从零写一个带参数校验的文件写入模块
以“写入指定内容到远程文件”为例,展示企业级开发习惯:
- 用
AnsibleModule初始化,声明argument_spec明确参数类型、是否必填、默认值和校验规则(比如path必须是绝对路径,content长度不能超 10KB) - 主动支持
check_mode:如果用户加了--check,模块只做预判,不真实写文件,但返回changed=True表示“若执行就会变更” - 异常捕获要具体:区分
IOError(磁盘满/权限不足)、ValueError(内容非法)等,并用module.fail_json(msg=...)抛出带上下文的错误,方便 Playbook 中failed_when判断 - 幂等逻辑写清楚:先读原文件,比对内容是否一致;一致则
changed=False,避免无谓覆盖
部署与调试的实用技巧
别等写完再测试,边写边验证更高效:
- 本地快速验证:直接在控制节点运行模块脚本,模拟 Ansible 输入,例如:
echo '{"path":"/tmp/test.txt","content":"hello"}' | python library/my_upload.py - Playbook 中启用详细输出:
ansible-playbook deploy.yml -vvv可看到模块传输过程、临时路径、完整 JSON 输入输出 - Python 版本注意:模块首行
#!/usr/bin/env python3要和目标主机实际 Python 解释器匹配;若部分主机只有 Python 2,要么统一升级,要么在ansible.cfg中配置interpreter_python = /usr/bin/python3 - 模块路径优先级:Playbook 当前目录下的
library/>ANSIBLE_LIBRARY环境变量 >ansible.cfg中配置的library路径 > 默认系统路径;多项目共用时建议用环境变量统一管理
什么场景值得写自定义模块,而不是拼内置模块?
不是所有需求都该写模块,判断依据很实际:
- 调用内部 API:比如向 CMDB 提交资产变更、触发工单系统审批、查询监控平台阈值 —— 这些没有现成模块,且涉及鉴权、重试、字段映射等复杂逻辑
-
封装重复判断链:例如“先查进程是否存在 → 存在则杀掉 → 再拉起新版本 → 最后验证端口监听”,拆成多个
command或shell任务可读性差、难复用、失败点分散 -
需要精确控制 changed 状态:内置
copy模块只看文件内容和属性,但业务上可能要求“只要时间戳更新就算 changed”,或“仅当配置项实际生效才标记 changed” -
跨平台行为差异大:比如在 Linux 上用
systemctl,在 AIX 上用startsrc,在 Windows 上用win_service—— 自定义模块可内部做 OS 判定,对外暴露统一接口

















