EBSのタグをアタッチされているインスタンスのものに同期させる

DBのスナップショットを手で作成するときや,CloudFormationでインスタンスを作成するときに,EBSにタグを付け忘れることがある.

そんなときのために,アタッチされているインスタンスに対して自動でタグを付けられるスクリプトを書いた.

import boto3
import os


def handler(event, context):
    synced_tag_keys = os.environ['SYNCED_TAG_KEYS'].split(',')

    client = boto3.client('ec2')

    ebs_messages = ''

    instance_tags_dict = get_instance_tags(client)

    response = client.describe_volumes()
    for volume in response['Volumes']:
        volume_id = volume['VolumeId']

        # アタッチされていないEBSボリュームは無視
        if not volume_id in instance_tags_dict:
            continue

        # ボリュームIDからインスタンスのTagsを取得
        instance_tags = instance_tags_dict[volume_id] 

        # ボリュームIDからボリュームのTagsを取得
        volume_tags = tags_array_to_dict(volume.get('Tags'))
        
        # 同期させたタグを通知するため覚える
        tags_to_notify = {}

        for synced_tag in synced_tag_keys:
            instance_tag_value = instance_tags.get(synced_tag)
            # 指定したタグがインスタンスに存在しない場合
            if instance_tag_value is None:
                continue

            # ボリュームにタグがない場合もしくはインスタンスのタグと違う場合,インスタンスのタグに合わせる
            if volume_tags.get(synced_tag) != instance_tag_value:
                create_tags(client, volume_id, synced_tag, instance_tag_value)
                tags_to_notify[synced_tag] = instance_tag_value

        if tags_to_notify != {}:
            ebs_messages += volume_id + '(' + str(instance_tags.get('Name')) + '): ' + str(tags_to_notify) + '\n'

    message = '(' + context.function_name + ') ' + '同期するタグ: ' + str(synced_tag_keys) + '\n'

    if ebs_messages != '':
        message += '以下にあるボリュームのタグを同期しました\n' + ebs_messages
    else:
        message += 'タグの同期が必要なボリュームはありません'

    print(message)


# volumeIdとTagsの辞書を紐づけた辞書を返す
def get_instance_tags(client):
    response = client.describe_instances()
    instance_tags = {}

    for res in response['Reservations']:
        for instance in res['Instances']:
            for mapping in instance['BlockDeviceMappings']:
                volume_id = mapping['Ebs']['VolumeId']
                tags_dict = tags_array_to_dict(instance.get('Tags'))
                instance_tags[volume_id] = tags_dict

    return instance_tags


def tags_array_to_dict(tags_array):
    if tags_array is None:
        return {}

    tags_dict = {}

    for tag in tags_array:
        tags_dict[tag['Key']] = tag['Value']

    return tags_dict


def create_tags(client, volume_id, tag_key, tag_value):
    client.create_tags(
        Resources=[
            volume_id,
        ],
        Tags=[{
            'Key': tag_key,
            'Value': tag_value,
        }]
    )

EBSボリュームのidからアタッチされているインスタンスを検索して,環境変数SYNCED_TAG_KEYSで指定したタグを同期させる.

Lambdaでの実行を想定しているので,CloudWatch などで定期実行すれば,自動的に同期することができる.