Ansibleで各サーバーごとに異なるサービスやパッケージを自動起動・インストールしたいとき、`host_vars` に配列変数を定義してタスクでループさせたいですよね。

でも、いざ試してみると変数がうまく展開されなかったり、`{{ item }}` を書くとエラーが出たりして詰まることがあります。

この記事では、`host_vars` に配列変数を定義して `loop`(旧 `with_items`)でループさせる方法を、具体的なYAMLコードと合わせて解説します。

目次

Ansibleのhost_varsとは

Ansibleにはホストごとに変数を管理する仕組みとして `host_vars` ディレクトリがあります。

inventory/
├── hosts              # インベントリファイル
└── host_vars/
    ├── server1.yml    # server1 専用の変数
    └── server2.yml    # server2 専用の変数

この `host_vars/<ホスト名>.yml` に変数を書いておくと、対象ホストにのみ変数が適用されます。サーバーごとに設定を出し分けたいときに非常に便利な仕組みです。

`host_vars` はAnsibleのベストプラクティスで推奨されているディレクトリ構成です。Playbookをシンプルに保ちながら、ホストごとのカスタマイズが可能になります。

host_varsに配列変数を定義する

要件の整理

  • server1 に自動起動させたいサービス:`httpd`、`postfix`
  • server2 に自動起動させたいサービス:`httpd`、`postfix`、`mysql`

サーバーごとにリストが異なるため、`host_vars` にそれぞれ配列変数として定義します。

host_vars/server1.yml

service_on:
  - httpd
  - postfix

host_vars/server2.yml

service_on:
  - httpd
  - postfix
  - mysql

YAMLで配列(リスト)を定義するときは、各要素の頭に ` ` (ハイフン + スペース)を付けます。インデントは2スペースが一般的です。フロースタイル(`service_on: [httpd, postfix]`)でも動作しますが、可読性のためにブロックスタイルを推奨します。

loopでhost_vars変数をループさせる(Ansible 2.5以降推奨)

タスクファイルの書き方

`roles/service_on/tasks/main.yml` を以下のように記述します。

- name: 各サービスを自動起動に設定する
  ansible.builtin.service:
    name: "{{ item }}"
    state: started
    enabled: yes
  loop: "{{ service_on }}"
  become: yes

これで `service_on` に定義された配列が1要素ずつ `{{ item }}` に展開され、各サービスに対して `service` モジュールが実行されます。

`name: “{{ item }}”` のように、変数展開を含むフィールドは必ずダブルクォートで囲む必要があります。クォートがないとAnsibleがYAMLとしてパースできずエラーになります。

with_itemsでの書き方(旧バージョン・後方互換)

Ansible 2.4以前のバージョン、もしくは既存PlaybookのメンテナンスのためにAnsible 2.5以降でも `with_items` を使い続けることはできます。

- name: 各サービスを自動起動に設定する
  ansible.builtin.service:
    name: "{{ item }}"
    state: started
    enabled: yes
  with_items: "{{ service_on }}"
  become: yes
比較`loop``with_items`
対応バージョンAnsible 2.5以降推奨全バージョン(非推奨)
フラット化なしリストを自動フラット化する
公式推奨推奨非推奨(将来削除予定)

`with_items` はAnsible 2.5以降では非推奨(deprecated)です。新規でPlaybookを書く場合は `loop` を使うことを強く推奨します。

よくあるエラーと対処法

エラー1: `name` フィールドのクォート忘れ

エラーになるコード(NG)

- name: 各サービスを起動する
  ansible.builtin.service:
    name: {{ item }}   # ← クォートがない!
    state: started
  loop: "{{ service_on }}"

エラーメッセージ(例)

ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:

修正後(OK)

- name: 各サービスを起動する
  ansible.builtin.service:
    name: "{{ item }}"   # ← ダブルクォートで囲む
    state: started
  loop: "{{ service_on }}"

エラー2: loop変数が文字列として渡されている

`host_vars` で配列を定義しているつもりが、実際には文字列になっているケースです。

NG(文字列として定義されてしまう例)

# host_vars/server1.yml
service_on: "httpd postfix"   # スペース区切りの文字列になっている

OK(正しいYAML配列)

# host_vars/server1.yml
service_on:
  - httpd
  - postfix

`ansible -m debug -a “var=service_on” server1` コマンドで変数の値をデバッグ確認できます。配列として認識されているかチェックしてみてください。

まとめ

Ansibleの `host_vars` に配列変数を定義して `loop` でループさせるポイントをまとめます。

  • ✅ `host_vars/<ホスト名>.yml` にYAMLの配列形式(`– 要素名`)で変数を定義する
  • ✅ タスクでは `loop: “{{ 変数名 }}”` と書く(Ansible 2.5以降推奨)
  • ✅ `name: “{{ item }}”` などの変数展開は必ずダブルクォートで囲む
  • ✅ `with_items` は非推奨なので、新規作成時は `loop` を使う

`host_vars` を活用してサーバーごとに変数を管理することで、Playbookをシンプルかつ可読性の高い状態に保てます。一度覚えると、Ansibleのベストプラクティスな構成がグッと使いやすくなります。ぜひ活用してみてください。

インフラの自動化・構成管理でお困りごとがあれば、お気軽にご相談ください。