- 技術 💻
Ansible入門|playbookの書き方を実行結果つきで解説
Ansibleとは、サーバーやPCの設定作業を「YAMLで書いた手順書(playbook)」として自動実行する、エージェント不要の構成管理ツールです。 管理される側に専用ソフトを入れず、SSH(またはローカル実行)とPythonだけで動きます。同じplaybookを何度流しても「あるべき状態」にそろえる冪等性(べきとうせい)が最大の特長です。
この記事では、概念の説明だけで終わらせず、localhost を対象にしたplaybookを実際に2回実行し、1回目の changed が2回目に ok へ変わる様子を出力そのままでお見せします。さらに --check --diff、ansible-lint、ansible-vault、実際のエラー文と直し方まで、コピペで試せる形でまとめました。検証環境は ansible 12.3.0(ansible-core 2.19.13)/Python 3.11/Ubuntu 24.04 です。
Ansibleの早見表
| やりたいこと | 書き方・コマンド | ポイント |
|---|---|---|
| インストール | pip install ansible |
2026年10月時点の最新は ansible 14.4.0(core 2.21.4、Python 3.12以上)。Python 3.11 では 12.3.0 まで |
| バージョン確認 | ansible --version |
表示されるのは ansible-core のバージョン |
| 接続確認 | ansible -i inventory.ini all -m ansible.builtin.ping |
"ping": "pong" が返れば成功 |
| playbook実行 | ansible-playbook -i inventory.ini site.yml |
最後の PLAY RECAP で結果を確認 |
| 文法チェック | ansible-playbook site.yml --syntax-check |
実行せずにYAMLと構文だけ確認 |
| 変更の予告 | --check --diff |
実際には変えずに差分だけ表示(ドライラン) |
| タスク一覧 | --list-tasks |
実行順の確認に便利 |
| 変数を上書き | -e app_port=7802 |
コマンドライン変数は優先度が最も高い |
| 一部だけ実行 | --tags config / --limit web01 |
タグ・ホストで絞り込み |
| ロールの雛形 | ansible-galaxy role init roles/webapp |
標準ディレクトリが一式できる |
| 品質チェック | ansible-lint site.yml |
FQCN・name・権限などを指摘 |
| 秘密情報の暗号化 | ansible-vault encrypt secret.yml |
実行時は --vault-password-file か --ask-vault-pass |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Ansible入門|playbookの書き方を実行結果つきで解説」
Ansibleの全体像:構成図と用語表
Ansibleは「どこに(インベントリ)」「何を(playbook)」「どうやって(モジュール)」の3つで考えると整理できます。
flowchart LR
subgraph CN[制御ノード(Ansibleを入れたPC)]
INV[インベントリ<br/>inventory.ini]
PB[playbook<br/>site.yml]
VARS[変数<br/>vars / group_vars]
ROLE[ロール<br/>roles/webapp]
PB --> ROLE
VARS --> PB
end
PB -- SSH / local --> H1[管理対象ノード1]
PB -- SSH / local --> H2[管理対象ノード2]
INV -. 対象を指定 .-> PB
subgraph Task[playbookの中身]
T1[タスク = モジュール呼び出し] -- 変更あり notify --> HD[handler]
end
| 用語 | ひとことで | 例 |
|---|---|---|
| 制御ノード | Ansibleを実行する側のマシン | 手元のPC・踏み台サーバー |
| 管理対象ノード | 設定される側のマシン。Pythonがあればよい | Webサーバー、検証用VM |
| インベントリ | 対象ホストとグループの一覧 | inventory.ini、inventory.yml |
| playbook | 「どのホストに何をするか」を書いたYAML | site.yml |
| play | playbook内の1ブロック。hosts とタスクの組 |
- name: Web設定 / hosts: web |
| タスク | 1つの作業。中身は1つのモジュール呼び出し | ディレクトリ作成 |
| モジュール | 作業の部品。冪等に作られている | ansible.builtin.file |
| FQCN | モジュールの完全修飾名 | ansible.builtin.copy |
| コレクション | モジュールやロールの配布単位 | ansible.builtin、community.general |
| 変数 | 値を差し替える仕組み | vars:、group_vars/、-e |
| ファクト | 対象ノードから自動収集する情報 | ansible_facts['os_family'] |
| handler | 変更があったときだけ最後に1回動くタスク | サービス再起動 |
| ロール | タスク・変数・テンプレートを部品化したもの | roles/webapp |
| 冪等性 | 何度実行しても同じ結果になる性質 | 2回目は ok(変更なし) |
インストールとバージョンの選び方
Ansibleには「ansible」(コレクション同梱のパッケージ)と「ansible-core」(本体のみ)の2種類があります。 入門では、よく使うコレクションがそろう ansible を入れるのがおすすめです。
PyPIで確認した2026年10月1日時点の情報は次のとおりです。
| パッケージ | 最新版 | 必要なPython | 備考 |
|---|---|---|---|
| ansible | 14.4.0(2026-09-08) | 3.12以上 | ansible-core 2.21系を同梱 |
| ansible-core | 2.21.4(2026-09-08) | 3.12以上 | 本体のみ |
| ansible 12.3.0 | — | 3.11以上 | ansible-core 2.19系。本記事の検証版 |
# 仮想環境に入れる(OSのPythonを汚さない)
python3 -m venv .venv
. .venv/bin/activate
pip install ansible==12.3.0 ansible-lint
python -c "import importlib.metadata as m; print(m.version('ansible'), m.version('ansible-core'))"
12.3.0 2.19.13
Python 3.12以上の環境なら pip install ansible だけで最新版が入ります。 本記事のplaybookは 2.19 と 2.21 で書き方に差が出ない基本機能だけを使っています。
まずはインベントリを書く
インベントリは「どのホストに接続するか」の一覧です。 今回は手元のマシン自身を対象にするため、ansible_connection=local を指定します。SSHの準備なしで試せるので、入門の最初の一歩にぴったりです。
# inventory.ini
[local]
localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python3
接続確認には ping モジュールを使います(ICMPのpingではなく、Pythonが動くかの確認です)。
ansible -i inventory.ini local -m ansible.builtin.ping
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}
リモートサーバーを対象にするときは、IPとユーザーを書くだけです。
[web]
web01 ansible_host=192.0.2.10 ansible_user=deploy
web02 ansible_host=192.0.2.11 ansible_user=deploy
[web:vars]
ansible_ssh_private_key_file=~/.ssh/id_ed25519
playbookの書き方:基本モジュールをまとめて動かす
playbookは「play → tasks → モジュール」の入れ子で書きます。 次の1本で、入門で必要な要素(file、template、lineinfile、copy、command と creates、loop、when、register/debug、handler)を全部使っています。
準備するファイル
ans/
├── inventory.ini
├── site.yml
├── templates/
│ └── app.conf.j2
└── files/
└── motd.txt # 中身: hello from ansible
{# templates/app.conf.j2(最初の版) #}
# managed by Ansible
app_name = {{ app_name }}
port = {{ app_port }}
site.yml(コピペで動く完成形)
---
- name: ローカルで基本モジュールを試す
hosts: local
gather_facts: true
vars:
base_dir: /tmp/t80_demo
app_name: todo-api
app_port: 7801
users_dirs:
- logs
- data
- tmp
tasks:
- name: ベースディレクトリを作る
ansible.builtin.file:
path: "{{ base_dir }}"
state: directory
mode: "0755"
- name: サブディレクトリをloopで作る
ansible.builtin.file:
path: "{{ base_dir }}/{{ item }}"
state: directory
mode: "0755"
loop: "{{ users_dirs }}"
- name: 設定ファイルをテンプレートから生成
ansible.builtin.template:
src: templates/app.conf.j2
dest: "{{ base_dir }}/app.conf"
mode: "0644"
notify: 設定変更を通知
- name: 1行だけ追記・置換する
ansible.builtin.lineinfile:
path: "{{ base_dir }}/app.conf"
regexp: "^log_level ="
line: "log_level = info"
- name: ファイルをコピーする
ansible.builtin.copy:
src: files/motd.txt
dest: "{{ base_dir }}/motd.txt"
mode: "0644"
- name: 初回だけコマンドを実行する(creates)
ansible.builtin.command:
cmd: touch {{ base_dir }}/data/initialized
creates: "{{ base_dir }}/data/initialized"
- name: OSファミリーがDebianのときだけ実行(when)
ansible.builtin.debug:
msg: "Debian系です: {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}"
when: ansible_facts['os_family'] == "Debian"
- name: 結果を変数に保存(register)
ansible.builtin.command:
cmd: cat {{ base_dir }}/app.conf
register: conf_result
changed_when: false
- name: 保存した結果を表示(debug)
ansible.builtin.debug:
var: conf_result.stdout_lines
handlers:
- name: 設定変更を通知
ansible.builtin.debug:
msg: "app.conf が変わったので再起動します(デモ)"
| 書き方 | 意味 |
|---|---|
"{{ 変数 }}" |
Jinja2で変数を埋め込む。値の先頭が {{ のときは必ずクォートで囲む |
mode: "0644" |
権限は文字列で書く(8進数の解釈ずれを防ぐ) |
loop: |
配列の要素ごとにタスクを繰り返す。要素は item で参照 |
creates: |
そのファイルがあればコマンドを実行しない(commandを冪等にする) |
when: |
条件が真のときだけ実行。{{ }} は付けない |
register: |
モジュールの結果を変数に保存 |
changed_when: false |
読み取り専用コマンドを「変更なし」扱いにする |
notify: |
このタスクが changed のとき、指定した handler を予約する |
1回目の実行結果
ansible-playbook -i inventory.ini site.yml
PLAY [ローカルで基本モジュールを試す] ******************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [ベースディレクトリを作る] ************************************************
changed: [localhost]
TASK [サブディレクトリをloopで作る] ********************************************
changed: [localhost] => (item=logs)
changed: [localhost] => (item=data)
changed: [localhost] => (item=tmp)
TASK [設定ファイルをテンプレートから生成] **************************************
changed: [localhost]
TASK [1行だけ追記・置換する] ***************************************************
changed: [localhost]
TASK [ファイルをコピーする] ****************************************************
changed: [localhost]
TASK [初回だけコマンドを実行する(creates)] ***********************************
changed: [localhost]
TASK [OSファミリーがDebianのときだけ実行(when)] ******************************
ok: [localhost] => {
"msg": "Debian系です: Ubuntu 24.04"
}
TASK [結果を変数に保存(register)] ********************************************
ok: [localhost]
TASK [保存した結果を表示(debug)] *********************************************
ok: [localhost] => {
"conf_result.stdout_lines": [
"# managed by Ansible",
"app_name = todo-api",
"port = 7801",
"log_level = info"
]
}
RUNNING HANDLER [設定変更を通知] ***********************************************
ok: [localhost] => {
"msg": "app.conf が変わったので再起動します(デモ)"
}
PLAY RECAP *********************************************************************
localhost : ok=11 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1回目は作成系のタスクがすべて changed になり、template が changed だったので handler が最後に1回だけ動きました。
2回目の実行結果:changed が残る理由
同じplaybookをもう一度流すと、次のようになりました(該当部分を抜粋)。
TASK [ベースディレクトリを作る] ************************************************
ok: [localhost]
TASK [サブディレクトリをloopで作る] ********************************************
ok: [localhost] => (item=logs)
ok: [localhost] => (item=data)
ok: [localhost] => (item=tmp)
TASK [設定ファイルをテンプレートから生成] **************************************
changed: [localhost]
TASK [1行だけ追記・置換する] ***************************************************
changed: [localhost]
TASK [ファイルをコピーする] ****************************************************
ok: [localhost]
TASK [初回だけコマンドを実行する(creates)] ***********************************
ok: [localhost]
...
PLAY RECAP *********************************************************************
localhost : ok=11 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
file・copy・command(creates)は ok に変わりましたが、template と lineinfile が毎回 changed になります。 理由は次のとおりです。
- template がテンプレートどおりにファイルを書き戻す(
log_level行が消える)→ changed - lineinfile が
log_level = infoを再び追記する → changed - template が changed なので handler も毎回動く(=毎回「再起動」が走る)
1つのファイルを template と lineinfile の両方で管理すると、この「綱引き」が起きます。 直し方は、ファイルの持ち主を1つにまとめることです。
直す前と後
{# 直した後: templates/app.conf.j2 #}
# managed by Ansible
app_name = {{ app_name }}
port = {{ app_port }}
log_level = {{ log_level | default('info') }}
site.yml からは lineinfile タスクを削除します。修正後、作業ディレクトリを消してから2回続けて実行した結果です。
# 1回目(handler も1回動く)
localhost : ok=10 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
# 2回目
localhost : ok=9 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
changed=0 になり、handler も動かなくなりました。これが冪等なplaybookの状態です。
| 状態 | 2回目の changed | handler | 判断 |
|---|---|---|---|
| 直す前(template+lineinfile) | 2 | 毎回動く | 不要な再起動が発生する |
| 直した後(templateに一本化) | 0 | 動かない | 冪等。何度流しても安心 |
「2回目に changed=0 になるか」は、playbookを書いたら必ず確認したいチェック項目です。
--check と --diff で変更を予告する
--check は実際には何も変えずに「変わる予定」だけを表示するドライランです。 --diff を足すと、ファイルの差分も見られます。ポート番号を変える想定で実行しました(テンプレートを直した後、lineinfile タスクを消す前の site.yml で実行)。
ansible-playbook -i inventory.ini site.yml --check --diff -e app_port=7802
TASK [設定ファイルをテンプレートから生成] **************************************
--- before: /tmp/t80_demo/app.conf
+++ after: /root/.ansible/tmp/ansible-local-82958aeocwpb/tmpjcafr1_d/app.conf.j2
@@ -1,4 +1,4 @@
# managed by Ansible
app_name = todo-api
-port = 7801
+port = 7802
log_level = info
changed: [localhost]
...
TASK [結果を変数に保存(register)] ********************************************
skipping: [localhost]
TASK [保存した結果を表示(debug)] *********************************************
ok: [localhost] => {
"conf_result.stdout_lines": []
}
...
PLAY RECAP *********************************************************************
localhost : ok=10 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
実行後もファイルは port = 7801 のままで、本当に変更されていないことを確認しました。
注目したいのは、command タスクが skipping になり、register の中身が空になった点です。 command/shell モジュールは「何が変わるか予測できない」ため、チェックモードでは実行されません。読み取り専用のコマンドなら、次のように書くとチェックモードでも結果を取れます。
- name: 結果を変数に保存(register)
ansible.builtin.command:
cmd: cat {{ base_dir }}/app.conf
register: conf_result
changed_when: false
check_mode: false # チェックモードでも実行する
| オプション | 用途 |
|---|---|
--check |
変更せずに予定だけ表示 |
--diff |
ファイル内容の差分を表示(--check なしでも使える) |
--syntax-check |
構文だけ確認(成功時は playbook: site.yml とだけ出る) |
--list-tasks |
実行されるタスクの一覧 |
--start-at-task "名前" |
途中のタスクから再開 |
-v 〜 -vvvv |
詳細ログ。接続トラブル時は -vvv |
ansible-lint で書き方をそろえる
ansible-lint は、playbookの書き方の「よくあるつまずき」を自動で指摘してくれるツールです。 あえて省略形で書いた bad.yml を検査しました(ansible-lint 26.9.0)。
# bad.yml(指摘を受ける例)
- hosts: local
tasks:
- shell: echo hello > /tmp/t80_demo/hello.txt
- name: install
copy: src=files/motd.txt dest=/tmp/t80_demo/m2.txt
name[play]: All plays should be named.
fqcn[action-core]: Use FQCN for builtin module actions (shell).
name[missing]: All tasks should be named.
no-changed-when: Commands should not change things if nothing needs doing.
yaml[indentation]: Wrong indentation: expected at least 3
name[casing]: All names should start with an uppercase letter.
no-free-form: Avoid using free-form when calling module actions. (copy)
risky-file-permissions: File permissions unset or incorrect.
fqcn[action-core]: Use FQCN for builtin module actions (copy).
Failed: 9 failure(s), 0 warning(s) in 1 files processed of 1 encountered. Last profile that met the validation criteria was 'min'.
(ファイル位置の行は省略しています)一方、先ほどの site.yml は次のとおり最上位のプロファイルを通過しました。
Passed: 0 failure(s), 0 warning(s) in 1 files processed of 1 encountered. Last profile that met the validation criteria was 'production'.
| 指摘ルール | 意味 | 直し方 |
|---|---|---|
name[play] / name[missing] |
play・タスクに名前がない | - name: を付ける |
name[casing] |
名前が小文字始まり | 先頭を大文字に(日本語名は対象外) |
fqcn[action-core] |
モジュール名が短縮形 | ansible.builtin.copy と書く |
no-free-form |
src=... dest=... の1行形式 |
キー: 値 の形式で書く |
risky-file-permissions |
mode がない |
mode: "0644" を付ける |
no-changed-when |
command/shell が常に changed | creates か changed_when を付ける |
yaml[indentation] |
リストのインデント | tasks: の下を2スペース下げる |
ansible-vault で秘密情報を暗号化する
パスワードやAPIキーは、ansible-vault で暗号化してから Git に入れます。 復号用のパスワードファイルは Git に入れません。
echo 'S3cretPass-demo' > .vault_pass && chmod 600 .vault_pass
printf 'db_password: "p@ssw0rd-demo"\n' > secret.yml
ansible-vault encrypt secret.yml --vault-password-file .vault_pass
head -3 secret.yml
$ANSIBLE_VAULT;1.1;AES256
37626462376532666632626561373963613964336439366565313432643438666433373663326631
3039303466356639393262396265333162383463616664650a373337313230356238373135343938
ansible-vault view secret.yml --vault-password-file .vault_pass
db_password: "p@ssw0rd-demo"
playbookでは普通の変数ファイルと同じように読み込めます。
# vault_use.yml
- name: vault変数を使う
hosts: local
gather_facts: false
vars_files:
- secret.yml
tasks:
- name: パスワードの長さだけ表示
ansible.builtin.debug:
msg: "db_password は {{ db_password | length }} 文字です"
TASK [パスワードの長さだけ表示] ************************************************
ok: [localhost] => {
"msg": "db_password は 13 文字です"
}
パスワードを渡し忘れると、次のエラーになります。
[ERROR]: Invalid vars_files file '.../secret.yml': Attempting to decrypt but no vault secrets found.
→ --vault-password-file .vault_pass か --ask-vault-pass を付けると解決します。ファイル全体ではなく1つの値だけ暗号化したいときは encrypt_string が便利です。
ansible-vault encrypt_string 'p@ssw0rd-demo' --name db_password --vault-password-file .vault_pass
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
65346232363531343439383836353563323833373639336530643163343139306466363637616466
...(以下略)
| コマンド | 用途 |
|---|---|
ansible-vault create f.yml |
新規作成(エディタが開く) |
ansible-vault encrypt f.yml |
既存ファイルを暗号化 |
ansible-vault view f.yml |
中身を表示 |
ansible-vault edit f.yml |
復号して編集→再暗号化 |
ansible-vault rekey f.yml |
パスワード変更 |
ansible-vault encrypt_string |
値1つだけ暗号化 |
ロールで部品化する
ロールは、タスク・handler・変数・テンプレートを決まったフォルダに分けて再利用できるようにしたものです。 雛形は ansible-galaxy で作れます。
ansible-galaxy role init roles/webapp
- Role roles/webapp was created successfully
roles/webapp/
├── README.md
├── defaults/main.yml # 既定値(優先度が最も低く、上書き前提)
├── files/ # copy で配るファイル
├── handlers/main.yml # handler
├── meta/main.yml # 依存ロールなど
├── tasks/main.yml # タスク本体
├── templates/ # template で使う .j2
├── tests/
└── vars/main.yml # ロール内部の変数(優先度が高い)
# roles/webapp/defaults/main.yml
---
webapp_dir: /tmp/t80_demo/webapp
webapp_port: 7803
# roles/webapp/tasks/main.yml
---
- name: アプリ用ディレクトリを作る
ansible.builtin.file:
path: "{{ webapp_dir }}"
state: directory
mode: "0755"
- name: 設定ファイルを置く
ansible.builtin.copy:
content: "port={{ webapp_port }}\n"
dest: "{{ webapp_dir }}/webapp.env"
mode: "0644"
notify: Webapp再起動
# roles/webapp/handlers/main.yml
---
- name: Webapp再起動
ansible.builtin.debug:
msg: "webapp を再起動します(port={{ webapp_port }})"
# role_site.yml
---
- name: ロールを使う
hosts: local
gather_facts: false
roles:
- role: webapp
vars:
webapp_port: 7804
TASK [webapp : アプリ用ディレクトリを作る] *************************************
changed: [localhost]
TASK [webapp : 設定ファイルを置く] *********************************************
changed: [localhost]
RUNNING HANDLER [webapp : Webapp再起動] ****************************************
ok: [localhost] => {
"msg": "webapp を再起動します(port=7804)"
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2回目は ok=2 changed=0 となり、handler は動きませんでした。defaults の 7803 が、playbook側の vars で 7804 に上書きされている点も確認できます。
変数の優先順位(よく使うものだけ)
| 優先度 | 場所 |
|---|---|
| 低 | ロールの defaults/main.yml |
| ↓ | インベントリの変数・group_vars/all |
| ↓ | group_vars/グループ名・host_vars/ホスト名 |
| ↓ | play の vars:・vars_files: |
| ↓ | ロールの vars/main.yml・set_fact・register |
| 高 | コマンドラインの -e(常に最優先) |
「上書きされる前提の既定値は defaults、変えてほしくない値は vars」と使い分けると迷いません。
おすすめのディレクトリ構成
infra/
├── ansible.cfg
├── inventories/
│ ├── dev/
│ │ ├── hosts.ini
│ │ └── group_vars/
│ │ └── all.yml
│ └── stg/
│ ├── hosts.ini
│ └── group_vars/
│ ├── all.yml
│ └── vault.yml # ansible-vault で暗号化
├── roles/
│ ├── common/
│ └── webapp/
├── site.yml # 全体を呼ぶ入口
└── webservers.yml # 役割ごとのplaybook
# ansible.cfg(毎回 -i を書かずに済む)
[defaults]
inventory = inventories/dev/hosts.ini
roles_path = roles
stdout_callback = default
環境ごとにインベントリを分けると、-i inventories/stg/hosts.ini を切り替えるだけで同じplaybookを使い回せます。
よく使うモジュール一覧(FQCN)
| やりたいこと | モジュール(FQCN) | 主な引数 |
|---|---|---|
| ディレクトリ・権限・リンク | ansible.builtin.file |
path state mode owner |
| ファイルを配る | ansible.builtin.copy |
src / content dest mode |
| テンプレートから生成 | ansible.builtin.template |
src dest mode |
| 1行だけ編集 | ansible.builtin.lineinfile |
path regexp line |
| 複数行ブロック編集 | ansible.builtin.blockinfile |
path block marker |
| コマンド実行 | ansible.builtin.command |
cmd creates removes |
| パイプ・リダイレクト付き | ansible.builtin.shell |
cmd creates |
| パッケージ(汎用) | ansible.builtin.package |
name state |
| apt / dnf | ansible.builtin.apt / ansible.builtin.dnf |
name state update_cache |
| サービス起動 | ansible.builtin.service / ansible.builtin.systemd_service |
name state enabled |
| ユーザー作成 | ansible.builtin.user |
name groups shell |
| ファイルの有無を調べる | ansible.builtin.stat |
path(結果を register) |
| URLから取得 | ansible.builtin.get_url |
url dest checksum |
| Git から取得 | ansible.builtin.git |
repo dest version |
| 待ち合わせ | ansible.builtin.wait_for |
port timeout |
| 変数を作る | ansible.builtin.set_fact |
任意のキー |
| メッセージ表示 | ansible.builtin.debug |
msg / var |
| 条件チェック | ansible.builtin.assert |
that fail_msg |
| 別ファイルのタスク | ansible.builtin.include_tasks / ansible.builtin.import_tasks |
ファイル名 |
command と shell は冪等にならないため、専用モジュールがあればそちらを優先し、使う場合は creates / changed_when を付けるのが基本です。
よくあるエラーと直し方
実際に ansible-core 2.19.13 で出したエラー文です。2.19 以降はエラー箇所の行と列が表示されるので、まず Origin の行を見ると早く解決できます。
変数名のタイプミス(undefined)
[ERROR]: Task failed: Finalization of task args for 'ansible.builtin.debug' failed: Error while resolving value for 'msg': 'app_nmae' is undefined
...
7 msg: "{{ app_nmae }}"
^ column 14
→ 変数名を確認します。未定義でもよい変数は {{ app_name | default('none') }} と既定値を付けます。
モジュール名の誤り
[ERROR]: couldn't resolve module/action 'ansible.builtin.cpy'. This often indicates a misspelling, missing collection, or incorrect module path.
→ つづりを直すか、community.general など必要なコレクションを ansible-galaxy collection install で入れます。
引数のインデント不足
# 直す前
- name: ファイル
ansible.builtin.file:
path: /tmp/t80_demo/y # モジュールと同じ深さになっている
state: directory
[ERROR]: conflicting action statements: ansible.builtin.file, path
# 直した後
- name: ファイル
ansible.builtin.file:
path: /tmp/t80_demo/y
state: directory
「conflicting action statements」は、引数がモジュール名と同じ深さに並んでいるサインです。
インベントリが読めない
[WARNING]: Unable to parse .../nohost.ini as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: Could not match supplied host pattern, ignoring: local
...
skipping: no hosts matched
→ エラーで止まらず「何もせず終わる」ため見落としがちです。-i のパスと hosts: のグループ名を確認します。
その他よく見るもの
| エラー・症状 | 主な原因 | 対処 |
|---|---|---|
UNREACHABLE! ... Permission denied (publickey) |
SSH鍵・ユーザー違い | ansible_user、鍵のパス、ssh 単体で接続確認 |
Missing sudo password |
become に必要なパスワードがない | -K(--ask-become-pass)を付ける |
Attempting to decrypt but no vault secrets found |
vaultのパスワード未指定 | --vault-password-file |
| 2回目も changed が出る | command/shell、template と lineinfile の併用 | creates・changed_when、管理を一本化 |
--check で register が空 |
command はチェックモードで実行されない | check_mode: false |
よくある質問
Q. Ansible と Terraform はどう使い分けますか? A. Terraform はクラウドのリソース(ネットワーク・VM など)を「作る」のが得意で、Ansible は作ったサーバーの中身(パッケージ・設定ファイル)を「整える」のが得意です。Terraform でVMを作り、Ansible で設定する組み合わせがよく使われます。
Q. 管理対象のサーバーに何かインストールする必要はありますか? A. SSHで接続でき、Pythonが入っていれば追加のエージェントは不要です。Pythonがない機器(ネットワーク機器など)向けには専用のコネクションプラグインがあります。
Q. command と shell はどちらを使えばよいですか?
A. パイプ(|)やリダイレクト(>)、環境変数の展開が必要なときだけ shell、それ以外は command を使います。どちらも冪等ではないので、creates や changed_when を付けます。
Q. Python 3.11 の環境で最新の Ansible は使えますか?
A. 2026年10月時点の ansible 14.x/ansible-core 2.21 は Python 3.12 以上が必要です。Python 3.11 では ansible 12.3.0(core 2.19)が最新になります。uv python install 3.12 などで新しいPythonを用意すると最新版も使えます。
まとめ
- Ansibleは、YAMLのplaybookで設定作業を自動化するエージェント不要のツールで、インベントリ・playbook・モジュールの3つで考えると整理しやすい
ansible_connection=localを使えば、SSHの準備なしに手元で練習できる- 2回目の実行で changed=0 になるかを必ず確認する。template と lineinfile の併用は「綱引き」になるので一本化する
--check --diffで事前に差分を確認でき、command はcheck_mode: falseで読み取りを続けられる- ansible-lint で FQCN・name・mode などの書き方をそろえ、ansible-vault で秘密情報を暗号化する
- ロールで部品化し、既定値は defaults、上書きは
-eや play の vars で行う
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Ansible入門|playbookの書き方を実行結果つきで解説」
株式会社RJCでは、Ansibleによる構成管理の知識を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。