aws向けAnsible自作モジュールを作る


概要

AWS向けのAnsible自作モジュールを公式のAWSモジュールに見習って作り方を学びたいと思います
ちなみに公式のAWSモジュールのソースコードはこちらで確認できます

自作したモジュールの置き場はplaybookと同じディレクトリ内にlibraryディレクトリに配置すれば自動的に読み込まれます
それ以外は環境変数ANSIBLE_LIBRARY、コマンドパラメータ--module-path、ansible.cfgのlibraryで指定することができます

playbook.yml
library/
  |-----my_lib_hoge.yml
  |-----my_lib_fuga.yml

さっそくコード

以下ではアプリケーションロードバランサーの情報を取得するコードになります
細かい説明はコード内のコメントを参照ください

my_describe_elbv2.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

DOCUMENTATION = '''
module: qiita sample module
short_description: describe elb v2 by elb name
description:
    - Gather facts about EC2 Elastic Load Balancers V2 in AWS
version_added: "2.4"
author:
    - "comefigo"
options:
  names:
    description:
      - List of ELB V2 names
    required: True
    default: null
'''

# Ansibleモジュールのおまじない
from ansible.module_utils.basic import AnsibleModule

# awsとのやりとりに便利な機能を提供してくれるutility
from ansible.module_utils.ec2 import (
    boto3_conn,
    boto_exception,
    ec2_argument_spec,
    get_aws_connection_info,
    snake_dict_to_camel_dict,
    camel_dict_to_snake_dict
)

def main():
    # aws_access_key, aws_secret_key, ec2_url などのパラメータ定義が取得できる
    fields = ec2_argument_spec()

    # 取得したパラメータにnamesというパラメータ定義を追加します(dict型)
    fields.update(dict(
        names={'required': True, 'type': 'list'}
    ))

    # このモジュールで必要なパラメータを引数にmoduleを生成
    module = AnsibleModule(argument_spec=fields)

    result = {}

    try:
        # aws接続用に必要なパラメータを取得
        region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)

        # 上記で取得した値でboto3クライアントを生成
        elbv2 = boto3_conn(module, conn_type='client', resource='elbv2', region=region, endpoint=ec2_url, **aws_connect_kwargs)

        ############################################################
        #  boto3のクライアントを取得できたのであとは好きなように実装してください
        #
        # describe ELB Application Load Balancer
        ret = elbv2.describe_load_balancers(Names=module.params['names'])

        # 結果を追加
        # キャメルケースからスネークケースに変換
        result.update(camel_dict_to_snake_dict(ret))

    except Exception as err:
        import traceback
        error_msg = boto_exception(err)

        # 異常時はmodule.fail_jsonに戻り値を出力
        module.fail_json(msg=error_msg, exception=traceback.format_exc())

    # 正常時はmodule.exit_jsonに戻り値を出力
    module.exit_json(**result)

if __name__ == '__main__':
    main()

使い方

ファイル名がそのままモジュール名になります

- my_describe_elbv2:
    aws_access_key: xxxxxxxxxx
    aws_secret_key: xxxxxxxxxx
    ec2_url: yyyyyyy
    names:
      - my-elb1
      - my-elb2

ポイント

module_utils.ec2で提供されているAWS接続用の各種機能を使うことで簡単に実装できます
boto3のクライアントが生成された後はboto3に従って実装できますので、好き勝手に作れます

ec2_argument_spec

AWS関連のモジュールで必要なaws_access_keyaws_secret_keysecurity_tokenec2_urlprofilevalidate_certsregion のモジュール用のパラメータ定義が取得できる

boto3_conn

boto3のコネクションオブジェクトが取得できる

snake_dict_to_camel_dict、camel_dict_to_snake_dict

AWSからのJSONレスポンスのキー値はキャメルケースになっているため、pythonのコード規約に合わせてスネークケースに変換する時にcamel_dict_to_snake_dictを使用します
また、その逆の時はsnake_dict_to_camel_dictを使用します

required_if(サンプルコードでは未使用)

特定の条件下でパラメータを必須とする時はrequired_ifで指定します
例) stateがpresentの時にarg1とarg2を必須とする

module = AnsibleModule(argument_spec=fields,
                       required_if=[('state', 'present', ['arg1', 'arg2'])])

required_together(サンプルコードでは未使用)

特定のパラメータを使用する時に関連するパラメータを必須指定しなければならない時にrequired_togetherを指定します
例) arg1とarg2とarg3はセットで指定しなければならない

module = AnsibleModule(argument_spec=fields,
                       required_together=[['arg1', 'arg2', 'arg3']])

リトライを実装しましょう!

上記のコードにはないですが、リトライ処理は必ず実装しましょう!
理由としては、AWSのみならず共通して言えるのがAPIをコールしてなにかする系のモジュールでは、コール先がコール制限(1秒間にコールできる数)やネットワークの品質によるエラーになることが割とあります。そのたびに処理が中断されるのもつらいので、冪等性を担保しつつリトライ処理を実装しましょう!(※リトライ処理した分だけリソースが追加されないように)

いかがでしょうか
かなり簡単に実装できたのではないでしょうか?

Enjoy Asinble!!