てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] モジュールのオプションのデフォルト値をカスタマイズできる module_defaults

はじめに

Playbookではモジュールにオプションを指定してさまざまな処理を自動化していきます。

Playbookを書いていくうちに、このモジュール(またはコレクション単位)のこのオプションにはいつも同じ値を設定する、というときはないでしょうか。

たとえば ansible.builtin.uri モジュールをたくさん使っていて、毎回 force_basic_auth: true を指定しているようなケースです。

モジュールの仕様としてデフォルト値が設定されている場合もありますが、別のデフォルト値を設定したかったり、そもそもデフォルト値がないオプションにデフォルト値を設定したくなります。

こんなときに便利な module_defaults というキーワードがあります。

docs.ansible.com

module_defaults を使うと、都度都度モジュールごとに毎回同じオプションを指定しなくて良くなります。

module_defaults は、モジュール単位で指定する方法と、「グループ」によって複数モジュールにまるごと指定する方法があります。

指定方法1: モジュール単位で指定

公式ドキュメントのサンプルを引用します。

- hosts: localhost
  module_defaults:
    ansible.builtin.uri:
      force_basic_auth: true
      user: some_user
      password: some_password
  tasks:
    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever1

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever2

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever3

冒頭の module_defaults のところで、ansible.builtin.uri モジュールに対して3つのオプションのデフォルト値を設定しています。

ansible.builtin.uri モジュールの仕様としては、force_basic_auth オプションのデフォルト値は yesuserpassword オプションはデフォルト値なしです。これらのデフォルト値を上書き、または新たに設定している、ということです。

これを、仮に module_defautls を使わずタスクごとに指定する場合は、以下のような Playbook になります。少々ノイジーになってしまうかなと思います。

- hosts: localhost
  tasks:
    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever1
        force_basic_auth: true    # タスクごとに指定
        user: some_user           # タスクごとに指定
        password: some_password   # タスクごとに指定

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever2
        force_basic_auth: true    # タスクごとに指定
        user: some_user           # タスクごとに指定
        password: some_password   # タスクごとに指定

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever3
        force_basic_auth: true    # タスクごとに指定
        user: some_user           # タスクごとに指定
        password: some_password   # タスクごとに指定

オプションの値は変数にすれば統一管理できますが、タスクごとにオプションを指定すること自体は避けられません。module_defaults を使うとこのような重複を避けられて便利です。

指定方法2: グループによる複数モジュールまとめた指定

モジュールの中には、共通のオプションをもったものがあります。こういうときのために、これらを「グループ」で定義してまとめてデフォルト値を設定する Module defaults groupsという機能があります。

以前からも、aws や、azuregcp など向けのモジュールのいくつかのグループが、Ansible本体に定義されていました

module_defaults でグループまとめて指定する場合は、以下のようにします。

  module_defaults:
    group/グループ名:
      オプション名1: 値1
      オプション名2: 値2

ansible-core 2.12 からは、コレクション側でグループを定義できるようになりました。これが後押しになったのか、グループ化されたモジュールも増えてきました。これはかなり便利だなと思っています。

グループ名は各コレクションの meta/runtime.yml ファイル内の、action_group キー配下で確認できます。

meta/runtime.yml は、コレクションを管理しているリポジトリ側でも確認できます(利用するバージョンと同じものを確認する)し、もちろんインストールした手元の環境でも確認できます。

以下、一つのコレクションをもとに説明します。

netbox.netbox コレクションの場合

netbox.netbox コレクション 3.8.0 の場合は、meta/runtime.ymlに以下の定義があります。(この定義は 3.8.0 からできました

action_groups:
  netbox:
    - netbox_aggregate
    - netbox_cable
    - netbox_circuit
#...(略)...

これは、netbox_aggregatenetbox_cable などモジュールなどを netbox というグループでまとめてるよ、という意味です。

なので、module_defaults の指定の仕方は以下のようになります。

  module_defaults:
    group/netbox.netbox.netbox:
      オプション名1: 値1
      オプション名2: 値2
#...(略)...

グループ名が netbox.netbox.netbox と、わけがわからない感じになってますが、netbox.netbox コレクション内の netbox グループ、という指定です。最後の netboxmeta/runtime.ymlに定義されているグループ名です。

おためし

netbox.netbox コレクション内のモジュールでで試した結果をまとめます

  • 環境
    • ansible-core 2.13.1
    • netbox.netbox コレクション 3.8.0 (これ未満のバージョンにはグループ名の定義がないため機能しません)

netbox.netbox コレクションの各モジュールは、netbox_urlnetbox_token など接続関連のオプションを共通で持っています。netbox_urlnetbox_token オプションは必須なので、通常であれば各タスクに都度指定する必要があります。

module_defaults を利用すると、各タスクに都度指定する必要がなくなります。

以下は、module_defaults を利用したPlaybook例です。タスク側には netbox_urlnetbox_token を指定していません。

---
- hosts: netbox01
  gather_facts: false

  module_defaults:
    group/netbox.netbox.netbox:
      netbox_url: "{{ netbox_url }}"      # Playごとに指定
      netbox_token: "{{ netbox_token }}"  # Playごとに指定

  tasks:
    - name: Create a site
      netbox.netbox.netbox_site:
        data:
          name: Nagoya
        state: present

    - name: Create a device
      netbox.netbox.netbox_device:
        data:
          name: Test Device
          device_type: C9200-48P
          device_role: Access Switch
          site: Nagoya
        state: present

ホスト変数定義ファイル、host_vars/netbox.01.yml には、以下のように netbox_urlnetbox_token 変数を定義しておきます(機密情報は ansible-vault などで暗号化推奨)。

---
ansible_connection: local
netbox_url: https://netbox.example.com
netbox_token: xxxxxxxxxxxxx_dummy_xxxxxxxxxxxx
ansible_python_interpreter: "{{ ansible_playbook_python }}"

以下、Playbook 実行例です。

 % ansible-playbook -i inventory.ini netbox.yml    

PLAY [netbox01] ****************************************************************************************************

TASK [Create a site] ***********************************************************************************************
changed: [netbox01]

TASK [Create a device] *********************************************************************************************
changed: [netbox01]

PLAY RECAP *********************************************************************************************************
netbox01                   : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

無事に実行できました。

なお、仮にこのタスクの定義方法で modulue_defautls の定義をしなかった場合は、missing required arguments: netbox_token, netbox_url というエラーになります。今回正常に実行できるのは、netbox_tokennetbox_url オプションは必須のオプションでタスク側には定義がないけど、modulue_defautls で定義してあるから大丈夫ということです。

NetBox 側の結果です。

site Nagoya が作成できた

Test Device が作成できた

(おためしには NetBox のデモサイト https://demo.netbox.dev/ を利用させていただきました。便利です。)

他のコレクションのグループ名

他のコレクションのグループ名の定義も、現時点の最新バージョンのものを少し覗いてみます。

コレクションに含まれる全モジュールをグループ化している傾向にあるようです。

コレクション名前 調査バージョン meta/runtime.yml グループ名(FQCN
awx.awx 21.6.0 meta/runtime.yml awx.awx.controller
community.grafana 1.5.2 meta/runtime.yml community.grafana.grafana
communityhashi_vault 3.2.0 meta/runtime.yml communityhashi_vault.vault

他、もともと Ansible 本体で定義されていたグループは、コレクション側の定義に紐づけるような仕組みになったようです。

補足: module_defaults キーワードを指定できる場所

今回は Play 単位で、module_defaults キーワードを指定しましたが、他にも block や role 、 task 単位でも指定で来ます。

キーワード一覧 Playbook Keywords — Ansible Documentation