Ansibleのfailed_when・changed_when・ignore_errorsでタスク結果を制御する方法|commandモジュールを安全に扱う設計パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > Ansibleのfailed_when・changed_when・ignore_errorsでタスク結果を制御する方法|commandモジュールを安全に扱う設計パターン
「なぜかAnsibleのタスクが全部 "changed" になってしまう」「commandモジュールを使ったら、実際には変わっていないのに毎回 "changed" と表示される」

こんな状況に直面したことはないでしょうか。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を条件式に組み込む設計が基本


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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_whenchanged_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"

「rc が0以外 AND 出力に "not installed" が含まれない」場合のみ失敗とみなすため、パッケージが見つからない(終了コード1 + "not installed"の出力)という想定済みのパターンは失敗にならない設計にできます。

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] }}"

このタスクは何度実行しても常に「ok」と報告されます。確認のためだけに実行するコマンドに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

スクリプトが「already installed」という文字列を出力した場合は「変更なし(ok)」とし、それ以外の出力(実際にインストールが走った場合)に「changed」とみなします。failed_whenと組み合わせることで、成否と変更の両方を細かく制御できます。
failed_when・changed_whenを含むAnsibleのPlaybook設計スキルを体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

ignore_errorsとfailed_whenの違いと使い分け

「タスクが失敗しても処理を続けたい」という場面ではignore_errorsfailed_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: falsechanged_when: falseを両方指定することで、このタスクは「純粋な確認専用」の位置づけになります。後続のタスクでwhen条件としてnginx_status変数を参照できます。

2. カスタムバイナリの存在確認とインストール設計

dnfaptモジュールは冪等性を内部に持っていますが、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"

スクリプトが「DEPLOY_OK」という文字列を標準出力に出した場合のみ成功かつ変更ありとみなします。スクリプト側との設計上の取り決め(成功時の識別子を出力する)が必要ですが、Playbook側で確実に成否を検出できる堅牢な設計になります。

トラブルシュート:registerとdebugで動作を確認する

failed_whenchanged_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を実行すると、rcstdoutstderrstdout_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_whenchanged_whenを意識的に設計することが、堅牢で読みやすい構成管理の基礎になります。まずregisterで変数に結果を格納し、debugモジュールで実際の値を確認してから条件式を組み立てる習慣を持つと、試行錯誤を最小限に抑えられます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。