この記事はエーピーコミュニケーションズ Advent Calendar 2022の16日目の記事です qiita.com
やりたいこと
Xmasといったらサンタですね。
大人になってからというものサンタにあっていません。
豊富なモジュールやフィルタがあるAnsibleでも
サンタに会えるモジュール・フィルタはありませんでした。
せっかくなので
自作のフィルタ(カスタムフィルター)を作成してサンタと会いたいと思います。
スクリプト書く
いきなりAnsibleのフィルタにするのではなく、まずはPythonスクリプトでJinja2Templateを呼び出し
その中で自作したXmasフィルタを動かします。
xmas.py
/で区切ったYYYY/MM/DDのデータを受け取り
MMが12、DDが25の場合のみサンタに会えるフィルタを作成します。
import datetime def Xmas(*args, **kwargs): split_arg = args[0].split('/') try: date = datetime.date(int(split_arg[0]),int(split_arg[1]),int(split_arg[2])) if date.month == 12 and date.day == 25: res = ''' [ 内緒 ] ''' return res else: res = ''' サンタかと思ったか?馬鹿め!それは残像だ! -= ∧ ∧ -=と( ・∀・) -=/ と_ノ -=_//⌒ソ ''' return res except IndexError: return args[0]
処理は簡単に、受け取った文字列を/で分割して12月25日を判断しています。
[ 内緒 ]の部分にはサンタのAAが入っていますが、最後の表示の楽しみということにします。
script.py
from jinja2 import Environment, FileSystemLoader from xmas import Xmas if __name__ == '__main__': j2_env = Environment(loader=FileSystemLoader('template/',encoding='utf-8')) j2_env.filters['Xmas'] = Xmas template = j2_env.get_template('stdout.j2') render = template.render(data="2022/12/15") print(render)
j2_env.filter[<フィルタ名>] = 関数と定義することで自作のフィルターをテンプレートで利用可能となります
dataは"2022/12/15"のため、まだサンタに会えないのが想定になります
template/stdout.j2
{{ data | Xmas }}
ディレクトリ内は以下のような配置になっています
. ├── script.py ├── template │ └── stdout.j2 └── xmas.py
スクリプトを実行する

まだサンタには会えていませんが、自作したフィルタが動くことが確認できました。
カスタムフィルターにする
続いて、作成したカスタムフィルタをAnsibleで読み込めるようにします
xmas.py
import datetime
def Xmas(*args, **kwargs):
split_arg = args[0].split('/')
try:
date = datetime.date(int(split_arg[0]),int(split_arg[1]),int(split_arg[2]))
if date.month == 12 and date.day == 25:
res = '''
[ 内緒 ]
'''
return res
else:
res = '''
サンタかと思ったか?馬鹿め!それは残像だ!
-= ∧ ∧
-=と( ・∀・)
-=/ と_ノ
-=_//⌒ソ
'''
return res
except IndexError:
return args[0]
class FilterModule(object):
def filters(self):
return {
'Xmas': Xmas,
}
class FilterModuleを追記します。
関数filtersの戻り値でJinja2で利用可能なフィルタ名とスクリプトないの関数を紐つけています。
書き方はbuiltinのcore.pyを参考にしています
docs.ansible.com
Ansibleで実行する
さて、これでカスタムフィルターは作成できました
続けて実際にPlaybook内で自作したフィルターを呼び出してみたいと思います。
---
- name: Xmas?
hosts: localhost
connection: local
gather_facts: false
vars:
date: 2022/12/15
tasks:
- name: Xmas?
debug:
msg: "{{ date | Xmas }}"
実行してみる

エラーになりました、 自作したカスタムフィルタを使うにはansible.cfgでfilter_pluginsを指定する必要があります
[defaults] filter_plugins = .
再実行

カスタムフィルターは動きましたが、一行で表示されているためせっかくのAAが確認できません
せっかくなのでstdout_callback_pluginをYAMLにします
※yamlコールバックプラグインはcommunity.generalコレクションに入っています。ない場合は事前にインストールします
[defaults] filter_plugins = . stdout_callback = community.general.yaml
再々実行

バッチリですね
インプットは12/15なのでクリスマスではなくサンタには会えませんでした。
幸せになる
未来を先取りして、変数を12/25にします
---
- name: Xmas?
hosts: localhost
connection: local
gather_facts: false
vars:
date: 2022/12/25
tasks:
- name: Xmas?
debug:
msg: "{{ date | Xmas }}"
実行してみます
サンタに会えました。最高です。
おまけ
- ansible.cfgをいじりたくない場合はデフォルトでfilter参照するパスに直接配置します
ansible-config dumps DEFAULT_FILTER_PLUGIN_PATH(default) = ['/Users/kouta/.ansible/plugins/filter', '/usr/share/ansible/plugins/filter']
フィルターを配置
(v_ansible) MBA:CustomFilter_2022 kouta$ cp xmas.py /Users/kouta/.ansible/plugins/filter/xmas.py
- collectionにして配布したい
ansible-collectionにして配布したい場合は、
ansible-galaxy collection init <コレクション名>で生成されたディレクトリのplugins/filter配下に自作のフィルターを配置します
usalab.xmasコレクションを作成し、スクリプトを配置しました
コレクションを配置したパスをansible.cfgで指定します。
指定したパスのディレクトは以下のような構成になっている必要があるようです
<指定したパス>/ansible_collection/<自作したコレクション>
[defaults] filter_plugins = filter_plugins stdout_callback = community.general.yaml collections_paths = collections
せっかくなので、filterに引数が追加で渡されるパターンで拡張してみます
利用イメージとしては、以下のようにフィルターを使うにあたって2つの引数がある(1つめdate,2つめ'santa')
{{ date | Xmas('santa') }}
第2引数の文字列がsantaもしくはreindeer(トナカイ)の文字列に応じて返す値を変えます
import datetime
def Xmas(*args, **kwargs):
split_arg = args[0].split('/')
key = args[1]
try:
date = datetime.date(int(split_arg[0]),int(split_arg[1]),int(split_arg[2]))
if date.month == 12 and date.day == 25:
if key == 'santa':
res = '''
[内緒]
'''
elif key == 'reindeer':
res = '''
[内緒]
'''
return res
else:
res = '''
サンタかと思ったか?馬鹿め!それは残像だ!
-= ∧ ∧
-=と( ・∀・)
-=/ と_ノ
-=_//⌒ソ
'''
return res
except IndexError:
return args[0]
class FilterModule(object):
def filters(self):
return {
'Xmas': Xmas,
}
プレイブックは以下のように記載します usalab.xmasにあるXmasフィルタを呼び出します
---
- name: Xmas?
hosts: localhost
connection: local
gather_facts: false
vars:
date: 2022/12/25
tasks:
- name: Xmas?_サンタ
debug:
msg: "{{ date | usalab.xmas.Xmas('santa') }}"
- name: Xmas?_トナカイ
debug:
msg: "{{ date | usalab.xmas.Xmas('reindeer') }}"
バッチリ動きますね

※ansible.cfgでcollections_pathを指定した場合、指定したパスに存在するコレクションのみが対象になるようです、コールバックプラグインで利用しているcommunity.generalコレクションもcollections_pathで指定したディレクトリに含める必要があります。