エラーの内容
Playbookが次のエラーで突然停止します:
fatal: [host]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'variable_name' is undefined"
}
AnsibleUndefinedVariable: 'variable_name' is undefined
Ansibleが、見つけることができない変数を参照しているJinja2テンプレートに遭遇しました。変数がまったく定義されていないか、スコープが間違っているか、または名前にタイポがある可能性があります。
原因
- 変数がどこにも定義されていない —
vars:、defaults/main.yml、インベントリ、-eフラグのいずれにもない - スコープの誤り:ブロック内や、このタスクに適用されないホスト固有の変数として定義されている
- タイポ(
db_portvsdb_ports— 見落としやすい) - ネストされたキーが存在しない(
resultにoutputキーがないのにresult.outputにアクセスしている) registerした変数を、そのタスクが実行される前、またはスキップされた後に使用している
手順ごとの修正方法
ステップ1 — 変数が定義されるべき場所を探す
-vオプションを使ってエラー出力の詳細を取得します:
ansible-playbook site.yml -v
次に、プロジェクト全体で変数を検索します:
grep -r "variable_name" roles/ group_vars/ host_vars/ inventory/
結果が出ない場合、答えは明白です — どこにも定義されていません。
ステップ2 — 適切な場所に変数を定義する
変数をどの範囲で適用するかに応じて、定義場所を選択します:
Playbookに直接記述(タスクレベルのスコープ):
- name: アプリをデプロイ
hosts: webservers
vars:
app_port: 8080
app_user: deploy
tasks:
- name: アプリを起動
ansible.builtin.systemd:
name: myapp
environment:
PORT: "{{ app_port }}"
group_varsに記述(そのグループのすべてのホストに適用):
# group_vars/webservers.yml
app_port: 8080
app_user: deploy
ロールのdefaultsに記述(最低優先度 — 他の設定で上書き可能):
# roles/myapp/defaults/main.yml
app_port: 8080
app_user: deploy
実行時に-eで指定(最高優先度 — すべてを上書き):
ansible-playbook site.yml -e "app_port=8080 app_user=deploy"
ステップ3 — デフォルト値でオプション変数を処理する
すべての変数を必須にする必要はありません。Jinja2のdefault()フィルターを使ってフォールバック値を指定し、Playbookがクラッシュするのを防ぎましょう:
- name: タイムアウトを設定
ansible.builtin.template:
src: config.j2
dest: /etc/myapp/config.conf
vars:
timeout: "{{ connection_timeout | default(30) }}"
debug_mode: "{{ enable_debug | default(false) }}"
値が設定されていない場合にパラメーター自体を完全にスキップしたい場合は、default(omit)を使用します:
- name: ユーザーを作成
ansible.builtin.user:
name: "{{ username }}"
password: "{{ user_password | default(omit) }}"
ステップ4 — ネストされた変数アクセスを修正する
辞書内のキーにアクセスする際、そのキーが存在しない場合があります。ガードを追加しましょう:
# 危険 — resultにstdoutキーがない場合に失敗する
- debug:
msg: "{{ result.stdout }}"
# 安全 — stdoutが存在しない場合は空文字列を返す
- debug:
msg: "{{ result.stdout | default('') }}"
# または最初に存在確認をする
- debug:
msg: "{{ result.stdout }}"
when: result.stdout is defined
ステップ5 — スキップされたタスクの登録変数を修正する
よくある落とし穴:タスクがスキップされると、登録されるはずだった変数が存在しなくなり、後続のタスクがそれを使おうとします。
# 問題:このタスクがスキップされると、check_resultは未定義になる
- name: サービスを確認
ansible.builtin.command: systemctl status myapp
register: check_result
when: run_checks | default(false)
# 修正:使用箇所をガードする
- name: 結果を表示
debug:
msg: "{{ check_result.stdout }}"
when: check_result is defined and check_result.stdout is defined
修正の確認
Playbookを再実行します。タスクがFAILEDにならずに成功するはずです。実際に実行する前に、Ansibleが変数を認識できているか確認しましょう:
# グループ内のすべてのホストに対して特定の変数を確認する
ansible -m debug -a "var=app_port" webservers
# 特定のホストのすべての変数をダンプする
ansible -m debug -a "var=hostvars[inventory_hostname]" webservers --limit web01
ドライランで変更を加えずに問題を検出します:
ansible-playbook site.yml --check --diff
クイックヒント
- 変数の優先順位:Ansibleには22段階の優先順位があります。予期しない値が来る場合は、優先順位の一覧を確認してください。Extra vars(
-e)が常に最優先;ロールのdefaultsが常に最低優先です。 - 厳格モード:
ansible.cfgにerror_on_undefined_vars = Trueを設定すると、未定義変数をより早い段階で検出できます — サイレントに誤った出力を生成する前に、明確なエラーとして捕捉できます。 - プレイ途中ですべての変数をダンプ:一時的なデバッグタスクを追加して、プレイのその時点で何が使用可能かを正確に確認できます:
- name: すべての変数をデバッグ debug: var: vars
- **`when:`でのベア変数**:`when: my_var`は`my_var`が未定義の場合にクラッシュします。代わりに`when: my_var is defined and my_var`と記述してください。

