Ansibleのshellモジュールとインデント

確認version

  • Ansible 2.7.0
  • Python 3.6.5

Ansibleのshellモジュールに記載した実行コマンドは、スペースとタブが取り除かれて実行される。そのためPythonのようなインデントが意味を持つプログラムを実行することができない。

- name: exec python program
  shell: |
    python -c '
    import sys
    for i in range(1, 20):
      if i % 3 == 0 and i % 5 == 0:
        print(i)
        sys.exit()
    '
  register: result

- name: print result
  debug: msg="{{ result }}"

このroleを実行すると以下のようなエラーが出力されてしまう。"cmd"の部分で実行されるスクリプトを確認するとインデントが消えていることがわかる。また"stderr_lines"にはIndentationError: expected an indented blockと出力されている。

TASK [test : exec python program] ********************************************************************************************************************************************
fatal: [server01]: FAILED! => {"changed": true, "cmd": "python -c '\nimport sys\nfor i in range(1, 20):\nif i % 3 == 0 and i % 5 == 0:\nprint(i)\nsys.exit()\n'", "delta": "0:00:00.040754", "end": "2018-10-26 20:39:53.171771", "failed": true, "rc": 1, "start": "2018-10-26 20:39:53.131017", "stderr": "  File \"<string>\", line 4\n    if i % 3 == 0 and i % 5 == 0:\n     ^\nIndentationError: expected an indented block", "stderr_lines": ["  File \"<string>\", line 4", "    if i % 3 == 0 and i % 5 == 0:", "     ^", "IndentationError: expected an indented block"], "stdout": "", "stdout_lines": []}

インデントを残したままshellモジュールに渡す方法

インデントを残したままshellモジュールに渡すには、ロール内でset_factを使って変数にプログラムをセットし、その変数をshellモジュールに渡す。

args.executableにpythonを切り出せるし、さらにpython -c '...'というようにシングルクォーテーションでプログラム本体をくくる必要がないので、プログラム内でシングルクォーテーションをエスケープを気にせず使用することができる。

- name: create python program
  set_fact:
    script_content: |
      import sys
      for i in range(1, 20):
        if i % 3 == 0 and i % 5 == 0:
          print(i)
          sys.exit()

- name: exec python program
  shell: "{{ script_content }}"
  args:
    executable: python
  register: result

- name: print result
  debug: msg="{{ result.stdout }}"

これを実行すると以下のように正しく実行できた。

TASK [test : print result] ***************************************************************************************************************************************************
ok: [server01] => {
    "msg": "15"
}

参考

Shell module does not respect indentation with multiline command