Ansible の変数の受け渡し

レッドハットの杉村です。Ansible のテクニカルサポートをしています。毎月1つは何か書くことを目標にしています。3月分は3月38日(4月7日ですが)になってしまいましたが、4月は何とか滑り込みできました。

前回は変数の基本的なところを紹介しました。

rheb.hatenablog.com

今回はその変数の受け渡しについて紹介します。変数をプレイブックの外から与えられるようにすることで、より再利用性の高い自動化が実現できるようになります。

タスク間でやりとりする変数

task1の処理結果をtask2で使いたいというときにどう渡せるかを見ていきます。

タスクごとに定義できる register を使って、そのタスクの処理結果を変数に格納することができます。インデントに注意してください。name:command: (モジュール定義) と並列に並べます。

---
- hosts: all
  gather_facts: true

  tasks:
    - name: task1
      command:
        cmd: "hostnamectl status"
      register: result

    - name: task2
      debug:
        msg: "{{ result }}"

実行結果はこのようになります。

$ ansible-playbook tasks.yml -i 192.168.0.xxx, -k 
SSH password: 

PLAY [all] ************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [192.168.0.xxx]

TASK [task1] **********************************************************************************************************************************************************************
changed: [192.168.0.xxx]

TASK [task2] **********************************************************************************************************************************************************************
ok: [192.168.0.xxx] => {
    "msg": {
        "changed": true,
        "cmd": [
            "hostnamectl",
            "status"
        ],
        "delta": "0:00:00.148812",
        "end": "2022-04-20 13:53:49.593813",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2022-04-20 13:53:49.445001",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "   Static hostname: xxxxxxxx\n         Icon name: computer-vm\n           Chassis: vm\n        Machine ID: 428067dxxxxxxxxxxxxxxxxxxxxxx\n           Boot ID: 5a15d0a8xxxxxxxxxxxxxxxxxxxx\n    Virtualization: vmware\n  Operating System: Red Hat Enterprise Linux 8.5 (Ootpa)\n       CPE OS Name: cpe:/o:redhat:enterprise_linux:8::baseos\n            Kernel: Linux 4.18.0-348.el8.x86_64\n      Architecture: x86-64",
        "stdout_lines": [
            "   Static hostname: xxxxxxxx",
            "         Icon name: computer-vm",
            "           Chassis: vm",
            "        Machine ID: 428067dxxxxxxxxxxxxxxxxxxxxxx",
            "           Boot ID: 5a15d0a8xxxxxxxxxxxxxxxxxxxx",
            "    Virtualization: vmware",
            "  Operating System: Red Hat Enterprise Linux 8.5 (Ootpa)",
            "       CPE OS Name: cpe:/o:redhat:enterprise_linux:8::baseos",
            "            Kernel: Linux 4.18.0-348.el8.x86_64",
            "      Architecture: x86-64"
        ]
    }
}

PLAY RECAP ************************************************************************************************************************************************************************
192.168.0.xxx              : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

コマンドラインから渡す変数

前回は変数をプレイブックの中に定義したり、外のファイルに書いて vars_files: で指定したりという方法で利用するものを紹介しました。

コマンドラインから渡すこともできます。

---
- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: debug
      debug:
        msg: "Good morning {{ message }} !"

例えばこのようなプレイブックでは、変数 message が定義されていないときにはこのように fatal エラーになります。

$ ansible-playbook extra.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

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

TASK [debug] **********************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'message' is undefined\n\nThe error appears to be in '/home/sugimura/blog/extra.yml': line 7, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n  tasks:\n    - name: debug\n      ^ here\n"}

PLAY RECAP ************************************************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

--extra-vars オプションを使って、変数定義を渡すことができます。短縮形 -e も使えます。

$ ansible-playbook extra.yml -e message=ansible
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

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

TASK [debug] **********************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "Good morning ansible !"
}

PLAY RECAP ************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

このように変数が複数あるときや、ディクショナリやリストを渡したいときを見てみます。

---
- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: debug
      debug:
        msg: "Good morning {{ item }} !"
      loop: "{{ messagelist }}"

この message にリストを渡したいとすると、コマンドラインから [ ] の形で渡す場合はこのようになります。

$ ansible-playbook extra.yml -e messagelist="['ansible','automation','platform']"
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

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

TASK [debug] **********************************************************************************************************************************************************************
ok: [localhost] => (item=ansible) => {
    "msg": "Good morning ansible !"
}
ok: [localhost] => (item=automation) => {
    "msg": "Good morning automation !"
}
ok: [localhost] => (item=platform) => {
    "msg": "Good morning platform !"
}

PLAY RECAP ************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

extra_vars はファイルで渡すこともできます。ファイルに YAML でこのように書いて、@ファイル名 で渡します。もしプレイブックの中に変数がたくさんあるような場合には、この方法で渡すのが楽だと思います。将来的にロールにまとめるときも、この方式を覚えておくと便利です。

---
messagelist:
  - "ansible"
  - "automation"
  - "platform"
$ ansible-playbook extra.yml -e @parameterlist.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

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

TASK [debug] **********************************************************************************************************************************************************************
ok: [localhost] => (item=ansible) => {
    "msg": "Good morning ansible !"
}
ok: [localhost] => (item=automation) => {
    "msg": "Good morning automation !"
}
ok: [localhost] => (item=platform) => {
    "msg": "Good morning platform !"
}

PLAY RECAP ************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

普段使うものでは VMware vCenter で VM を作るときに VM 名や OS テンプレートを引数で渡すときにこのようなやり方をしています。

ホスト変数とグループ変数

変数定義は上の例のようにプレイブックに一括して渡すというものもあれば、インベントリに定義するホストやグループにそれぞれ決めて渡したいということもあるかと思います。

例えばインベントリをこのように用意したとします。

[RHEL]
192.168.0.100
192.168.0.120

[Windows]
192.168.0.200

それぞれの対象について変数を定義したいことがあります。接続方式や認証情報もありますし、プレイブックを具体的に書き進めたときにはその使っているモジュールに対する変数定義も書きたいことがあります。

まずはインベントリファイルに定義することができます。これはグループに変数を定義した例です。YAML 形式に慣れていると、たまにインベントリを ini 形式で書いたときに : だったか = だったかがごちゃごちゃになりますので注意してください。

[RHEL]
192.168.0.100
192.168.0.120

[Windows]
192.168.0.200

[RHEL:vars]
ansible_connection=ssh
ansible_user=ansibleuser
ansible_ssh_private_key_file=~/.ssh/privatekey.pem

[Windows:vars]
ansible_connection=winrm
ansible_winrm_transport=basic
ansible_winrm_server_cert_validation=ignore
ansible_user=ansibleuser
ansible_password=XXXXXXXX

ホストに変数を定義することもできます。

[RHEL]
192.168.0.100 ansible_user=sugimura
192.168.0.120 ansible_user=ansible

グループとホストの両方に定義を書いたときは、ホストに書いた方で上書きされます。実際にどう反映されているかは、ansible-inventory コマンドで知ることができます。

[RHEL]
192.168.0.100 ansible_user=sugimura ansible_ssh_private_key_file=~/.ssh/sugimura.pem
192.168.0.120 ansible_user=ansible ansible_ssh_private_key_file=~/.ssh/ansible.pem

[Windows]
192.168.0.200

[RHEL:vars]
ansible_connection=ssh
ansible_user=ansibleuser
ansible_ssh_private_key_file=~/.ssh/privatekey.pem

[Windows:vars]
ansible_connection=winrm
ansible_winrm_transport=basic
ansible_winrm_server_cert_validation=ignore
ansible_user=ansibleuser
ansible_password=XXXXXXXX

このようにインベントリファイルを書いたとしたとき、ansible-inventory --list で確認することができます。

$ ansible-inventory -i inventory --list
{
    "RHEL": {
        "hosts": [
            "192.168.0.100",
            "192.168.0.120"
        ]
    },
    "Windows": {
        "hosts": [
            "192.168.0.200"
        ]
    },
    "_meta": {
        "hostvars": {
            "192.168.0.100": {
                "ansible_connection": "ssh",
                "ansible_ssh_private_key_file": "~/.ssh/sugimura.pem",
                "ansible_user": "sugimura"
            },
            "192.168.0.120": {
                "ansible_connection": "ssh",
                "ansible_ssh_private_key_file": "~/.ssh/ansible.pem",
                "ansible_user": "ansible"
            },
            "192.168.0.200": {
                "ansible_connection": "winrm",
                "ansible_password": "XXXXXXXX",
                "ansible_user": "ansibleuser",
                "ansible_winrm_server_cert_validation": "ignore",
                "ansible_winrm_transport": "basic"
            }
        }
    },
    "all": {
        "children": [
            "RHEL",
            "Windows",
            "ungrouped"
        ]
    }
}

インベントリファイルに書くだけでなく、命名規則に従ったディレクトリ構成を作ることで外のファイルに書いて読ませることもできます。このようにホストやグループの定義と変数定義を完全に分割することで、変更に強くすることができることもあります。ansible-inventory -i inventory --list で得られる結果は同じですので確認してみてください。インベントリファイルは ini 形式のままにしていますが、外出しした変数定義は YAML にしてみました。読める形式であれば自動的に判別しますので好きな形式で書いてください。

$ cat inventory
[RHEL]
192.168.0.100
192.168.0.120

[Windows]
192.168.0.200

$ cat group_vars/RHEL.yml 
---
ansible_connection: ssh
ansible_user: ansibleuser
ansible_ssh_private_key_file: ~/.ssh/privatekey.pem

$ cat group_vars/Windows.yml 
---
ansible_connection: winrm
ansible_winrm_transport: basic
ansible_winrm_server_cert_validation: ignore
ansible_user: ansibleuser
ansible_password: XXXXXXXX

$ cat host_vars/192.168.0.100.yml 
---
ansible_user: sugimura
ansible_ssh_private_key_file: ~/.ssh/sugimura.pem

$ cat host_vars/192.168.0.120.yml 
---
ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/ansible.pem

もっと量が多くなってきたときには、group_vars などのディレクトリの下にさらにグループ名のディレクトリを作って読ませることもできます。詳しくはドキュメントをご覧ください。

docs.ansible.com

Ansible automation controller では、Web UI から定義することができます。定義した変数はデータベースの中に保存されます。このとき、認証情報は別に定義して実行の時に差し込む形にすることをお勧めします。

終わりに

Ansible での変数の扱いについて2回に分けて紹介してみました。4月ということでこれから Ansible を触っていただける方もいらっしゃると思いますし、何か役に立つところがありましたらとてもうれしいです。

この変数について書き始めたきっかけはこの先にありました。Ansible automation controller でプレイブックを実行するときにはジョブテンプレートやワークフロージョブテンプレートを使うのですが、そのジョブの間での受け渡しについて次回紹介しようと考えています。

Ansible Automation Platform の試用版はこちらをご覧ください。

www.redhat.com

Happy Automation!

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。