AnsibleのJinja2フィルター設計入門|変数変換・デフォルト値・リスト操作でPlaybookの柔軟性を高めるパターン集

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > AnsibleのJinja2フィルター設計入門|変数変換・デフォルト値・リスト操作でPlaybookの柔軟性を高めるパターン集
Ansibleのroleを複数人で使い始めると、必ずと言っていいほど遭遇する問題があります。「変数を渡し忘れたらPlaybookがエラーで止まった」「インベントリから文字列で渡した値を条件分岐で使おうとしたら型エラーになった」——こうしたケースの多くは、Jinja2フィルターを設計の段階で適切に組み込むことで防げます。

この記事では、AnsibleのJinja2フィルターの設計入門として、defaultフィルター・型変換・文字列操作・リスト操作・ternaryの各パターンを、実際の実行例とrole設計への組み込み方まで含めて解説します。実行環境はAnsible 2.17 / RHEL 9.4です。

この記事のポイント

・defaultフィルターで未定義変数を安全な既定値で処理できる
・int・bool・stringフィルターで型の不一致エラーを防げる
・select・map・combineでリスト・辞書を動的に変換する設計ができる
・ternaryフィルターで条件分岐をインラインに書きPlaybookを短くできる


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

なぜJinja2フィルターを覚えるとPlaybookの設計が変わるのか

Ansibleの変数は「文字列・数値・真偽値・リスト・辞書」として扱われます。しかし、インベントリファイルのINI形式から渡した値は基本的に文字列として受け取られます。これが型エラーの元凶になります。

例えば次のPlaybookは実行するとエラーになります。

--- - name: ポート設定の確認 hosts: localhost vars: port: "8080" # INI形式インベントリから文字列として渡される tasks: - debug: msg: "次のポート番号: {{ port + 1 }}" # エラー: TypeError: can only concatenate str (not "int") to str

{{ port | int + 1 }} と書くだけで解決します。これがJinja2フィルターの役割です。

Jinja2フィルターは {{ 変数 | フィルター名 }} の形で変数を加工する仕組みで、「変数を受け取る段階で型変換・加工まで完結させる」という設計思想を実現します。複数のフィルターをパイプ | で連結でき、{{ port | int + 1 | string }} のように連鎖させることも可能です。

Jinja2フィルターには、Pythonのjinja2ライブラリ由来のものとAnsible固有のものの2種類があります。Ansible固有のフィルター(combinedict2itemsternaryなど)はjinja2本体には存在せず、Ansibleを使う際にのみ利用できます。設計の観点で重要なのは「どの段階で型変換・加工するか」を意識することです。

defaultフィルターで未定義変数を安全に処理する

Ansible roleの設計でまず覚えるべきフィルターが default です。呼び出し側がオプション変数を渡し忘れた場合でも、フォールバック値を返してPlaybookを安全に継続させます。

# roles/webapp/tasks/main.yml --- - name: アプリのポートを確認 debug: msg: "使用ポート: {{ webapp_port | default(8080) }}"

変数を指定した場合としなかった場合の実行結果を比較します。

# webapp_portを指定した場合 $ ansible-playbook -i hosts site.yml -e 'webapp_port=9000' TASK [アプリのポートを確認] ok: [web01] => { "msg": "使用ポート: 9000" } # webapp_portを指定しなかった場合 $ ansible-playbook -i hosts site.yml TASK [アプリのポートを確認] ok: [web01] => { "msg": "使用ポート: 8080" }

default フィルターの第2引数に true を渡すと、変数が定義されていても空文字・nullfalse[] などのFalsy値の場合にデフォルト値を使います。

# 未定義・空文字・falseのいずれでもデフォルト値8080を使う {{ webapp_port | default(8080, true) }}

role/defaults/main.yml との使い分け: roleの defaults/main.yml ですべてのオプション変数にデフォルト値を宣言している場合は、default フィルターは通常不要です。default フィルターが真価を発揮するのは、「タスク内で動的に組み立てた値のフォールバック」や「外部変数ファイルから読み込んだ値に対する追加の防御」が必要な場面です。
Jinja2フィルターを含むAnsible実務設計を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

型変換フィルター(int・float・bool・string)の使い分け

インベントリのINI形式は値をすべて文字列として渡します。算術演算・条件比較・モジュールの引数に渡す前に、型変換フィルターを通す習慣をつけると型エラーを防げます。

# hosts(INI形式インベントリ) [webservers] web01 app_workers=4 webapp_port=8080 use_ssl=yes

上記の app_workerswebapp_portuse_ssl はすべて文字列として渡されます。

# tasks/main.yml --- - name: ワーカー数の計算 debug: msg: "総接続数上限: {{ app_workers | int * 100 }}" - name: SSL設定の判定 debug: msg: "SSL: {{ 'enabled' if use_ssl | bool else 'disabled' }}"

実行結果:

TASK [ワーカー数の計算] ok: [web01] => { "msg": "総接続数上限: 400" } TASK [SSL設定の判定] ok: [web01] => { "msg": "SSL: enabled" }

主要な型変換フィルター:

| int:文字列→整数変換。変換できない場合は 0 を返す
| int(default=1):変換失敗時のデフォルト整数を指定可能
| float:文字列→浮動小数点変換
| bool:"true"/"yes"/"1"/1 → True、"false"/"no"/"0"/0 → False(大文字も可)
| string:整数・リスト等を文字列に変換

bool フィルターはYAMLの "yes""True" といった文字列を正しく真偽値に変換します。when: use_ssl と書いた場合、"yes" は文字列として評価されて常に真になりますが、when: use_ssl | bool と書くと正しく True として評価されます。

文字列操作フィルター(upper・lower・replace・regex_replace)

ホスト名・環境名・ファイルパスを動的に生成するroleでは、文字列操作フィルターで命名規則を統一できます。

--- - name: ログディレクトリのパスを生成 vars: app_name: "MyWebApp" environment: "Production" debug: msg: "ログ先: /var/log/{{ app_name | lower }}/{{ environment | lower }}" # 出力: "ログ先: /var/log/mywebapp/production"

複数の命名規則が混在するインベントリを正規化するパターン:

# "prod"・"production"・"PROD" をすべて "production" に統一 {{ env_name | lower | regex_replace('^prod(uction)?$', 'production') }}

実行結果(3パターン確認):

$ ansible -i hosts localhost -m debug \ -a "msg={{ 'prod' | lower | regex_replace('^prod(uction)?$', 'production') }}" ok: [localhost] => { "msg": "production" } $ ansible -i hosts localhost -m debug \ -a "msg={{ 'PROD' | lower | regex_replace('^prod(uction)?$', 'production') }}" ok: [localhost] => { "msg": "production" } $ ansible -i hosts localhost -m debug \ -a "msg={{ 'staging' | lower | regex_replace('^prod(uction)?$', 'production') }}" ok: [localhost] => { "msg": "staging" }

主要な文字列操作フィルター:

| upper:全文字を大文字
| lower:全文字を小文字
| capitalize:先頭文字のみ大文字
| replace('old', 'new'):固定文字列の置換
| regex_replace('正規表現', '置換文字列'):正規表現での置換
| trim:前後の空白を削除
| truncate(30):指定文字数で切り詰め(末尾に "..." が付く)

フィルターをパイプで連結できるのがJinja2の強みです。| lower | trim | regex_replace(...) のように、複数のフィルターを順番に適用できます。

リスト・ディクショナリ操作(select・map・reject・combine)

複数ホストへの設定配布でリストや辞書を動的に加工するフィルターを活用すると、複雑な条件分岐をシンプルに書けます。

select・reject フィルター: リストから条件に合う/合わない要素を抽出する

--- - name: 1000以上のポートのみ取得 vars: all_ports: [80, 443, 8080, 8443, 22, 3306] debug: msg: "{{ all_ports | select('ge', 1000) | list }}" # 出力: "[8080, 8443]"

ge はgreater than or equal(以上)を意味するJinja2のテスト名です。gt(超)・lt(未満)・le(以下)・eq(等しい)も使えます。

map フィルター: リストの各要素を変換する

--- - name: サーバー名にドメインを付ける vars: servers: ["web01", "web02", "db01"] debug: msg: "{{ servers | map('regex_replace', '$', '.example.com') | list }}" # 出力: "['web01.example.com', 'web02.example.com', 'db01.example.com']"

combine フィルター: 辞書をマージする。roleの defaults/main.yml の設定をユーザー側の変数で上書きするパターンに使います。

--- - name: デフォルト設定とユーザー設定をマージ vars: default_config: port: 80 timeout: 30 debug: false user_config: port: 8080 debug: true debug: msg: "{{ default_config | combine(user_config) }}" # 出力: "{'port': 8080, 'timeout': 30, 'debug': True}" # user_configで上書きされたport・debugだけが変わり、timeoutはデフォルト値が維持される

dict2items フィルター: 辞書をloop処理できる形式に変換する

--- - name: パッケージをバージョン指定でインストール vars: packages: nginx: "latest" php-fpm: "8.2" mariadb: "10.11" ansible.builtin.dnf: name: "{{ item.key }}" state: "{{ item.value }}" loop: "{{ packages | dict2items }}"

辞書を dict2items[{'key': 'nginx', 'value': 'latest'}, ...] の形式に変換してから loop に渡すと、辞書をそのままループできます。

ternaryと条件制御フィルターのパターン

ternary フィルターは三項演算子をJinja2で実現します。when がタスクの実行可否を制御するのに対して、ternary はタスクを実行しつつ引数の値だけを環境によって切り替える設計に使います。

--- - name: ログレベルを環境別に設定 vars: is_production: true debug: msg: "ログレベル: {{ is_production | bool | ternary('warn', 'debug') }}" # 出力: "ログレベル: warn"(is_production が true なので)

ternary はテンプレートファイル(.j2)の中で特に使い勝手が良く、設定ファイルの値を環境によって切り替える場面に頻繁に登場します。

# templates/nginx.conf.j2 worker_processes {{ ansible_processor_vcpus | default(1) }}; error_log /var/log/nginx/error.log {{ nginx_log_level | lower | default('warn') }}; gzip {{ nginx_enable_gzip | default(true) | bool | ternary('on', 'off') }};

defined テスト(フィルターではなくテスト)との組み合わせ: 変数が定義されているときだけ実行する設計

--- # backup_enabledが未定義か false なら when を満たさずスキップ - name: バックアップ先を確認 debug: msg: "バックアップ先: {{ backup_path | default('/var/backup') }}" when: backup_enabled | default(false) | bool

backup_enabled が未定義の場合は false をデフォルト値として使い、bool で変換してから when で評価します。「未定義変数でのエラーを防ぎつつ、条件分岐も安全に書く」という2つの問題をフィルターの連鎖で解決しています。

role設計でJinja2フィルターを活かす実践パターン

実際のroleでJinja2フィルターを組み合わせた設計例を示します。Webサーバー(Nginx)の設定を管理するroleです。

roleのディレクトリ構成:

roles/ nginx/ defaults/ main.yml # デフォルト変数の宣言(型も明示) tasks/ main.yml # フィルターを使った処理 templates/ nginx.conf.j2 # テンプレート内フィルター

defaults/main.yml でデフォルト変数を宣言するとき、型が明確な値で初期化しておくと呼び出し側の混乱を防げます。

# roles/nginx/defaults/main.yml --- nginx_port: 80 # int nginx_worker_processes: 2 # int nginx_enable_gzip: true # bool nginx_log_level: "warn" # string: debug/info/notice/warn/error/crit nginx_allowed_ips: [] # list(空=制限なし) nginx_server_name: "localhost" # string

tasks/main.yml で型変換とバリデーションを組み込む:

# roles/nginx/tasks/main.yml --- - name: ポートをファイアウォールに登録 ansible.builtin.firewalld: port: "{{ nginx_port | int }}/tcp" permanent: true state: enabled - name: ワーカー数を物理コア数上限に調整 ansible.builtin.set_fact: nginx_workers_actual: "{{ [nginx_worker_processes | int, ansible_processor_vcpus | int] | min }}" - name: Nginx設定ファイルを展開 ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' notify: reload nginx

templates/nginx.conf.j2 でのフィルター使用:

worker_processes {{ nginx_workers_actual | int }}; pid /run/nginx.pid; error_log /var/log/nginx/error.log {{ nginx_log_level | lower }}; events { worker_connections {{ nginx_worker_processes | int * 1024 }}; } http { gzip {{ nginx_enable_gzip | bool | ternary('on', 'off') }}; server { listen {{ nginx_port | int }}; server_name {{ nginx_server_name }}; {% if nginx_allowed_ips | length > 0 %} {% for ip in nginx_allowed_ips %} allow {{ ip }}; {% endfor %} deny all; {% endif %} } }

このroleでは型変換・デフォルト値・ternaryを役割別に使い分けています。「defaults/main.yml で型の正解を宣言 → tasks で型変換を挟んで安全に処理 → template でternaryを使って設定値を動的生成」という流れが、Jinja2フィルターを活かしたrole設計の基本パターンです。

本記事のまとめ

Jinja2フィルターの主要パターン早見表です。
フィルター 用途 使用例
default(値) 未定義変数のフォールバック {{ port | default(80) }}
default(値, true) Falsy値もフォールバック対象にする {{ port | default(80, true) }}
int 文字列→整数変換 {{ app_workers | int * 2 }}
bool 文字列→真偽値変換 {{ use_ssl | bool }}
float 文字列→浮動小数点変換 {{ ratio | float }}
string 整数・リスト等→文字列変換 {{ port | string }}
lower 全文字を小文字化 {{ env_name | lower }}
upper 全文字を大文字化 {{ env_name | upper }}
replace('old', 'new') 固定文字列の置換 {{ name | replace('-', '_') }}
regex_replace('rx', 'new') 正規表現での置換 {{ env | regex_replace('^prod$', 'production') }}
trim 前後の空白削除 {{ value | trim }}
select('ge', N) 条件に合う要素を抽出 {{ ports | select('ge', 1000) | list }}
reject('search', 'rx') 条件に合わない要素を抽出 {{ hosts | reject('search', 'maint') | list }}
map('フィルター') 各要素を変換 {{ names | map('lower') | list }}
combine(dict2) 辞書のマージ(後者優先) {{ defaults | combine(overrides) }}
dict2items 辞書→ループ用リストに変換 {{ pkgs | dict2items }}
ternary(T値, F値) 条件によって値を切り替え {{ flag | bool | ternary('on', 'off') }}
Jinja2フィルターは「変数を受け取る段階で変換・加工まで完結させる」という設計の要です。型エラー・未定義変数・命名規則の不一致は、適切なフィルターを組み込むことで事前に防げます。

まず defaultintbool の3つを覚え、実際のroleで使いながら replaceselectternary へと少しずつ拡張していくのが、現場で使えるJinja2フィルター設計の定着方法です。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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