【AWS】S3に格納したExcelファイルをLambdaで読み込んでEC2にタグ付けする

# はじめに
久しぶりにコードを書く機会があったのでそこで使用したコードの一部をテンプレートっぽく記事として残そうと思います。
エクセルファイルを読み込んで、その他のサービス(SystemsManagerやEventBridgeなど)に対して処理を行う場合のコードとかも
別のタイミングで記事として残そうと思っていますので興味がある方は読んでみてください。

今回の検証では以下のサービスを使用します。
* S3
* EC2
* Lambda(python3.9)

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [処理の概要](#処理の概要)
1. [インプットデータを作成する](#インプットデータを作成する)
1. [IAMロールを作成する](#iamロールを作成する)
1. [Lambda関数を作成する](#lambda関数を作成する)
1. [Iamロールを作成する](#iamロールを作成する)
1. [Lambdaを実行する](#lambdaを実行する)
1. [タグの確認](#タグの確認)
1. [さいごに](#さいごに)


# 処理の概要
S3に格納したエクセルファイルをLambdaで読み込んで、対象のインスタンスにタグを付与します。
言語はPython3.9でモジュールはOpenpyxlを使用します。



<a id="#Chapter2"></a>
# インプットデータを作成する
画像のようなイメージでインプットデータを作成します。
InstanceNameの列は存在するインスタンス名を記載してください。(今回の検証ではあらかじめ３台用意してあります。)
シート名は今回はデフォルトのまま変更せず、「Sheet1」としてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/10ca63e3-72e4-232a-0de2-38c4161ba64a.png)


* S3にアップロードする
「qiita-test」というフォルダを作成し、その中にエクセルファイルをアップロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/70304a84-15fe-c460-e5c9-a191f3de7741.png)
* 対象インスタンスのタグを確認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/29c805e1-f681-8399-1396-b65008048633.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/378789dc-e7f3-eecd-6e2a-d79e02a12ad8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/97c5d511-3402-eae0-41b9-477a2344b93e.png)


# IAMロールを作成する
信頼されたエンティティでLambdaを指定してロールを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/9c33a1a3-80f9-c1ec-2cf2-2b6f3b71af44.png)

* 以下の通り作成します
ロール名
    * Create-EC2-Tag-for-Lambda
    
    ポリシー
    * AmazonS3FullAccess
    * AmazonEC2FullAccess
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/be3a767f-0443-24ab-efc0-c3f735627a51.png)


# Lambda関数を作成する
先ほど作成したIAMロールを指定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/64221560-35cb-ae17-82fd-c2a1b38a6c9b.png)

ソースコード
※１行ずつに対して処理を実行するので大量のインスタンスに対して実行する場合はいい感じに書き換えて使ってください（笑）
(APIの書き方サンプルだと思っていただければ)
``` create_tag.py
import json
import boto3
import openpyxl
from io import BytesIO

s3_client = boto3.client('s3')
ec2_client = boto3.client('ec2')
bucket_name = "バケット名"

#指定したインスタンスIDにタグを付与
def create_tags(instance_id,tag_key,tag_value):
    response = ec2_client.create_tags(
        Resources = [
            instance_id
            ],
        Tags=[
            {
                'Key': tag_key,
                'Value': tag_value
            },
        ]
    )

#インスタンス名からインスタンスIDを取得
def get_instanceid(instance_name):
    response = ec2_client.describe_instances(Filters=[{'Name':'tag:Name','Values':[instance_name]}])
    for reservation in response['Reservations']:
      for instance in reservation['Instances']:
        # インスタンスIDを取得
        instance_id = instance.get('InstanceId')
        return instance_id

#エクセルファイルを1行ずつ読み込んで処理
#インスタンス名からインスタンスIDを取得し、対象のインスタンスにタグを付与
def lambda_handler(event, context):
    object_key = event["excel-file"]
    s3_resp = s3_client.get_object(Bucket=bucket_name, Key=object_key)
    wb = openpyxl.load_workbook(BytesIO(s3_resp['Body'].read()),data_only=True)
    main_ws = wb["Sheet1"]

    #エクセルファイルを読み込む
    for row in main_ws.rows:
        if row[0].row == 1:
            # １行目
            header_cells = row
        else:
            # ２行目以降
            row_dic = {}
            # セルの値を「key-value」で登録
            for k, v in zip(header_cells, row):
                row_dic[k.value] = v.value
    
            print("エクセルファイルから１行ずつ読み込んだ結果:{}".format(row_dic))
            
            #インスタンス名からインスタンスIDを取得
            instance_id = get_instanceid(row_dic["InstanceName"])
            
            #取得したインスタンスIDにタグを付与
            tag_key = row_dic["TagKey"]
            tag_value = row_dic["TagValue"]
            create_tags(instance_id,tag_key,tag_value)
            
    print("タグ付けが完了しました")
```

レイヤーにOpenpyxlモジュールを追加します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/8f85582a-d11f-a9e1-bf3d-a1feacdd3dab.png)

# Lambdaを実行する
テストイベントを定義し保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/0398bff1-eb6d-212d-dcd8-ebae06cf2bdf.png)

テストを実行します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/1539b0f6-c9bc-a4c6-c4ac-e3057ab50c64.png)

成功しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/afe3b9b8-75ff-586d-b688-1edd919776d6.png)


# タグの確認
対象インスタンス３台に対してエクセルファイル内で指定したタグが付与されていることが確認できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/2bde34c6-ea8e-5edf-37ba-661758fbc596.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/2c47e774-d293-ef94-2185-f22ce9cc7824.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615865/b2af4b9f-9d74-3fd1-5da0-a77ce146da9a.png)

# さいごに
今回はLambdaを使ってEC2にタグを付与してみました。
Openpyxlのモジュールの使い方の勉強にもなったので検証できてよかったです。

とりあえず動くものをと思って作ったので見返すと無駄が多いコードになってしまっていますが。。。。
基本的なboto3を使ったAPIの呼び出しのサンプルとして誰かの役に立てれば幸いです。
(大量のインスタンスに対して処理する際はインスタンスIDをリスト化したり、エラー処理を挟んだほうがいいと思うので、いい感じに書き換えて使ってください笑)

最後まで読んでいただきありがとうございました。