こんな状況に直面したことはないでしょうか。Ansibleは
commandモジュールやshellモジュールを使う場合、終了コードが0なら「OK(成功)」、0以外なら「failed(失敗)」と判断しますが、「変更があったか否か」についてはPlaybook側で明示的に定義しない限り、常に「changed」と返します。この記事では、failed_when・changed_when・ignore_errorsの3つのキーワードを使い、タスクの成否と変更状態をPlaybook側から精密に制御する方法を解説します。RHEL 9.4 / Ubuntu 24.04 LTS の実環境で動作確認した出力例を交えながら、現場で即使えるパターンを紹介します。
この記事のポイント
・failed_whenで「終了コード以外の失敗条件」をPlaybook側で定義できる
・changed_whenでcommandの「変更有無」の判定ロジックを上書きする
・ignore_errorsはエラー後も処理を継続するが、乱用は要注意
・registerとセットで使い、stdout/rcを条件式に組み込む設計が基本
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
AnsibleがタスクをOK・changed・failedに分類する仕組み
Ansibleがタスクを実行すると、結果は次の4種類のうちいずれかで報告されます。・ok:タスクが成功し、変更なし
・changed:タスクが成功し、何かが変更された
・failed:タスクが失敗した
・skipped:whenなどの条件によってスキップされた
fileモジュールやdnfモジュールのように、Ansible組み込みのモジュールを使う場合は、このOK/changed/failedの判定がモジュール内部に実装されています。たとえばファイルを作成するとき、すでに同名のファイルが存在すれば「ok」、新規作成なら「changed」と適切に判定してくれます。問題が起きやすいのは
commandモジュールやshellモジュールを使うときです。これらは汎用コマンド実行モジュールであり、実行したコマンドが何かを変更したかどうかをAnsibleは知る術がありません。そのため、デフォルトでは「終了コード(rc)が0なら成功、0以外なら失敗」「成功した場合は常にchanged」という単純なルールが適用されます。この挙動を変えるのが、failed_whenとchanged_whenです。
failed_whenでタスクの失敗条件を自分で定義する
failed_whenを使うと、「どのような状態を失敗とみなすか」をJinja2の条件式で定義できます。1. 基本構文とregisterの組み合わせ
failed_whenは単体では動きません。まずregisterでタスクの実行結果を変数に格納し、その変数の値をfailed_whenの条件式で参照します。# サービスの稼働状態を確認するタスク例 - name: nginxの稼働状態を確認する command: systemctl is-active nginx register: nginx_status failed_when: nginx_status.rc not in [0, 3]
register: nginx_statusで実行結果を変数nginx_statusに格納します。この変数には以下のキーが含まれます。・rc:コマンドの終了コード(Return Code)
・stdout:標準出力の内容(文字列)
・stderr:標準エラーの内容(文字列)
・stdout_lines:標準出力を行ごとに分割したリスト
上の例では、終了コードが0(active)か3(inactive)以外の場合に失敗とみなします。
systemctl is-activeはサービスが存在しない場合に終了コード4を返すため、そのケースのみを失敗として検出する設計になっています。2. 出力内容(stdout)で失敗を判断する
終了コードだけでなく、コマンドの出力内容を見て失敗を判断する場合もfailed_whenが使えます。以下は実サーバー上でのad-hoc実行結果です。# ディスク使用率が95%以上なら失敗とみなすタスク - name: ルートパーティションのディスク使用率を確認する command: df --output=pcent / register: disk_result changed_when: false failed_when: disk_result.stdout_lines[-1] | replace('%','') | int >= 95
[tomohiro@ansible-ctl ~]$ ansible -m command -a "df --output=pcent /" web01 -i inventory/prod web01 | SUCCESS => { "changed": true, "cmd": ["df", "--output=pcent", "/"], "rc": 0, "stdout": "Use%\n 42%", "stdout_lines": [ "Use%", " 42%" ] }
stdout_lines[-1]でリストの最後の要素( 42%の行)を取り出し、| replace('%','')でパーセント記号を除去したうえで| intフィルタで整数に変換して比較します。ディスク使用率が95%以上になった瞬間にタスクが失敗ステータスを返し、alertのhandlerやrescueブロックにつなげる設計が可能です。3. 複数条件をandで組み合わせる
failed_whenの条件はJinja2の論理演算子(and/or/not)で組み合わせられます。リスト形式で並べるとすべての条件がAND評価になります。# 終了コードが0以外 かつ "not installed" が出力に含まれない場合のみ失敗 - name: パッケージの存在確認 command: rpm -q mypackage register: rpm_result failed_when: - rpm_result.rc != 0 - "'not installed' not in rpm_result.stdout"
changed_whenで「変更あり」の定義を上書きする
changed_whenは「このタスクが何かを変更したか」の判定ロジックを上書きするキーワードです。commandモジュールのデフォルト動作(成功時は常にchanged)を制御するために頻繁に使います。1. changed_when: falseで常に「変更なし」にする
最もシンプルな使い方はchanged_when: falseです。確認のためだけに実行するコマンドには「変更なし」とみなすのが適切です。# 確認系コマンドにはchanged_when: falseを付ける - name: PHPのバージョンを確認する command: php -v register: php_version changed_when: false - name: バージョン情報を表示する debug: msg: "{{ php_version.stdout_lines[0] }}"
changed_when: falseを付けておくと、毎回「changed」の黄色が並んで結果が見づらくなる問題を解消できます。2. 出力内容で変更を判断する実践例
インストールスクリプトのように「すでにインストール済みの場合はスキップし、新規インストールの場合だけchangedにしたい」という場面で特に役立ちます。# カスタムスクリプトでインストール状態を検出する - name: myappをインストールする command: /opt/install-myapp.sh register: install_result changed_when: "'already installed' not in install_result.stdout" failed_when: install_result.rc != 0
failed_whenと組み合わせることで、成否と変更の両方を細かく制御できます。
Ansible実践ハンズオンの詳細を見る >>
ignore_errorsとfailed_whenの違いと使い分け
「タスクが失敗しても処理を続けたい」という場面ではignore_errorsとfailed_whenの2つの手段があります。両者の動作と使いどころは明確に異なります。ignore_errors: trueを使うと、タスクが失敗してもPlaybookの実行を継続します。ただしタスクの最終的なステータスは「failed」のままであり、Playbookのサマリーにはfailedとして記録されます。# 旧設定ファイルの削除を試みる(存在しなくてもエラーにしない) - name: 旧設定ファイルを削除する(任意) command: rm /etc/app/old-config.conf register: rm_result ignore_errors: true - name: 削除成功時のみメッセージを表示する debug: msg: "旧設定ファイルを削除しました" when: rm_result.rc == 0
failed_when: falseを使うとタスクは決して失敗ステータスにはなりません。サマリーには「ok」または「changed」として記録されます。使い分けの判断基準は以下の通りです。
・ignore_errors:「失敗しても処理を続けたい」が、失敗の記録はサマリーに残したい場面
・failed_when: false:「そもそも失敗という概念がないタスク」として扱いたい場面
・failed_whenで条件を絞る:「一部の終了コードや出力のみを本当の失敗とみなしたい」場面
【重要】ignore_errorsの乱用が招くリスク
本番環境ではignore_errorsを多用するのは禁止に近い扱いとして意識してください。私のセミナーでも「なぜPlaybookが止まるか分からないからignore_errors: trueを全タスクに付けた」という事例を見かけることがあります。こうすると本来検出すべき障害がサイレントに通過してしまい、原因特定が困難になります。ignore_errorsを使う場面は「ファイルが存在しない場合のrm」「サービスが起動していない場合の停止試行」など、失敗しても業務に影響がないことが明確な任意処理に限定してください。失敗の原因を明示的に定義できる場面では、必ずfailed_whenで条件を絞るのが鉄則です。実務でよく使うパターン集
1. サービスの状態確認を安全に行う
systemctl is-activeはサービスの状態に応じて終了コードが変わるコマンドです。ansible.builtin.service_factsモジュールが使えない環境や、特定コンテナ環境での状態確認に活用できます。# サービスの状態を取得し、後続タスクの条件分岐に使う - name: nginxの稼働状態を取得する command: systemctl is-active nginx register: nginx_status changed_when: false failed_when: false - name: nginxが停止している場合にアラートを出す debug: msg: "nginx is not running (status: {{ nginx_status.stdout }})" when: nginx_status.rc != 0
failed_when: falseとchanged_when: falseを両方指定することで、このタスクは「純粋な確認専用」の位置づけになります。後続のタスクでwhen条件としてnginx_status変数を参照できます。2. カスタムバイナリの存在確認とインストール設計
dnfやaptモジュールは冪等性を内部に持っていますが、tarball展開やカスタムインストーラーを使う場面ではcommandモジュールとの組み合わせが必要です。# インストール済みチェック→未インストール時のみインストール実行 - name: myappがインストール済みかチェックする command: which myapp register: which_result changed_when: false failed_when: false - name: myappをインストールする command: /opt/install-myapp.sh when: which_result.rc != 0 register: install_result changed_when: "'installed successfully' in install_result.stdout" failed_when: install_result.rc != 0
3. 外部スクリプトの終了コードと出力を組み合わせた判定
自社開発のオペレーションスクリプトを呼び出す場面では、終了コードだけでは成否の判定が難しいケースがあります。出力内容との組み合わせで判断します。# デプロイスクリプトを呼び出す - name: アプリをデプロイする command: /opt/deploy.sh {{ app_version }} register: deploy_result failed_when: - deploy_result.rc != 0 - "'DEPLOY_OK' not in deploy_result.stdout" changed_when: "'DEPLOY_OK' in deploy_result.stdout"
トラブルシュート:registerとdebugで動作を確認する
failed_whenやchanged_whenの条件が想定通りに動いているか確認するときは、debugモジュールを組み合わせるのが基本です。# registerの中身をすべて表示してデバッグする - name: コマンドを実行する command: df --output=pcent / register: df_result changed_when: false failed_when: false - name: df_resultの中身を確認する(デバッグ用・運用時は削除) debug: var: df_result
debug: var: df_resultを実行すると、rc・stdout・stderr・stdout_linesの全内容がAnsible実行時に表示されます。failed_whenの条件式を作る前に、まずこの方法で実際の値を確認してから条件式を設計するのが、手戻りを防ぐコツです。また、
ansible-playbook実行時に-vオプション(詳細)または-vv(さらに詳細)を付けると、タスクの実行結果が詳しく表示されます。条件式が正しく評価されているかを確認する際に役立ちます。よくある実務上の注意として、Jinja2変数の型を間違えるケースがあります。
stdoutは文字列(str)なので、数値と比較する際は| intフィルタで整数に変換する必要があります。# NG: stdout_lines[-1]は文字列のため、整数との直接比較は意図通りにならない場合がある failed_when: disk_result.stdout_lines[-1] >= 95 # OK: | replaceでパーセント記号を除去し、| intで整数に変換してから比較する failed_when: disk_result.stdout_lines[-1] | replace('%','') | int >= 95
本記事のまとめ
| キーワード | 役割 | 典型的な使い方 |
|---|---|---|
failed_when |
失敗条件をPlaybook側で定義する | failed_when: result.rc != 0 and "error" in result.stderr |
changed_when |
変更ありの条件をPlaybook側で定義する | changed_when: "'installed' in result.stdout" |
changed_when: false |
常に「変更なし」とみなす(確認系コマンド専用) | 確認・情報収集専用のタスクに必須 |
ignore_errors: true |
失敗してもPlaybookを継続させる | 任意処理・存在しても問題ないファイル削除など |
register |
タスクの実行結果を変数に格納する | register: resultでrc・stdout・stderrを保存 |
commandモジュールやshellモジュールを使うPlaybookでは、failed_whenとchanged_whenを意識的に設計することが、堅牢で読みやすい構成管理の基礎になります。まずregisterで変数に結果を格納し、debugモジュールで実際の値を確認してから条件式を組み立てる習慣を持つと、試行錯誤を最小限に抑えられます。Ansible実践ハンズオンの詳細を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Ansibleの接続設計|コントロールノードとSSH鍵・踏み台サーバーの設定からインベントリ変数まで
- この記事の属するカテゴリ:Ansibleへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます