Edited at
AnsibleDay 18

これだけ覚えとけばもう怖くないAnsibleの(動的)変数


概要

 Ansibleで必ず使うことになる変数ですが、プレイブック内で動的にセットする変数(set_fact)で思うように値を作ることができないことありませんか?私もAnsibleを使い始めた頃は、こういう変数がほしいけどどうやってつくるんだろう・・・っと悩んだ末、冗長化しまくりのイケてないコードをズラズラ書いていました。しかし、ブロックスタイルJinja Templateで自由自在に変数を作ることができますのでもう悩むことはない!


ブロックスタイル

 ヒアドキュメント(Here Document)に似たような感じで一つの変数や処理に対して複数行の値を入力することができる。可読性を高める効果もある(例↓)


sample-heredocument

> cat << EOF

The Ansible Advent Calendar !!!
Enjoy Ansible ♪
EOF

------------------
出力結果
------------------
The Ansible Advent Calendar !!!
Enjoy Ansible ♪


Ansibleでは以下のようになります

正確に言うとBlock Scalar Styleというらしいですが、YAMLのリファレンスで定義されています

- set_fact:

example: >
The Ansible Advent Calendar !!!
Enjoy Ansible ♪

------------------
出力結果
------------------
ok: [localhost] => {
"example": "The Ansible Advent Calendar !!! Enjoy Ansible ?\n"
}

上記の例では> (folded style)を使用しましたが、結果からわかるように途中の改行コード(\n)が取り除かれています

改行が必要な場合は|(literal style)を使用しましょう。また、最終行に改行の有無の指定もできますので、こちらのわかりやすい解説サイトで違いを確認しましょう!


Jinja Template

 Ansibleでお馴染みのJinja Templateですが、前述のブロックスタイルの中でフル活用することで自由自在に変数を操れるようになります!


基礎編


# monthが12の場合は、"The Ansible xxxx...(省略)"と表示
# そうではない場合は、"Coming soon..."と表示
- set_fact:
example: >-
{%- set month = 12 -%}
{%- if month == 12 -%}
The Ansible Advent Calendar !!!
Enjoy Ansible ♪
{%- else -%}
Coming soon...
{%- endif -%}

------------------
出力結果
------------------
ok: [localhost] => {
"example": "The Ansible Advent Calendar !!! Enjoy Ansible ?\n"
}


ポイント

{%--%}内にJinjaテンプレートで使える各種ステートメントが利用できるので、その中で条件分岐、ループ、変数の生成が可能


応用編


# キーaの値が3のdictにnew_dictを追加する
- set_fact:
my_list_dict: [{'a': 1, 'b': 2},{'a': 3, 'd': 2}]
new_dict: {'c': 4}

- set_fact:
example2: >-
{%- set _my_list_dict = my_list_dict -%}
{%- for _dict in _my_list_dict -%}
{%- if _dict['a'] == 3 -%}
{%- set dummy = _dict.update(new_dict) -%}
{%- endif -%}
{%- endfor -%}
{%- set dummy = _my_list_dict.append({'a': 5, 'z': 0}) -%}
{{ _my_list_dict }}

------------------
出力結果
------------------
ok: [localhost] => {
"example2": [
{
"a": 1,
"b": 2
},
{
"a": 3,
"c": 4,
"d": 2
},
{
"a": 5,
"z": 0
}
]
}


ポイント1

 すでにset_factで定義されている変数はテンプレート内で扱えますが、テンプレート内で定義されている変数のスコープはテンプレート内だけになります。set_factの変数(my_list_dict)に代入したい場合は{{ _my_list_dict }}のように出力する必要があります。変数の型(list、dictとか)も保証されて出力されます。ただし、複数回出力した場合は文字列扱いになる


ポイント2

{%- set dummy = _dict.update(new_dict) -%}のように_dictに新たにnew_dictを追加するように既存の変数を更新する処理では、必ずset 変数名 = 値でなければならないため、dummyという変数を使っています

@fumiyas よりアドバイスで


ansible.cfg

[defaults]

jinja2_extensions=jinja2.ext.do

にすることで、set dummy = _dict.update(new_dict) でなく do _dict.update(new_dict)と記述できるそうです


ポイント3

 list型はappendで要素を追加、dict型はupdateで要素の更新ができます