Ansibleのloopキーワードで、任意のループ数以上の場合のみ処理を行いたい(小ネタ)
本記事の背景
AWSのEC2インスタンスのAMI取得をAnsibleで実施しようと実装を進めていたところ、AMI取得ついでに世代管理も合わせて行えるようにしてしまおう!と思い立ちました。
パッと思いついた流れは、
- AMIの一覧を取得し、結果をregisterキーワードで変数に格納
- 一覧から、定義した保持世代数以上に該当するAMIを登録解除
という、実にシンプルな流れです。
はて、「保持世代数以上に該当するAMI」をどうやって判別するか・・・?
loopキーワードを用いて実装した検証結果をご紹介させていただきます。
検証してみる
検証環境の情報です。
Playbook
--- - hosts: all connection: local gather_facts: no vars: ami_keep_generation: 3 tasks: - name: Get AMI list ec2_ami_facts: filters: name: "{{ hostname }}*" region: "{{ aws_region }}" register: return_ami_list - name: Sort AMI list and set to variable set_fact: ami_list: "{{ return_ami_list.images | sort(attribute='creation_date',reverse=True) }}" - name: (debug) Display AMI list debug: msg: "Name: {{ item.name }}, Id: {{ item.image_id }}, CreationData: {{ item.creation_date }}" loop: "{{ ami_list }}" loop_control: label: "{{ item.name }}" - name: (debug) Loop AMI list debug: msg: "{{ item.name }}" when: ansible_loop.index > ami_keep_generation loop: "{{ ami_list }}" loop_control: label: "{{ item.name }}" extended: yes
varsセクション内で変数ami_keep_generationを定義し、保持したい世代数を指定します。
以下、一部のタスクを解説します。
Get AMI list
filterのnameにホスト名を指定し、指定したホスト名が含まれるAMIのみ取得するようにしています。
※AMI名は <ホスト名>-yyyymmdd-hhmm としています。
Sort AMI list and set to variable
ec2_ami_factsでAMIの一覧を取得し、作成日時順かつeverse=Trueを指定して逆順にソートするようにしています。
「指定した世代数以上の場合に処理を行う」と決めていたため、AMIが作成された日時順にすることを目的としています。
(debug) Loop AMI list
このタスクが本題です。
loop_controlにてextended: yesを指定すると、ループのカウントを取得できます。
ループカウントは変数ansible_loop.indexに格納されているため、これをwhenで評価します。
条件判定でTrueとなった場合(保持世代数以上に該当した場合)、メッセージでAMIを出力するようにしています。
実行結果
$ ansible-playbook -l webserver-01 GetAMIList.yml PLAY [all] ********************************************************************************************************************************************************************************************************************************** TASK [Get AMI list] ************************************************************************************************************************************************************************************************************************* ok: [webserver-01] TASK [Sort AMI list and set to variable] **************************************************************************************************************************************************************************************************** ok: [webserver-01] TASK [(debug) Display AMI list] ************************************************************************************************************************************************************************************************************* ok: [webserver-01] => (item=webserver-01-20191016-0512) => { "msg": "Name: webserver-01-20191016-0512, Id: ami-xxxxxxxxxxxxxxxxxxx, CreationData: 2019-10-16T05:12:25.000Z" } ok: [webserver-01] => (item=webserver-01-20191016-0509) => { "msg": "Name: webserver-01-20191016-0509, Id: ami-xxxxxxxxxxxxxxxxxxx, CreationData: 2019-10-16T05:09:11.000Z" } ok: [webserver-01] => (item=webserver-01-20191016-0500) => { "msg": "Name: webserver-01-20191016-0500, Id: ami-xxxxxxxxxxxxxxxxxxx, CreationData: 2019-10-16T05:00:40.000Z" } ok: [webserver-01] => (item=webserver-01-20191016-0459) => { "msg": "Name: webserver-01-20191016-0459, Id: ami-xxxxxxxxxxxxxxxxxxx, CreationData: 2019-10-16T05:59:54.000Z" } ok: [webserver-01] => (item=webserver-01-20191016-0441) => { "msg": "Name: webserver-01-20191016-0441, Id: ami-xxxxxxxxxxxxxxxxxxx, CreationData: 2019-10-16T05:41:40.000Z" } TASK [(debug) Loop AMI list] **************************************************************************************************************************************************************************************************************** skipping: [webserver-01] => (item=webserver-01-20191016-0512) skipping: [webserver-01] => (item=webserver-01-20191016-0509) skipping: [webserver-01] => (item=webserver-01-20191016-0500) ok: [webserver-01] => (item=webserver-01-20191016-0459) => { "msg": "webserver-01-20191016-0459" } ok: [webserver-01] => (item=webserver-01-20191016-0441) => { "msg": "webserver-01-20191016-0441" } PLAY RECAP ********************************************************************************************************************************************************************************************************************************** webserver-01 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
AMI名は <ホスト名>-yyyymmdd-hhmm としています。(AMI IDはマスクしています)
TASK [(debug) Loop AMI list]の結果を見ると、3世代以上の場合のみタスクが実行されていることが確認できます。
今回は検証のため実際にAMIの登録解除は行っていませんが、やりたいことを実現できていることは確認できました!
whenとloopを同時に指定した場合、whenが優先されてしまい、動かないのでは・・・?と思い込んでいましたが・・・
実際に手を動かして検証をする大切さを、改めて実感しました。
最後に
実は以前にAnsibleでのAMI世代管理を実施していたのですが、その際はloopキーワードとwhenを組み合わせるという発想がありませんでした。
登録解除したいAMIのリストを作ってしまえばいいじゃない!と思いつき、取得したAMI一覧をjinja2 templateにて無理やりリスト形式に変えて、そのリストをモジュールのパラメータに与える・・・という力技で実装していました。
この情報が役にたつかは不明ですが、こういうこともできます!という意味合いも含めて紹介させていただきます。
- name: Set deregister AMI list to variable set_fact: deregister_ami_list: >- {%- set temp_ami_list = [] -%} {%- for count in range(ami_list | length) -%} {%- if loop.index > ami_retain_generation -%} {%- set _ = temp_ami_list.append(ami_list[count].image_id) -%} {%- endif -%} {%- endfor -%} {{ temp_ami_list }}
行数からすると大したことないですが、少々プログラムチック?な書き方になってしまいました。
個人的には、Ansibleのシンプルさが失われてしまったな〜とモヤモヤしていましたが、その反面、こんなやり方もできるのか・・・と少し感動したのはナイショです。