＜Ansible＞変数を使用した処理のコントロール ~ 数値編 ~

#はじめに
この記事はAnsibleを勉強し始めた人を対象としたものです。
私自身は、IT未経験で今の会社に入社し、2年目に突入しました。
Ansibleは多少触ったことがある程度ですので、自由自在に使いこなせるよう、日々勉強中です。
#概要
変数（数値）を定義したファイルを読み取り、その変数を使用して処理をコントロールできることを目的とします。
変数定義ファイルを修正した際、他のファイルの修正を一切必要とせず、タスクの内容が記述通りに行われることを確認します。

前回は変数に『文字列』を定義して処理を実行するロール、Playbookを作成しましたが、今回は変数に『数値』を定義して処理を実行するロール、Playbookを作成します。
whenディレクティブで条件を記述し、変数（数値）の大きさを対象の値と比較します。
その対象の値を定義する場所の違いで、以下2つのパターンに分けました。

①比較対象となる値を直接whenディレクティブに書き込む（varsディレクトリ配下のYMLファイルで変数を定義）
②比較対象となる数値を定義するファイルを別に用意する（CSVファイルで変数を定義）

それでは、パターンごとにタスクの内容を説明していきます。
#①比較対象となる値を直接whenディレクティブに書き込む（varsディレクトリ配下のYMLファイルで変数を定義）
指定する温度により、水（H₂O）の状態を示すメッセージが異なるという処理を実装しました。
ディレクトリ構成、ファイルの中身は以下の通りです。
##### ディレクトリ構成
```
/etc/ansible
├── site.yml               # ロールを実行するPlaybook
└── roles                  # ロールを配置
    └── H2O                # ロール名のディレクトリ
        ├── tasks          # Tasksセクションを記載したファイルを配置
        │   └── main.yml   # タスク処理を定義
        └── vars           # Varsセクションを記載したファイルを配置
            └── main.yml   # 変数定義ファイル（温度を指定）
```
#####/etc/ansible/roles/H2O/tasks/main.yml（Playbook）
```
---
# 存在しないセ氏を指定
- name: Point out the mistake
  fail:
    msg: "-273.15 より低い温度は存在しません"
  when: Celsius <= -273.15

# H2Oのセ氏が-273.15 以上かつ0 以下
- name: Check if it is solid
  debug:
    msg: "H20は固体です"
  when: -273.15 <= Celsius <= 0

# H2Oのセ氏が0 以上かつ100 以下
- name: Check if it is liquid
  debug:
    msg: "H20は液体です"
  when: 0 <= Celsius <= 100

# H2Oのセ氏が100 以上かつ10000 以下
- name: Check if it is a gas
  debug:
    msg: "H2Oは気体です"
  when: 100 <= Celsius <= 10000

# H2Oのセ氏が10000 以上
- name: Check if it is plasma
  debug:
    msg: "H2Oはプラズマです"
  when: 10000 <= Celsius
...
```
##### /etc/ansible/roles/H2O/vars/main.yml（変数定義ファイル：『200』を指定）
```
---
Celsius: 200
...
```
上記のように、変数『Celsius』に『200』を指定すると次のような結果となります。

```
PLAY [localhost] *******************************************************************************************************************************************************

TASK [H2O : Point out the mistake] *************************************************************************************************************************************
skipping: [localhost]

TASK [H2O : Check if it is solid] **************************************************************************************************************************************
skipping: [localhost]

TASK [H2O : Check if it is liquid] *************************************************************************************************************************************
skipping: [localhost]

TASK [H2O : Check if it is a gas] **************************************************************************************************************************************
ok: [localhost] => {
    "msg": "H2Oは気体です"
}

TASK [H2O : Check if it is plasma] *************************************************************************************************************************************
skipping: [localhost]

PLAY RECAP *************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=4    rescued=0    ignored=0
```
whenディレクティブの条件に当てはまったので、"H2Oは気体です" というメッセージが出力されました。

ここで変数定義ファイルを修正し（『-273.15』を指定）、どのような結果になるのか確認してみましょう。
##### /etc/ansible/roles/H2O/vars/main.yml（変数定義ファイル：『-273.15』を指定）
```
---
Celsius: -273.15
```
```
PLAY [localhost] *******************************************************************************************************************************************************

TASK [H2O : Point out the mistake] *************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "-273.15 より低い温度は存在しません"}

PLAY RECAP *************************************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```
『Celsius』に『-273.15』未満の数値を指定すると、failモジュールによりメッセージを出力して処理を終了します。

これで、変数定義ファイルを修正した際、他ファイルの修正を一切しなくても、処理が行われることを確認できました。
#②比較対象となる数値を定義するファイルを別に用意する
指定する数値以上の市町村数を持つ都道府県を、リストにして表示するという処理を実装しました。
ディレクトリ構成、ファイルの中身は以下の通りです。
##### ディレクトリ構成

```
/etc/ansible
├── site.yml                 # ロールを実行するPlaybook
├── variable_definition.csv  # 変数定義ファイル
└── roles                    # ロールを配置
    └── Municipalities       # ロール名のディレクトリ
        ├── tasks            # Tasksセクションを記載したファイルを配置
        │   └── main.yml     # タスク処理を定義
        └── vars             # Varsセクションを記載したファイルを配置
            └── main.yml     # 市町村数を指定
```
##### /etc/ansible/variable_definition.csv（変数定義ファイル：47都道府県）
```
Prefecture,NumberOfMunicipalities
北海道,179
青森県,40
岩手県,33
宮城県,35
秋田県,25
山形県,35
福島県,59
茨城県,44
栃木県,25
群馬県,35
埼玉県,63
千葉県,54
東京都,39
神奈川県,33
新潟県,30
富山県,15
石川県,19
福井県,17
山梨県,27
長野県,77
岐阜県,42
静岡県,35
愛知県,54
三重県,29
滋賀県,19
京都府,26
大阪府,43
兵庫県,41
奈良県,39
和歌山県,30
鳥取県,19
島根県,19
岡山県,27
広島県,23
山口県,19
徳島県,24
香川県,17
愛媛県,20
高知県,34
福岡県,60
佐賀県,20
長崎県,21
熊本県,45
大分県,18
宮崎県,45
鹿児島県,43
沖縄県,41
```
#####  /etc/ansible/roles/Municipalities/tasks/main.yml（Playbook）
```
---
# 変数定義ファイルの読み込み
- name: Read CSV file
  read_csv:
    path: variable_definition.csv
  register: csv_data

# リストの定義
- name: Define empty list
  set_fact:
    new_list : []

# 指定した数以上の市町村数を持つ都道府県をリストに格納
- name: Store values in a list
  set_fact:
    new_list : "{{ new_list + [item.Prefecture] }}"
  with_items: "{{ csv_data.list }}"
  when: item.NumberOfMunicipalities | int >= "{{ number }}" | int

# リストの表示
- name: Display the result
  debug:
    msg: "{{ new_list }}"
...
```
#####/etc/ansible/roles/Municipalities/vars/main.yml（市町村数を指定）
```
---
number: 40
...
```
上記のように、変数『number』に『40』を指定すると次のような結果となります。

```
PLAY [localhost] *******************************************************************************************************************************************************

TASK [Municipalities : Read CSV file] **********************************************************************************************************************************
ok: [localhost]

TASK [Municipalities : Define empty list] ******************************************************************************************************************************
ok: [localhost]

TASK [Municipalities : Store values in a list] *************************************************************************************************************************
 [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: item.NumberOfMunicipalities | int >= "{{ number }}" |
int

ok: [localhost] => (item={u'Prefecture': u'\u5317\u6d77\u9053', u'NumberOfMunicipalities': u'179'})
ok: [localhost] => (item={u'Prefecture': u'\u9752\u68ee\u770c', u'NumberOfMunicipalities': u'40'})
skipping: [localhost] => (item={u'Prefecture': u'\u5ca9\u624b\u770c', u'NumberOfMunicipalities': u'33'})
skipping: [localhost] => (item={u'Prefecture': u'\u5bae\u57ce\u770c', u'NumberOfMunicipalities': u'35'})
skipping: [localhost] => (item={u'Prefecture': u'\u79cb\u7530\u770c', u'NumberOfMunicipalities': u'25'})
skipping: [localhost] => (item={u'Prefecture': u'\u5c71\u5f62\u770c', u'NumberOfMunicipalities': u'35'})
ok: [localhost] => (item={u'Prefecture': u'\u798f\u5cf6\u770c', u'NumberOfMunicipalities': u'59'})
ok: [localhost] => (item={u'Prefecture': u'\u8328\u57ce\u770c', u'NumberOfMunicipalities': u'44'})
skipping: [localhost] => (item={u'Prefecture': u'\u6803\u6728\u770c', u'NumberOfMunicipalities': u'25'})
skipping: [localhost] => (item={u'Prefecture': u'\u7fa4\u99ac\u770c', u'NumberOfMunicipalities': u'35'})
ok: [localhost] => (item={u'Prefecture': u'\u57fc\u7389\u770c', u'NumberOfMunicipalities': u'63'})
ok: [localhost] => (item={u'Prefecture': u'\u5343\u8449\u770c', u'NumberOfMunicipalities': u'54'})
skipping: [localhost] => (item={u'Prefecture': u'\u6771\u4eac\u90fd', u'NumberOfMunicipalities': u'39'})
skipping: [localhost] => (item={u'Prefecture': u'\u795e\u5948\u5ddd\u770c', u'NumberOfMunicipalities': u'33'})
skipping: [localhost] => (item={u'Prefecture': u'\u65b0\u6f5f\u770c', u'NumberOfMunicipalities': u'30'})
skipping: [localhost] => (item={u'Prefecture': u'\u5bcc\u5c71\u770c', u'NumberOfMunicipalities': u'15'})
skipping: [localhost] => (item={u'Prefecture': u'\u77f3\u5ddd\u770c', u'NumberOfMunicipalities': u'19'})
skipping: [localhost] => (item={u'Prefecture': u'\u798f\u4e95\u770c', u'NumberOfMunicipalities': u'17'})
skipping: [localhost] => (item={u'Prefecture': u'\u5c71\u68a8\u770c', u'NumberOfMunicipalities': u'27'})
ok: [localhost] => (item={u'Prefecture': u'\u9577\u91ce\u770c', u'NumberOfMunicipalities': u'77'})
ok: [localhost] => (item={u'Prefecture': u'\u5c90\u961c\u770c', u'NumberOfMunicipalities': u'42'})
skipping: [localhost] => (item={u'Prefecture': u'\u9759\u5ca1\u770c', u'NumberOfMunicipalities': u'35'})
ok: [localhost] => (item={u'Prefecture': u'\u611b\u77e5\u770c', u'NumberOfMunicipalities': u'54'})
skipping: [localhost] => (item={u'Prefecture': u'\u4e09\u91cd\u770c', u'NumberOfMunicipalities': u'29'})
skipping: [localhost] => (item={u'Prefecture': u'\u6ecb\u8cc0\u770c', u'NumberOfMunicipalities': u'19'})
skipping: [localhost] => (item={u'Prefecture': u'\u4eac\u90fd\u5e9c', u'NumberOfMunicipalities': u'26'})
ok: [localhost] => (item={u'Prefecture': u'\u5927\u962a\u5e9c', u'NumberOfMunicipalities': u'43'})
ok: [localhost] => (item={u'Prefecture': u'\u5175\u5eab\u770c', u'NumberOfMunicipalities': u'41'})
skipping: [localhost] => (item={u'Prefecture': u'\u5948\u826f\u770c', u'NumberOfMunicipalities': u'39'})
skipping: [localhost] => (item={u'Prefecture': u'\u548c\u6b4c\u5c71\u770c', u'NumberOfMunicipalities': u'30'})
skipping: [localhost] => (item={u'Prefecture': u'\u9ce5\u53d6\u770c', u'NumberOfMunicipalities': u'19'})
skipping: [localhost] => (item={u'Prefecture': u'\u5cf6\u6839\u770c', u'NumberOfMunicipalities': u'19'})
skipping: [localhost] => (item={u'Prefecture': u'\u5ca1\u5c71\u770c', u'NumberOfMunicipalities': u'27'})
skipping: [localhost] => (item={u'Prefecture': u'\u5e83\u5cf6\u770c', u'NumberOfMunicipalities': u'23'})
skipping: [localhost] => (item={u'Prefecture': u'\u5c71\u53e3\u770c', u'NumberOfMunicipalities': u'19'})
skipping: [localhost] => (item={u'Prefecture': u'\u5fb3\u5cf6\u770c', u'NumberOfMunicipalities': u'24'})
skipping: [localhost] => (item={u'Prefecture': u'\u9999\u5ddd\u770c', u'NumberOfMunicipalities': u'17'})
skipping: [localhost] => (item={u'Prefecture': u'\u611b\u5a9b\u770c', u'NumberOfMunicipalities': u'20'})
skipping: [localhost] => (item={u'Prefecture': u'\u9ad8\u77e5\u770c', u'NumberOfMunicipalities': u'34'})
ok: [localhost] => (item={u'Prefecture': u'\u798f\u5ca1\u770c', u'NumberOfMunicipalities': u'60'})
skipping: [localhost] => (item={u'Prefecture': u'\u4f50\u8cc0\u770c', u'NumberOfMunicipalities': u'20'})
skipping: [localhost] => (item={u'Prefecture': u'\u9577\u5d0e\u770c', u'NumberOfMunicipalities': u'21'})
ok: [localhost] => (item={u'Prefecture': u'\u718a\u672c\u770c', u'NumberOfMunicipalities': u'45'})
skipping: [localhost] => (item={u'Prefecture': u'\u5927\u5206\u770c', u'NumberOfMunicipalities': u'18'})
ok: [localhost] => (item={u'Prefecture': u'\u5bae\u5d0e\u770c', u'NumberOfMunicipalities': u'45'})
ok: [localhost] => (item={u'Prefecture': u'\u9e7f\u5150\u5cf6\u770c', u'NumberOfMunicipalities': u'43'})
ok: [localhost] => (item={u'Prefecture': u'\u6c96\u7e04\u770c', u'NumberOfMunicipalities': u'41'})

TASK [Municipalities : Display the result] *****************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "北海道",
        "青森県",
        "福島県",
        "茨城県",
        "埼玉県",
        "千葉県",
        "長野県",
        "岐阜県",
        "愛知県",
        "大阪府",
        "兵庫県",
        "福岡県",
        "熊本県",
        "宮崎県",
        "鹿児島県",
        "沖縄県"
    ]
}

PLAY RECAP *************************************************************************************************************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
指定した数値以上の市町村数を持つ都道府県がリストに格納されました。
勿論、whenディレクティブの不等号・等号を使い分けることによってリストに格納される値も変わります。

ここで変数定義ファイルを修正し（中国・四国地方と九州地方を除く）、どのような結果になるのか確認してみましょう。
#####/etc/ansible/variable_definition.csv（変数定義ファイル：中国・四国地方と九州地方を除いた30都道府県）
```
Prefecture,NumberOfMunicipalities
北海道,179
青森県,40
岩手県,33
宮城県,35
秋田県,25
山形県,35
福島県,59
茨城県,44
栃木県,25
群馬県,35
埼玉県,63
千葉県,54
東京都,39
神奈川県,33
新潟県,30
富山県,15
石川県,19
福井県,17
山梨県,27
長野県,77
岐阜県,42
静岡県,35
愛知県,54
三重県,29
滋賀県,19
京都府,26
大阪府,43
兵庫県,41
奈良県,39
和歌山県,30
```
```
PLAY [localhost] *******************************************************************************************************************************************************

TASK [Municipalities : Read CSV file] **********************************************************************************************************************************
ok: [localhost]

TASK [Municipalities : Define empty list] ******************************************************************************************************************************
ok: [localhost]

TASK [Municipalities : Store values in a list] *************************************************************************************************************************
 [WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: item.NumberOfMunicipalities | int >= "{{ number }}" |
int

ok: [localhost] => (item={u'Prefecture': u'\u5317\u6d77\u9053', u'NumberOfMunicipalities': u'179'})
ok: [localhost] => (item={u'Prefecture': u'\u9752\u68ee\u770c', u'NumberOfMunicipalities': u'40'})
skipping: [localhost] => (item={u'Prefecture': u'\u5ca9\u624b\u770c', u'NumberOfMunicipalities': u'33'})
skipping: [localhost] => (item={u'Prefecture': u'\u5bae\u57ce\u770c', u'NumberOfMunicipalities': u'35'})
skipping: [localhost] => (item={u'Prefecture': u'\u79cb\u7530\u770c', u'NumberOfMunicipalities': u'25'})
skipping: [localhost] => (item={u'Prefecture': u'\u5c71\u5f62\u770c', u'NumberOfMunicipalities': u'35'})
ok: [localhost] => (item={u'Prefecture': u'\u798f\u5cf6\u770c', u'NumberOfMunicipalities': u'59'})
ok: [localhost] => (item={u'Prefecture': u'\u8328\u57ce\u770c', u'NumberOfMunicipalities': u'44'})
skipping: [localhost] => (item={u'Prefecture': u'\u6803\u6728\u770c', u'NumberOfMunicipalities': u'25'})
skipping: [localhost] => (item={u'Prefecture': u'\u7fa4\u99ac\u770c', u'NumberOfMunicipalities': u'35'})
ok: [localhost] => (item={u'Prefecture': u'\u57fc\u7389\u770c', u'NumberOfMunicipalities': u'63'})
ok: [localhost] => (item={u'Prefecture': u'\u5343\u8449\u770c', u'NumberOfMunicipalities': u'54'})
skipping: [localhost] => (item={u'Prefecture': u'\u6771\u4eac\u90fd', u'NumberOfMunicipalities': u'39'})
skipping: [localhost] => (item={u'Prefecture': u'\u795e\u5948\u5ddd\u770c', u'NumberOfMunicipalities': u'33'})
skipping: [localhost] => (item={u'Prefecture': u'\u65b0\u6f5f\u770c', u'NumberOfMunicipalities': u'30'})
skipping: [localhost] => (item={u'Prefecture': u'\u5bcc\u5c71\u770c', u'NumberOfMunicipalities': u'15'})
skipping: [localhost] => (item={u'Prefecture': u'\u77f3\u5ddd\u770c', u'NumberOfMunicipalities': u'19'})
skipping: [localhost] => (item={u'Prefecture': u'\u798f\u4e95\u770c', u'NumberOfMunicipalities': u'17'})
skipping: [localhost] => (item={u'Prefecture': u'\u5c71\u68a8\u770c', u'NumberOfMunicipalities': u'27'})
ok: [localhost] => (item={u'Prefecture': u'\u9577\u91ce\u770c', u'NumberOfMunicipalities': u'77'})
ok: [localhost] => (item={u'Prefecture': u'\u5c90\u961c\u770c', u'NumberOfMunicipalities': u'42'})
skipping: [localhost] => (item={u'Prefecture': u'\u9759\u5ca1\u770c', u'NumberOfMunicipalities': u'35'})
ok: [localhost] => (item={u'Prefecture': u'\u611b\u77e5\u770c', u'NumberOfMunicipalities': u'54'})
skipping: [localhost] => (item={u'Prefecture': u'\u4e09\u91cd\u770c', u'NumberOfMunicipalities': u'29'})
skipping: [localhost] => (item={u'Prefecture': u'\u6ecb\u8cc0\u770c', u'NumberOfMunicipalities': u'19'})
skipping: [localhost] => (item={u'Prefecture': u'\u4eac\u90fd\u5e9c', u'NumberOfMunicipalities': u'26'})
ok: [localhost] => (item={u'Prefecture': u'\u5927\u962a\u5e9c', u'NumberOfMunicipalities': u'43'})
ok: [localhost] => (item={u'Prefecture': u'\u5175\u5eab\u770c', u'NumberOfMunicipalities': u'41'})
skipping: [localhost] => (item={u'Prefecture': u'\u5948\u826f\u770c', u'NumberOfMunicipalities': u'39'})
skipping: [localhost] => (item={u'Prefecture': u'\u548c\u6b4c\u5c71\u770c', u'NumberOfMunicipalities': u'30'})

TASK [Municipalities : Display the result] *****************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "北海道",
        "青森県",
        "福島県",
        "茨城県",
        "埼玉県",
        "千葉県",
        "長野県",
        "岐阜県",
        "愛知県",
        "大阪府",
        "兵庫県"
    ]
}

PLAY RECAP *************************************************************************************************************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```
変数定義ファイルの修正前と比べ、5つの県が標準出力から消えました。

これで、変数定義ファイルを修正した際、他ファイルの修正を一切しなくても、処理が行われることを確認できました。
# あとがき
実現したい処理を実装していくことで、また新たな情報を得ることができました。
次回はモジュールの使い方をまとめたいと思います。