文中代码是基于 ansible 2.3.2.0 和 python 2.7.12 编写。

案例背景:

客户有一批 shell 脚本,希望可以通过 jenkins 结合 ansible,把脚本分发到目标机器上执行。
但脚本里有使用 ${VAR1} 这样的变量,每台服务器的变量值都不一样,客户不想改动 shell 脚本的内容。所以我们提出把 shell 改成 j2 模板的建议被否决了。

分析解决:

一开始我们认为直接用 shell 模块,export 相关变量值就好了。但结果发现是不可以的,变量并没保存到目标机器上。我猜想这是 ansible 的保护机制,避免不同的 playbook 在目标机器上执行的时候,相互覆盖环境变量。

参考了一下 stackoverflow 里的问答,我们变通了思路,写出一个 demo:

test.yml

- hosts: localhost
  connection: local
  tasks:
    - file:
        path: /tmp/testrc
        state: touch

    - lineinfile:
        path: /tmp/testrc
        line: 'export {{ item }}'
      with_items:
        - NAME1={{ name1 }}
        - NAME2={{ name2 }}

    - shell: '. /tmp/testrc && bash /tmp/test.sh'

test.sh

#!/usr/bin/env bash

touch /tmp/${NAME1}
touch /tmp/${NAME2}
touch /tmp/test.txt

注意:ansible 是无法调用 source 命令的,所以我们用 . 代替。然后,执行

ansible test.yml --extra-vars=" \
  name1=haibin \
  name2=someone"

就可以在执行 test.sh 之前,把环境变量导入了。

进一步完善

假如生成的 /tmp/testrc 里包含有敏感信息,怎么办?我们可以把 test.yml 改进一下:

- hosts: localhost
  connection: local
  tasks:
    - file:
        path: /tmp/testrc
        state: touch

    - lineinfile:
        path: /tmp/testrc
        line: 'export {{ item }}'
      with_items:
        - NAME1={{ name1 }}
        - NAME2={{ name2 }}

    - shell: '. /tmp/testrc && bash /tmp/test.sh'
    
    - file:
        path: /tmp/testrc
        state: absent

不管 shell 执行成不成功,敏感内容都是要删掉的~