LoginSignup
0
0

More than 1 year has passed since last update.

EC2の一覧を必要な情報だけ絞ってS3へ日次保管

Posted at

実装の方法

LambdaのPython(boto3)を使います。
1日1回実行するトリガーとして、EventBridgeを使います。

実装できると何がうれしいのか

1点目
EC2のdescribe_instances APIは、取得結果が冗長なデータ構造となっていて、Javascriptなどで取り込んだ時にやや扱いにくいです。
特に、Reservations[*].Instances[*] の階層の深さと、タグの構造が [{'Key': 'タグキー名' }, {'Value': 'タグの値}] となっている点が冗長でしょうか。
階層を単純な1次元配列(Pythonのリスト)とし、欲しい情報だけに絞ることで、プログラムから扱いやすくなります。

2点目
AWSのAPI/CLIでは、”現時点の”EC2の一覧を取得できますが、”x月x日時点の”EC2の一覧を取得したいとなった場合に、急に困ることになります。
1日1回Lambdaを実行して蓄積しておくことで、簡単に調べられるようになります。

IAMロールの作成

IAMロールを作成します。
最低でも、以下の4つのポリシー(IAMポリシー or インラインポリシー)を含んだIAMロールを作成しましょう。

  • EC2 の DescribeInstances
  • S3 の PutObject
  • S3 の GetObject
  • S3 の ListBucket

S3バケットの作成

S3バケットを作成します。
特にデフォルト設定で構いません。

Lambdaの概要

  • EC2の一覧取得のAPI(describe_instances)を実行してEC2の情報を取得します。
  • 深いデータ構造となっているのをシンプルな構造(1次元配列のインスタンス一覧)にします。
  • APIの結果から必要な属性情報だけを切り出します。今回は、インスタンスID、タグ、サブネットID、インスタンスタイプ、の4つが必要情報の例としています。
  • タグのデータ構造を [{"Key":"Name", "Value":"Server1"}, ..] から {"Name":"Server1", ..} とシンプルにします。
  • S3へ日付つきファイル名(キー名)と日付無しファイル名(キー名)の2つを保管します。
  • 日付つきは ec2lite_YYYYMMDD.json、日付無しは ec2lite.json の名前とします。

Lambdaの作成

Lambdaを作成します。
言語は Python で、3.8 にします。
EC2の数が多い場合は、デフォルト設定値の 3秒 では間に合わない可能性があります。その場合は 10秒 などに増やしましょう。
lambda_function.py を以下のコードに置き換えます。
'your_bucket_name'は自身のバケット名にします。

lambda_function.py
import json
import boto3
from datetime import datetime, time, timedelta, timezone

ec2 = boto3.client('ec2')
s3 = boto3.resource('s3')
# ↓↓ 正しいバケット名に書き換えてください ↓↓
s3_bucket = 'your_bucket_name'

# APIのデータにはJsonに存在しない日付型を含むため文字列に変換する
def datetime_json_parse(dict):
    return dict.isoformat() if isinstance(dict, (datetime, date)) else dict

def lambda_handler(event, context):
    ec2_lite_json = []
    # ファイル名(オブジェクトキー名)1
    s3_obj_path1 = 'ec2lite.json'
    # ファイル名(オブジェクトキー名)2  +9時間で日本時間に
    s3_obj_path2 = datetime.now(timezone(timedelta(hours=9))).strftime('ec2lite_%Y%m%d.json')

    try:
        ret_instances = ec2.describe_instances()
    except Exception:
        return { 'result': 'ng' }

    for rsv in ret_instances.get('Reservations'):
        for inst in rsv['Instances']:

            tags_new = {}
            # シンプルなタグの構造に変換
            # "or []" はタグのないEC2でNoneType Errorとなる対策
            for tag in inst.get('Tags') or []:
                tags_new[ tag['Key'] ] = tag['Value']

            # 必要な情報に絞ったデータを生成
            # SubnetIdのみTerminated中のEC2でKey Errorとなるためget()
            ec2_lite_json.append(
                {
                'InstanceId' : inst['InstanceId'],
                'InstanceType' : inst['InstanceType'],
                'Tags' : tags_new,
                'SubnetId' : inst.get('SubnetId')
                }
            )

    # キー1はputして保管、キー2はキー1からコピーして保管
    s3.Object(s3_bucket, s3_obj_path1).put(
        Body = json.dumps(ec2_lite_json, ensure_ascii=False, default=datetime_json_parse)
    )
    s3.Object(s3_bucket, s3_obj_path2).copy_from(CopySource={'Bucket': s3_bucket, 'Key': s3_obj_path1})

    return { 'result': 'ok' }

EventBridgeの作成

EventBridgeを作成します。
マネージメントコンソールで操作する場合、Lambdaのコンソール上のままでトリガーの追加ボタンを押してEventBridgeを選択しても新規のルール作成ができます。
cron式で rate(1 day) とすると日次で実行ができます。

もっとうれしいこと

サーバー運用していると、EC2インスタンスを終了(削除)したのは何月何日かをさかのぼって知りたい場合がありますが、消えてしまったEC2に対してAWSでは簡単に調べることができません。

このLambdaの実装が前提として、もう1つ別のLambdaを作成し、そのLambdaで生成したJsonを見るだけで簡単にEC2削除日を調べることができる、という展開も可能になります。
(別の記事で記載予定です)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0