【Ansible】タスクのデバッグが捗るTips

この記事は[NTTコムウェア Advent Calendar 2023](https://qiita.com/advent-calendar/2023/nttcomware) 15日目の記事です。

NTT コムウェアの金子です。
RHEL系のサーバ構築を自動化する業務に従事しています。
本記事ではAnsibleタスクのデバッグ作業時に活用できそうなノウハウを紹介します。

# 1. 筆者の検証環境
筆者の検証環境は次のとおりです。
- Rocky Linux 9.1
- Ansible Core 2.13.3

そのため、本記事の参考文献はAnsible Core 2.13やそれを含んでいるAnsible project 6.0のドキュメントが主であることにご注意ください。 

https://docs.ansible.com/ansible/latest/roadmap/COLLECTIONS_6.html

# 2. 用語
本記事で使用する用語を説明します。
|用語|説明|
|---|---|
|コントロールノード|Ansible CLIツール(`ansible-playbook`、`ansible`など)を実行する端末|
|マネージドノード|Ansibleで管理する対象となるデバイス(サーバやNW機器など)|
|インベントリ|各マネージドノード固有の情報(IPアドレスなど)の定義|

https://docs.ansible.com/ansible/6/network/getting_started/basic_concepts.html

その他、不明な用語がありましたら、コミュニティ提供の用語集を参照してください。

https://docs.ansible.com/ansible/6/reference_appendices/glossary.html

# 3. アドホックコマンド
本章では、デバッグ作業時に活用できそうな`ansible`コマンドの使い方を紹介します。

https://docs.ansible.com/ansible/6/user_guide/intro_adhoc.html

## 3.1. 疎通確認
インベントリを作成したら、まず実施したいのがコントロールノードからマネージドノードへの疎通確認。次のようなコマンドで実行できます。
```
$ ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.ping all
```
https://docs.ansible.com/ansible/6/collections/ansible/builtin/ping_module.html

https://docs.ansible.com/ansible/6/getting_started/get_started_inventory.html

疎通NGの場合は、次のコマンドでインベントリに誤りがないか確認します。
```
$ ansible-inventory -i /tmp/vagrant-ansible/inventory --list
```
https://docs.ansible.com/ansible/6/cli/ansible-inventory.html

## 3.2. 任意のコマンド実行
複数のマネージドノードで任意のコマンド実行したいが、マネージドノードごとに`ssh`コマンドを組み立てるのが面倒...。そんな時は次のように任意のコマンドを実行します。
```
$ ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.command -a 'ip address show' all
```
https://docs.ansible.com/ansible/6/collections/ansible/builtin/command_module.html

`ansible.builtin.command`モジュールはシェルを介して処理されないため、`$HOSTNAME`や`"*"`などの操作は機能しません。これらの機能が必要な場合は`ansible.builtin.shell`モジュールを使用します。
```
$ ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.shell -a 'ip address show eth1 | grep -e "inet\\s"' all
```
https://docs.ansible.com/ansible/6/collections/ansible/builtin/shell_module.html

## 3.3. Gathering Facts 確認
`ansible-playbook`実行時に自動的に実行される`Gathering Facts`タスクで取得される情報をうまく活用できれば、情報取得タスクを独自に実装する手間を減らすことができます。
`Gathering Facts`タスクで収集される情報は`ansible.builtin.setup`モジュールで確認できます。
```
$ ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.setup all
```
出力をフィルタすることもできます。
```
$ ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.setup -a "filter=ansible_local" all
```
https://docs.ansible.com/ansible/6/collections/ansible/builtin/setup_module.html

https://docs.ansible.com/ansible/6/user_guide/playbooks_vars_facts.html

## 3.4. Jinja2の動作確認
Jinja2の動作確認をしたい場合、まずはテンプレートファイルを作成します。
```jinja:test.txt.j2
{%- for user in users -%}
{{ loop.index }}:{{ user }}
{%  endfor -%}
```
次に、テンプレートファイルが参照する変数を定義するYAMLファイルを作成します。
```yaml:test_vars.yml
users:
- Mike
- Smith
- Klara
- Alex
```
`ansible.builtin.template`モジュールを実行します。`--extra-vars`オプションの値には`@<ファイルパス>`の書式で、変数を定義したYAMLファイルのパスを指定します。
```
$ ansible -i "localhost," -c local -m ansible.builtin.template -a "src=test.txt.j2 dest=./test.txt" --extra-vars=@test_vars.yml all
```
`ansible.builtin.template`モジュールによって生成されたファイルの内容に誤りがないか確認します。
```text:test.txt
1:Mike
2:Smith
3:Klara
4:Alex
```
動作確認できたJinja2テンプレートはPlaybookで直接使用しても問題なく動作します。
```
$ ansible-playbook -i "localhost," -c local --extra-vars=@test_vars.yml /dev/stdin <<EOF
- hosts: all
  gather_facts: false
  tasks:
  - ansible.builtin.debug:
      msg: |-
        {%- for user in users -%}
        {{ loop.index }}:{{ user }}
        {%  endfor -%}
EOF

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

TASK [ansible.builtin.debug] *********************************************************************************************************************************************************
ok: [localhost] => {
    "msg": "1:Mike\n2:Smith\n3:Klara\n4:Alex\n"
}

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

https://stackoverflow.com/questions/35407822/how-can-i-test-jinja2-templates-in-ansible#41682944

https://docs.ansible.com/ansible/6/user_guide/playbooks_templating.html

https://stackoverflow.com/questions/54275015/can-ansible-playbook-read-from-stdin-instead-of-a-file/54280101#54280101

## 3.5. 詳細メッセージ
`-v`オプションを付加すれば詳細メッセージを出力させることができます。`v`の数が多いほど詳細なメッセージが出力されます。Ansible Core 2.13 では`-vvvvvv`まで評価されます。
```
$ ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.ping -vvvvvv all
```
https://docs.ansible.com/ansible/6/cli/ansible.html

設定ファイルの`verbose`(`colors`セクション)、もしくは環境変数`ANSIBLE_COLOR_VERBOSE`で詳細メッセージの色を指定できます。(デフォルトは`blue`)

https://docs.ansible.com/ansible/6/reference_appendices/config.html#color-verbose

## 3.6. デバッグメッセージ
設定ファイルの`debug`(`defaults`セクション)、もしくは環境変数`ANSIBLE_DEBUG`に`True`を設定するとデバッグメッセージを出力させることができます。
```
$ ANSIBLE_DEBUG=True ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.command -a 'echo "hello world!"' -vvvv all
```
https://docs.ansible.com/ansible/6/reference_appendices/config.html#default-debug

設定ファイルの`debug`(`colors`セクション)、もしくは環境変数`ANSIBLE_COLOR_DEBUG`でデバッグメッセージの色を指定できます。(デフォルトは`dark gray`)

https://docs.ansible.com/ansible/6/reference_appendices/config.html#color-debug

マネージドノードでモジュールが他のコマンドを呼び出す場合は、`Executing: <command>`というパターンのログが`syslog`出力されます。
このログはモジュールがどのような処理しているのかを理解するのに役立つことがあります。
```:マネージドノードの/var/log/messages
...中略...
Nov 23 09:38:23 localhost ansible-ansible.legacy.command[146377]: Invoked with _raw_params=echo "hello world!" _uses_shell=False warn=False stdin_add_newline=True strip_empty_ends=True argv=None chdir=None executable=None creates=None removes=None stdin=None
Nov 23 09:38:23 localhost ansible-ansible.legacy.command[146377]: Executing: echo 'hello world!'
...中略...
```
`syslog`のファシリティは設定ファイルの`syslog_facility`(`defaults`セクション)、もしくは環境変数`ANSIBLE_SYSLOG_FACILITY`で指定できます。(デフォルトは`LOG_USER`)

https://docs.ansible.com/ansible/6/reference_appendices/config.html#default-syslog-facility

https://docs.ansible.com/ansible/6/dev_guide/developing_program_flow_modules.html#ansible-syslog-facility

https://docs.ansible.com/ansible/6/reference_appendices/logging.html

ちなみに、`Executing: <command>`というデバッグログは`ansible.module_utils.basic.‎AnsibleModule.run_command‎()`で出力しています。
```python:/usr/lib/python3.9/site-packages/ansible/module_utils/basic.py
###中略###
    def run_command(self, args, check_rc=False, close_fds=True, executable=None, data=None, binary_data=False, path_prefix=None, cwd=None,
                    use_unsafe_shell=False, prompt_regex=None, environ_update=None, umask=None, encoding='utf-8', errors='surrogate_or_strict',
                    expand_user_and_vars=True, pass_fds=None, before_communicate_callback=None, ignore_invalid_cwd=True, handle_exceptions=True):
        ###中略###
        try:
            if self._debug:
                self.log('Executing: ' + self._clean_args(args))
            cmd = subprocess.Popen(args, **kwargs)
        ###中略###
###中略###
```
https://github.com/ansible/ansible/blob/v2.13.3/lib/ansible/module_utils/basic.py#L2021

## 3.7. モジュールのデバッグ
モジュールの挙動がおかしい時はモジュールのデバッグが必要になります。
Ansible(コントロールノード)はモジュールのPythonコードをマネージドノードに転送し、マネージドノードでモジュールを実行します。デフォルトの動作では、実行完了後に転送したモジュールは削除されますが、設定ファイルの`keep_remote_files`(`defaults`セクション)、もしくは環境変数`ANSIBLE_KEEP_REMOTE_FILES`に`True`を設定すれば、転送したモジュールの削除は実行されません。

https://docs.ansible.com/ansible/6/reference_appendices/config.html#default-keep-remote-files

デバッグのために`ANSIBLE_KEEP_REMOTE_FILES`に`True`をして、`-vvv`オプション付きで`ansible`を実行します。
```
$ ANSIBLE_KEEP_REMOTE_FILES=True ansible -i /tmp/vagrant-ansible/inventory -m ansible.builtin.ping -a 'data=debugging_session' -vvv frontend01
...中略...
<172.19.0.11> PUT /home/vagrant/.ansible/tmp/ansible-local-14418s6swk43r/tmpq7stzauc TO /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/AnsiballZ_ping.py
<172.19.0.11> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o 'IdentityFile="/vagrant/.vagrant/machines/frontend01/qemu/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="vagrant"' -o ConnectTimeout=10 -o 'ControlPath="/home/vagrant/.ansible/cp/92e5a36ba9"' '[172.19.0.11]'
<172.19.0.11> (0, b'sftp> put /home/vagrant/.ansible/tmp/ansible-local-14418s6swk43r/tmpq7stzauc /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/AnsiballZ_ping.py\n', b'')
...中略...
<172.19.0.11> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o 'IdentityFile="/vagrant/.vagrant/machines/frontend01/qemu/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="vagrant"' -o ConnectTimeout=10 -o 'ControlPath="/home/vagrant/.ansible/cp/92e5a36ba9"' -tt 172.19.0.11 '/bin/sh -c '"'"'/usr/bin/python3 /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/AnsiballZ_ping.py && sleep 0'"'"''
<172.19.0.11> (0, b'\r\n{"ping": "debugging_session", "invocation": {"module_args": {"data": "debugging_session"}}}\r\n', b'Shared connection to 172.19.0.11 closed.\r\n')
frontend01 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "invocation": {
        "module_args": {
            "data": "debugging_session"
        }
    },
    "ping": "debugging_session"
}
...中略...
```
上記の例の場合、`sftp`でラッパースクリプトが、`AnsiballZ_ping.py`という名前でマネージドノード`172.19.0.11`に転送されたことがわかります。
マネージドノードにログインして、次のようにラッパースクリプト内のモジュールを実行することができます。
```
$ ssh -i /vagrant/.vagrant/machines/frontend01/qemu/private_key vagrant@172.19.0.11
$ /usr/bin/python3 /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/AnsiballZ_ping.py
```
モジュールはzipファイルをBase64でエンコードした文字列としてラッパースクリプト`AnsiballZ_ping.py`の中に埋め込まれています。
```python:/home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/AnsiballZ_ping.py
###中略###
def _ansiballz_main():
    ###中略###
    ZIPDATA = """UEsDBBQAAAAIAAEDd1cF...中略...LBQYAAAAAIQAhAMEKAACBWwEAAAA="""
    ###中略###
###中略###
```
https://docs.ansible.com/ansible/6/dev_guide/developing_program_flow_modules.html#ansiballz-framework


これを展開するにはラッパースクリプトの引数に`explode`を与えます。
```
$ /usr/bin/python3 /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/AnsiballZ_ping.py explode
Module expanded into:
/home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/debug_dir
```
出力結果には展開されたモジュールを格納するディレクトリが示されています。
展開されたモジュールは適宜編集してください。
モジュールに与えるパラメータ値を変更するには次のファイルを編集します。
```
$ vi /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/debug_dir/args
```
デフォルトの`args`ファイルには`ansible`コマンド実行時のパラメータが記述されています。
```
$ /usr/bin/python3 -m json.tool /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/debug_dir/args 
{
    "ANSIBLE_MODULE_ARGS": {
        "data": "debugging_session",
        "_ansible_check_mode": false,
        "_ansible_no_log": false,
        "_ansible_debug": false,
        "_ansible_diff": false,
        "_ansible_verbosity": 3,
        "_ansible_version": "2.13.3",
        "_ansible_module_name": "ansible.builtin.ping",
        "_ansible_syslog_facility": "LOG_USER",
        "_ansible_selinux_special_fs": [
            "fuse",
            "nfs",
            "vboxsf",
            "ramfs",
            "9p",
            "vfat"
        ],
        "_ansible_string_conversion_action": "warn",
        "_ansible_socket": null,
        "_ansible_shell_executable": "/bin/sh",
        "_ansible_keep_remote_files": true,
        "_ansible_tmpdir": "/home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/",
        "_ansible_remote_tmp": "~/.ansible/tmp"
    }
}
```
編集後のモジュールを実行するにはラッパースクリプトの引数に`execute`を与えます。
```
$ /usr/bin/python3 /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/AnsiballZ_ping.py execute
```
https://docs.ansible.com/ansible/6/dev_guide/debugging.html

https://rheb.hatenablog.com/entry/ansible_ssh_details

### 3.7.1. モジュールデバッグ後のクリーンアップ
設定ファイルの`keep_remote_files`(`defaults`セクション)、もしくは環境変数`ANSIBLE_KEEP_REMOTE_FILES`に`True`を設定したことによって、マネージドノードに残ったPythonコードは自動では消えないので、デバッグが終わったら消しておきましょう。
```
$ rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1700666643.417449-14421-71476177553771/
```

# 4. Ansible Playbook
本章では、デバッグ作業時に活用できそうな`ansible-playbook`コマンドの使い方を紹介します。

https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html

## 4.1. デバッガ
設定ファイルの`enable_task_debugger`(`defaults`セクション)、もしくは環境変数`ANSIBLE_ENABLE_TASK_DEBUGGER`を`True`に設定すると失敗したタスクでデバッガを呼び出すことができます。

https://docs.ansible.com/ansible/6/reference_appendices/config.html#enable-task-debugger

例として、次のように必ず失敗するPlaybookを作成します。
```yaml:playbook/fail.yml
- hosts: all

  tasks:
  - name: always failed
    ansible.builtin.fail:
```
環境変数`ANSIBLE_ENABLE_TASK_DEBUGGER=True`を設定して、`ansible-playbook`を実行すると失敗したタスクでデバッガが呼び出されます。
```
$ ANSIBLE_ENABLE_TASK_DEBUGGER=True ansible-playbook -i /tmp/vagrant-ansible/inventory playbook/fail.yml 

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

TASK [Gathering Facts] ***************************************************************************************************************************************************************
ok: [ansible_machine]
ok: [frontend02]
ok: [frontend01]

TASK [always failed] *****************************************************************************************************************************************************************
fatal: [ansible_machine]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
[ansible_machine] TASK: always failed (debug)> 
```
デバッグプロンプトでは次のコマンドが使用できます。
|コマンド|省略形|説明|
|---|---|---|
|`pprint task/task.args/task_vars/host/result`|`p`|タスクについての情報を出力する。<br>`task`、`task.args`、`task_vars`、`host`、`result`のいずれかを指定する。|
|`task.args[key] = value`||モジュールに渡すパラメータ値を更新する。|
|`task_vars[key] = value`||タスク変数を更新する。<br>(タスクへの反映には`update_task`を実行しなければならない。)|
|`update_task`|`u`|タスク変数を更新してタスクを再作成する。|
|`redo`|`r`|タスクを再実行する。|
|`continue`|`c`|次のタスクを開始して実行を継続する。|
|`help`|`h`|ヘルプを表示する。|

各デバッグコマンドの実行例はコミュニティのドキュメントに記載されているので、そちらを参照してください。

https://docs.ansible.com/ansible/6/user_guide/playbooks_debugger.html

## 4.2. プリントデバッグ
プリントデバッグしたい時は`ansible.builtin.debug`モジュールを使用します。
例として、`ansible.builtin.shell`モジュールの実行結果を出力するPlaybookを作成します。
```yaml:playbook/debug.yml
- hosts: all

  tasks:
  - name: Get uptime information
    ansible.builtin.shell: /usr/bin/uptime
    register: result

  - name: Print return information from the previous task
    ansible.builtin.debug:
      var: result
```
次に`ansible-playbook`の実行ログを示します。
`ansible.builtin.debug`モジュールがタスク変数`result`の内容を出力していることが確認できます。
```
$ ansible-playbook -i /tmp/vagrant-ansible/inventory playbook/debug.yml 

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

TASK [Gathering Facts] ***************************************************************************************************************************************************************
ok: [ansible_machine]
ok: [frontend02]
ok: [frontend01]

TASK [Get uptime information] ********************************************************************************************************************************************************
changed: [ansible_machine]
changed: [frontend01]
changed: [frontend02]

TASK [Print return information from the previous task] *******************************************************************************************************************************
ok: [ansible_machine] => {
    "result": {
        "changed": true,
        "cmd": "/usr/bin/uptime",
        "delta": "0:00:00.003631",
        "end": "2023-11-23 14:17:11.365953",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2023-11-23 14:17:11.362322",
        "stderr": "",
        "stderr_lines": [],
        "stdout": " 14:17:11 up 18:35,  2 users,  load average: 0.01, 0.03, 0.00",
        "stdout_lines": [
            " 14:17:11 up 18:35,  2 users,  load average: 0.01, 0.03, 0.00"
        ]
    }
}
ok: [frontend01] => {
    "result": {
        "changed": true,
        "cmd": "/usr/bin/uptime",
        "delta": "0:00:00.003483",
        "end": "2023-11-23 14:18:03.999522",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2023-11-23 14:18:03.996039",
        "stderr": "",
        "stderr_lines": [],
        "stdout": " 14:18:03 up 18:35,  1 user,  load average: 0.09, 0.21, 0.18",
        "stdout_lines": [
            " 14:18:03 up 18:35,  1 user,  load average: 0.09, 0.21, 0.18"
        ]
    }
}
ok: [frontend02] => {
    "result": {
        "changed": true,
        "cmd": "/usr/bin/uptime",
        "delta": "0:00:00.006027",
        "end": "2023-11-23 14:17:35.446164",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2023-11-23 14:17:35.440137",
        "stderr": "",
        "stderr_lines": [],
        "stdout": " 14:17:35 up 18:35,  0 users,  load average: 0.14, 0.17, 0.16",
        "stdout_lines": [
            " 14:17:35 up 18:35,  0 users,  load average: 0.14, 0.17, 0.16"
        ]
    }
}

PLAY RECAP ***************************************************************************************************************************************************************************
ansible_machine            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
frontend01                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
frontend02                 : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```

https://docs.ansible.com/ansible/6/collections/ansible/builtin/debug_module.html

## 4.3. 途中から開始
Playbookが失敗してしまった場合、失敗したタスクからPlaybookを実行したくなることがあります。そんな時は`--start-at-task`オプションを使用します。
```
$ ansible-playbook -i /tmp/vagrant-ansible/inventory --start-at-task="Print return information from the previous task" playbook/debug.yml
```
上記の例では`Print return information from the previous task`という名前のタスクからPlaybookを実行しています。
この機能には次の注意点があります。
- `include_*`で読み込まれるタスクに対しては利用できない。
- `--start-at-task`で指定されたタスクより前に実行されるタスクによって動的に設定される変数は未設定の状態になる。

https://docs.ansible.com/ansible/6/user_guide/playbooks_startnstep.html#start-at-task

## 4.4. ステップ実行
Playbookが期待通りに動かない時、タスクを一つずつ実行したくなることがあります。そんな時は`--step`オプションを使用します。
```
$ ansible-playbook -i /tmp/vagrant-ansible/inventory --step playbook/debug.yml
```
`--step`オプションを使用すると各タスクで実行するかを尋ねられます。
```
Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: 
```
選択肢は次の通りです。
|選択肢|説明|
|---|---|
|`n`|タスクをスキップする。(デフォルト)|
|`y`|タスクを実行する。|
|`c`|残りのタスクを尋ねることなく実行する。|

https://docs.ansible.com/ansible/6/user_guide/playbooks_startnstep.html#step-mode

## 4.5. タグ
大きなPlaybookを扱う場合、特定のタスクのみを実行して、効率よくデバッグしたくなることがあります。そんな時には`--tags`オプションを使用します。
例として、各タスクにタグを追加したPlaybookを作成します。
```yaml:playbook/tags.yml
- hosts: all

  tasks:
  - name: print tag1
    ansible.builtin.debug:
      msg: print tag1
    tags:
    - tag1

  - name: print tag2
    ansible.builtin.debug:
      msg: print tag2
    tags:
    - tag2

  - name: print tag3
    ansible.builtin.debug:
      msg: print tag3
    tags:
    - tag3
```
`--tags`オプションでタグを指定すると、指定タグにマッチするタグが付いているタスクのみが実行されます。
```
$ ansible-playbook -i /tmp/vagrant-ansible/inventory --tags tag2 playbook/tags.yml 

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

TASK [Gathering Facts] ***************************************************************************************************************************************************************
ok: [ansible_machine]
ok: [frontend01]
ok: [frontend02]

TASK [print tag2] ********************************************************************************************************************************************************************
ok: [ansible_machine] => {
    "msg": "print tag2"
}
ok: [frontend01] => {
    "msg": "print tag2"
}
ok: [frontend02] => {
    "msg": "print tag2"
}

PLAY RECAP ***************************************************************************************************************************************************************************
ansible_machine            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
frontend01                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
frontend02                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```
逆に指定タグにマッチするタグが付いているタスクをスキップしたい場合は`--skip-tags`オプションを使用します。
```
$ ansible-playbook -i /tmp/vagrant-ansible/inventory --skip-tags tag2 playbook/tags.yml

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

TASK [Gathering Facts] ***************************************************************************************************************************************************************
ok: [ansible_machine]
ok: [frontend01]
ok: [frontend02]

TASK [print tag1] ********************************************************************************************************************************************************************
ok: [ansible_machine] => {
    "msg": "print tag1"
}
ok: [frontend01] => {
    "msg": "print tag1"
}
ok: [frontend02] => {
    "msg": "print tag1"
}

TASK [print tag3] ********************************************************************************************************************************************************************
ok: [ansible_machine] => {
    "msg": "print tag3"
}
ok: [frontend01] => {
    "msg": "print tag3"
}
ok: [frontend02] => {
    "msg": "print tag3"
}

PLAY RECAP ***************************************************************************************************************************************************************************
ansible_machine            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
frontend01                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
frontend02                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

```

https://docs.ansible.com/ansible/6/user_guide/playbooks_tags.html

## 4.6. 詳細メッセージ
[前述の`ansible`コマンドと同様](#35-詳細メッセージ)のため割愛します。

## 4.7. デバッグメッセージ
[前述の`ansible`コマンドと同様](#36-デバッグメッセージ)のため割愛します。

## 4.8. モジュールのデバッグ
[前述の`ansible`コマンドと同様](#37-モジュールのデバッグ)のため割愛します。

# 5. Ansible Console
本章では、デバッグ作業時に活用できそうな`ansible-console`コマンドの使い方を紹介します。
例えば、次のように`ansible-console`を起動します。
```
$ ansible-console -i "localhost," -c local
```
https://docs.ansible.com/ansible/6/cli/ansible-console.html

プロンプトが表示されたら、アドホックにタスクを実行することができます。
`help`コマンドで実行可能なコマンドを確認できます。
```
vagrant@all (1)[f:20]$ help
```
## 5.1. フィルタの動作確認
フィルタの動作を確認したい時は`ansible-console`が便利です。
フィルタの動作確認には`debug`コマンドを使用します。
```
vagrant@all (1)[f:20]$ help debug
Print statements during execution
Parameters:
  msg The customized message that is printed. If omitted, prints a generic message.
  var A variable name to debug.
  verbosity A number that controls when the debug is run, if you set to 3 it will only run debug when -vvv or above.
```

例えば、次のようにフィルタの動作を確認できます。
```
vagrant@all (1)[f:20]$ debug msg="{{ ['--option1', '--option2', '--option3'] | zip(['value1', 'value2', 'value3']) | map('join', '=') | join(' ') }}"
localhost | SUCCESS => {
    "msg": "--option1=value1 --option2=value2 --option3=value3"
}
```

https://docs.ansible.com/ansible/6/user_guide/playbooks_filters.html

# 6. まとめ
Ansibleタスクのデバッグ作業に活用できそうなノウハウを紹介しました。
うまく動かないときはその部分を小さく切り出して動作確認してみると、デバッグ作業が早くなると思います。Playbookを一生懸命に作っていると、アドホックコマンド(`ansible`)やAnsible Console(`ansible-console`)は存在を忘れてしまいがちですが、うまく活用して品質の高いPlaybookを作成したいものですね。

---
記載されている会社名、製品名、サービス名は、各社の商標または登録商標です。