エラーの内容
プレイブックの実行中に処理が止まります:
ERROR! conflicting action statements: copy, template
タスクの内容によっては、次のようなバリエーションも発生します:
ERROR! conflicting action statements: apt, yum
ERROR! conflicting action statements: file, copy
モジュール名は異なりますが、根本原因は常に同じです。1つのタスクブロックが同時に2つのアクションモジュールを実行しようとしています。
なぜこのエラーが発生するのか
Ansibleには厳格なルールがあります。1つのタスクにはアクションモジュールを1つだけ、これが絶対です。パーサーが同じインデントレベルに2つのモジュールキーを検出した場合、どちらを優先すべきか判断する方法がありません。そのため、推測せずに処理を停止します。
ほとんどの場合、このエラーはコピー&ペーストの際に忍び込みます。2つのタスクブロックをマージしたとき、重複したキーが一緒についてきたことに気づかないのです:
- name: Deploy config file
copy:
src: files/app.conf
dest: /etc/app/app.conf
template:
src: templates/app.conf.j2
dest: /etc/app/app.conf
ここでは copy と template が同じ階層に存在しています。Ansibleは conflicting action statements: copy, template を発生させ、それ以上の処理を拒否します。
手順ごとの修正方法
ステップ1 — 問題のあるタスクを見つける
Ansibleのエラー出力は実際にかなり役立ちます。競合するモジュール名を示し、どこを確認すればよいかを正確に教えてくれます:
ERROR! conflicting action statements: copy, template
The error appears to be in '/home/deploy/playbooks/site.yml': line 14, column 5
ファイルを開いて14行目に移動してください。問題のあるタスクがそこにあります。
ステップ2 — 適切なモジュールを選択する
削除する前に、どちらのモジュールが本来必要なのかを判断してください。最もよくある競合パターンの早見表を示します:
- copy vs template — 変数を含まない静的ファイルなら
copyを使います。{{ db_host }}のようなJinja2式が含まれるならtemplateを使います。 - apt vs yum — 対象OSに合ったパッケージマネージャーを選ぶか、汎用の
packageモジュールを使って議論を回避しましょう。 - file vs copy — 既存パスのパーミッション、オーナーシップ、またはシンボリックリンクを管理するなら
fileです。コントロールノードからリモートにファイルを転送するならcopyです。
ステップ3 — 分割するか削除する
方法A:一方のモジュールを残し、もう一方を削除する
# Before (broken)
- name: Deploy config file
copy:
src: files/app.conf
dest: /etc/app/app.conf
template:
src: templates/app.conf.j2
dest: /etc/app/app.conf
# After (fixed) — 実際に必要な方を選択する
- name: Deploy config file
template:
src: templates/app.conf.j2
dest: /etc/app/app.conf
owner: root
group: root
mode: '0644'
方法B:両方のアクションが本当に必要な場合は、2つのタスクに分割する
- name: Copy static base config
copy:
src: files/base.conf
dest: /etc/app/base.conf
mode: '0644'
- name: Render dynamic app config
template:
src: templates/app.conf.j2
dest: /etc/app/app.conf
mode: '0644'
ステップ4 — YAMLアンカーによる隠れた競合を探す
問題のある行が一見わかりにくい場合もあります。YAMLアンカーがアクションキーを持つブロックを展開し、重複を見えにくくすることがあります:
defaults: &defaults
copy:
src: files/default.conf
dest: /etc/app/default.conf
- name: Deploy config
<<: *defaults
template: # conflict — 'copy' merged in from the anchor above
src: templates/app.conf.j2
dest: /etc/app/app.conf
アンカーをインラインで展開するか、マージによって競合するモジュールキーが取り込まれないようにタスクを再構成してください。
修正を確認する
プレイブック全体をそのまま実行しないでください。まず構文チェックから始めましょう。コストはゼロで、どのホストにも触れる前に問題を検出できます:
ansible-playbook site.yml --check --syntax-check
成功すると次のように表示されます。プレイブック名だけで、それ以外は何もありません:
playbook: site.yml
問題ありません。次に実際のホストに対してドライランを実行し、何が変更されるかを確認します:
ansible-playbook site.yml --check -v
出力に問題がなければ、実際に実行します:
ansible-playbook site.yml
クイックリファレンス:1タスク1モジュールの原則
有効なタスクにはアクションモジュールが1つだけあります。それ以外の become、when、notify、register、loop、tags はタスクディレクティブです。これらはモジュールと並列に配置され、競合しません:
- name: Descriptive name of what this task does
module_name: # exactly ONE action module
param1: value1
param2: value2
become: true # task directive — fine to have alongside the module
when: some_condition
notify: restart app
同じレベルに2つのアクションモジュール?それは絶対に許されません。覚えておくべきルールはこれだけです。
今後の予防策
- プレイブックを実行するたびに
yamllintを実行しましょう。Ansibleがファイルを読み込む前に重複キーを検出します。 ansible-lintにはモジュールの競合使用に特化したルールがあります。CIパイプラインに追加する価値があります。- ロールのタスクをマージしたり、プレイブック間でコピー&ペーストした後は素早くスキャンしてください。各タスクブロックにはモジュールキーが1つだけあるべきです。
- VS Code用のAnsible拡張機能は、入力中に競合するキーをハイライト表示し、問題を発生源で検出できます。

