ansible環境でshell/cmdに頼らない構成管理入門|role設計と代替モジュール選択指針

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Ansible > ansible環境でshell/cmdに頼らない構成管理入門|role設計と代替モジュール選択指針
「ansible shell cmd でPlaybookを書いてしまったが、このやり方で構成管理として合格なのだろうか」
Ansibleを学びはじめたエンジニアの多くが、こういう疑問を持ちます。

Linuxコマンドを書き慣れていると、ansible.builtin.shell や ansible.builtin.command は「シェルと同じ感覚で書ける」便利なモジュールに見えます。しかし現場で構成管理の規模が大きくなるにつれ、shell/cmdへの依存度が高いPlaybookはメンテナンスコストを急上昇させます。

この記事では、ansible shell cmdモジュールの依存から脱却するための代替モジュール選択指針と、role設計によるアーキテクチャレベルの防衛策を解説します。RHEL 9.4 / Ubuntu 24.04 LTSで動作確認した実行例を交えながら、現場で通用する構成管理の「型」を身につけていきましょう。

この記事のポイント

・shell/cmdは冪等性がなくPlaybookが毎回「changed」になる根本原因となる
・パッケージ・サービス・ファイル操作は専用モジュールで安全に代替できる
・role設計でshell/cmdの混入を構造的に防ぐレイヤー分離が有効
・changed_whenとcreatesオプションで止むを得ない場合の冪等性を確保できる


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

なぜAnsibleでもshell/cmdに頼ってしまうのか

Linuxコマンドを日常的に使っているエンジニアにとって、sshで接続してコマンドを手打ちする操作と、Ansibleのshellモジュールに同じコマンドを貼り付ける操作は、見た目上ほぼ変わりません。「動いた」という即効性があるため、最初のうちは深く考えずにshell/cmdを使い続けてしまいます。

具体的によく見かけるパターンを挙げます。

・パッケージインストールを shell: yum install -y httpd と書く
・サービス起動を command: systemctl start nginx と書く
・設定ファイルの1行追加を shell: echo "option=value" >> /etc/myapp.conf と書く

いずれも「Ansibleを使わずに手でやるとしたらこう打つ」コマンドをそのまま写したものです。動作はします。しかしこの書き方は、Ansibleの本来の機能を活かし切れていません。

shell/cmdへの過依存が招く3つのリスク

1. Playbookが毎回「changed」になる

shell/cmdモジュールはコマンドを実行するだけで、実行後の状態を確認しません。そのため2回目以降の実行でも「コマンドを実行した=何かが変わった」として changed を返します。

実際にshellモジュールで yum install -y httpd を繰り返し実行した出力がこちらです。

[tomohiro@sv01 ~]$ ansible-playbook -i inventory.ini site.yml PLAY [webservers] **************************************************** TASK [install httpd via shell] *************************************** changed: [192.168.1.101] changed: [192.168.1.102] PLAY RECAP *********************************************************** 192.168.1.101 : ok=1 changed=1 unreachable=0 failed=0 192.168.1.102 : ok=1 changed=1 unreachable=0 failed=0

2回目も3回目も同じ結果が返ります。「どのホストに変更が入ったか」を追えなくなり、監査ログとしての意味を失います。

2. checkモードが機能しない

ansible-playbook --check によるドライラン検証は、shell/cmdを含むPlaybookでは正確に動作しません。専用モジュールであれば「このタスクは実行時にchangedになるか」を事前に確認できますが、shell/cmdは実行してみるまで状態がわかりません。checkモードを本番前の安全弁として使えなくなります。

3. クロスプラットフォーム展開が壊れる

yum install -y httpd はRHEL系でしか動きません。同じPlaybookをUbuntuホストに適用しようとすると、そもそも yum コマンドが存在せず即座に失敗します。専用モジュールであれば ansible_pkg_mgr などのfactを活用してOSに応じた分岐を簡潔に書けます。

代替モジュール選択の実践指針

1. パッケージ管理はdnf/apt/yumモジュールへ

「インストール済みであればスキップ」という冪等性が自動的に保証されます。

# NG: shell/commandモジュール - name: install httpd ansible.builtin.shell: yum install -y httpd # OK: dnfモジュール(RHEL 8以降) - name: install httpd ansible.builtin.dnf: name: httpd state: present

dnf モジュールを使えば、httpdがすでにインストール済みの場合は ok を返します。変更が必要な場合のみ changed となり、ログが意味を持つようになります。

2. 設定ファイル操作はlineinfile/blockinfile/templateモジュールへ

設定ファイルへの1行追加には lineinfile、複数行の追加には blockinfile、変数を含む動的なファイル生成には template が使えます。

# NG: shellで追記(冪等性なし・二重追加の危険あり) - name: add config line ansible.builtin.shell: echo "MaxClients 150" >> /etc/httpd/conf/httpd.conf # OK: lineinfileモジュール(行の存在を確認して追加) - name: set MaxClients ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf regexp: '^MaxClients' line: 'MaxClients 150' state: present

lineinfile は「その行がすでに存在すれば変更しない・なければ追加する」動作を保証します。shellの >> では同じ行が何度も追記される危険があります。

3. サービス制御はserviceモジュールへ

# NG: commandモジュール - name: start nginx ansible.builtin.command: systemctl start nginx # OK: serviceモジュール - name: ensure nginx is running ansible.builtin.service: name: nginx state: started enabled: true

service モジュールは「すでに起動していれば何もしない」という冪等な動作をします。OSのinit システム(systemd / SysVinit)の違いも吸収します。

4. ユーザー管理はuserモジュールへ

# NG: shellで直接useradd - name: create deploy user ansible.builtin.shell: useradd -m -s /bin/bash deployuser # OK: userモジュール - name: ensure deploy user exists ansible.builtin.user: name: deployuser shell: /bin/bash create_home: true state: present

shellの useradd はユーザーが存在するとエラーを返すため、2回目以降の実行でPlaybookが失敗します。user モジュールは存在を確認してから操作するため安全です。

shell/cmdが許容される判断基準

すべてを専用モジュールに置き換えられるわけではありません。以下の条件が揃う場合に限り、shell/cmdは許容されます。

・専用モジュールが存在しない操作:独自のインストールスクリプトや複数コマンドを組み合わせた複雑な初期化処理
・冪等性を自分で保証できる場合:creates オプション(指定ファイルが存在すればスキップ)や changed_when: false を適切に使える場合

# createsオプションで冪等性を確保 - name: run setup script only once ansible.builtin.command: cmd: /opt/app/setup.sh creates: /opt/app/.setup_done

/opt/app/.setup_done が存在すれば setup.sh をスキップします。スクリプト自身が完了後にこのファイルを作成する設計にしておくことで冪等性を確保できます。

Ansibleの構成管理を体系的に学びたい方は、Ansible実践講座で実機を使ったハンズオンカリキュラムを確認できます。

role設計でshell/cmd混入を防ぐ

個々のモジュール選択のルールを守るだけでなく、roleの設計段階からshell/cmdの混入を防ぐ仕組みを作ることが重要です。

tasksとhandlersの分離:
設定変更後の再起動処理をtasks内に直接書かず、handlersに切り出すことで「必要な時だけ再起動」が自動的に保証されます。

# roles/webserver/tasks/main.yml - name: deploy httpd.conf ansible.builtin.template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf owner: root group: root mode: '0644' notify: restart httpd # roles/webserver/handlers/main.yml - name: restart httpd ansible.builtin.service: name: httpd state: restarted

notify / handler のペアを使うことで、設定ファイルが変更された場合のみhttpdが再起動されます。shellの systemctl restart httpd を毎回実行する書き方とは根本的に異なります。

defaultsとvarsの活用:
role内で使うパラメーターは defaults/main.yml にデフォルト値を定義し、呼び出し側で上書きできる設計にします。環境ごとの差異をshellコマンドの条件分岐で吸収するのではなく、変数の差し替えだけで制御できます。

# roles/webserver/defaults/main.yml webserver_max_clients: 150 webserver_port: 80 # roles/webserver/tasks/main.yml - name: set MaxClients ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf regexp: '^MaxClients' line: "MaxClients {{ webserver_max_clients }}"

変数名にrole名をプレフィックスとして付ける(webserver_)ことで、複数roleを組み合わせたときの変数名衝突を防ぎます。

shell/cmdからの移行でよく出るエラーと対処法

「使えるモジュールが見つからない」と感じたとき

「shellで書けばすぐ動くのに、なぜわざわざ調べないといけないのか」という気持ちになることがあります。そのような場合は ansible-doc -l | grep <キーワード> でモジュール一覧を検索するか、Ansible公式ドキュメントの「All modules」ページで操作カテゴリごとに絞り込むと目的のモジュールが見つかります。

「dnfモジュールでインストールしたのにchangedになり続ける」

state: latest を指定すると「最新バージョンかどうか」を毎回確認し、アップデートがあれば changed を返します。「インストール済みであれば何もしない」ならば state: present を使います。

# state: latest は毎回更新確認してchangedになりやすい - name: install httpd (keeps updating) ansible.builtin.dnf: name: httpd state: latest # NG: 毎回最新確認 # state: present はインストール済みならokを返す - name: install httpd (idempotent) ansible.builtin.dnf: name: httpd state: present # OK: 冪等

「lineinfileで設定が重複して書き込まれた」

regexp パラメーターを省略すると「その行が存在するか」のチェックなしに毎回追記します。必ず regexp で既存行のパターンを指定してください。正規表現が実際のファイル内容にマッチしていない場合も二重追加が起きます。まず --check --diff で変更内容を確認してから本番に適用する習慣をつけることが重要です。

本記事のまとめ

操作の種類 shell/cmdの代わりに使うモジュール
パッケージインストール ansible.builtin.dnf / apt / yum
設定ファイルの1行追加・変更 ansible.builtin.lineinfile
設定ファイルの複数行追加 ansible.builtin.blockinfile
静的ファイルの配布 ansible.builtin.copy
変数を含む設定ファイルの生成 ansible.builtin.template
サービスの起動・停止・有効化 ansible.builtin.service
ユーザー作成・管理 ansible.builtin.user
ファイル権限・所有者の変更 ansible.builtin.file
専用モジュールがない操作 ansible.builtin.command(createsで冪等性確保)
ansible shell cmdへの依存を減らすことは、Playbookの冪等性確保・checkモードの有効化・クロスプラットフォーム対応という3つの品質向上に直結します。まず手元のPlaybookで shell / command モジュールを使っているタスクをリストアップし、上の表を参考に置き換えられるものから着手してみてください。

roleの設計まで踏み込むと、handlersによる変更検知・defaultsによる変数管理・tasksとtemplatesの分離によって、shell/cmdに頼らなくても複雑な構成管理が実現できます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Ansibleのplaybookとrole設計を手を動かしながら学べる実践カリキュラムを用意しています。ツールの使い方にとどまらず、構成管理の設計思想から身につく講座です。>>Ansible実践講座の詳細はこちら

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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