てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] FQCN でモジュールを指定しないと困るケース

はじめに

多くのモジュールがコレクションに移行してから、モジュール名は namespace.collection.module_name のようにどのコレクションのモジュールなのかを指定ができるようになりました。ここではこの指定方法を FQCN と呼びます。

Ansible本体 (ansible-core / ansible-base) に組み込まれているモジュールは ansible.builtin というコレクションの扱いです。たとえばユーザー管理のモジュールは ansible.buitlin.user という指定になります。

基本的に FCQN でモジュール名を指定するのが良いと思っていますが、モジュール名のみを指定した場合でもリダイレクト等の仕組みによって、問題なく利用できることも多々あります。

一方で、FQCN でモジュールを指定しないと困るケースもあります。 この記事では、ansible.builtin 内のモジュールの例で再現してみます。

  • 検証環境
    • Ansible 7.6.0

困るケース

困りがちなのは以下のようなケースです。(だいぶわざとらしいですが)

---
- name: Manage Users
  hosts: web01
  gather_facts: false

  collections:
    - awx.awx

  tasks:
    - name: Create a user
      user:
        name: sakana
        groups:
          - admin
        append: true
    # 略

一見、ansible.buitlin.user モジュールを実行してくれそうですが、Playbook を実行すると以下のエラーになります。

TASK [Create a user] **************************************************************************************************************************************************
fatal: [web01]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": false, "msg": "missing required arguments: username"}

"missing required arguments: username" というエラーで、username という必須のパラメーターが指定されてないよ、と言われています。ところが ansible.buitlin.user モジュールのドキュメントには username パラメーターはありません。

これは、Playbook 内の collections キーワードで指定した awx.awx によって、users モジュールは awx.awx.user モジュールと解釈されたためです。そして、awx.awx.user モジュールでは usersname パラメーターが必須であるのに対して、Playbook では指定されていないため、前述のエラーが発生します。

このように、モジュール名のみで見た時にある種の衝突が起き、一意に特定できないことがあるため、困るケースです。

ちなみに、以前は awx.awx.awx_user モジュールのように、モジュール名の _ より前の部分があることによってモジュール名のみでも特定しやすい状態でした。コレクションによる配布が落ち着くにつれて「コレクション名 awx.awx があるからモジュール名には awx はなくてもいいでしょ」となったのか、モジュール名としては単に user となりました。

少し分かりにくい例

前述の例は、Playbook 内に collections キーワードがあったため原因を追いやすいかもしれません。

この collections キーワードは Play の単位だけでなく、さまざまな箇所で定義できます。この仕様により collections の定義に気が付きにくかったり、複数個所で定義された場合にどう適用されるんだっけと戸惑ったりしまいそうです。

たとえばロールにした場合は、以下の例のように meta/main.yml 内に collections を定義できます。

---
collections:
  - awx.awx

この状態で tasks/main.yml に以下のように、モジュール名を単に user と指定すると、awx.awx.user モジュールとして解釈されます。

---
- name: Create user
  user:
    name: sakana
    groups:
      - admin
    append: true

ロールを呼び出す Playbook は以下のように collecttions の定義なしで実行すると・・

---
- name: Manage Users
  hosts: web01
  gather_facts: false

  tasks:
    - name: Import manage_users
      ansible.builtin.import_role:
        name: manage_users

やはり "missing required arguments: username" というエラーになります。

Playboook にも タスクファイルにも collections キーワードがないため、やや気づきにくいかと思います。

トラブルシューティング

こういう事象を目の当たりにすると、モジュール名のみの指定した場合にどのコレクションとして解釈されたかを調べたくなるかもしれません。

いくつか方法はあるかもしれませんが、私が試した範囲では、Playbook 実行時に ansible-playbook コマンドに -vvv を付ける方法があります。チェックモード(-C)でも大丈夫です。

% ansible-playbook -i inventory.yml playbook.yml -vvv -C
...(略)...
TASK [manage_users : Create user] ***************************************************************************************************
..(略)...
Using module file /Users/sakana/.ansible/collections/ansible_collections/awx/awx/plugins/modules/user.py
...(略)...

awx/awx/plugins/modules/user.py というパスから、awx.awx.user モジュールを実行しようとしたことが分かります。

おわりに

FQCN でモジュールを指定しないと困るケースをご紹介しました。

公式ドキュメントにも、FQCN を推奨する旨の説明がありますし、省略しないほうがトラブル防止になって良いかなと思います。

This is one of the reasons we recommend you always use FQCN.