[AWS]EC2のタグ付けLambdaをServerlessFrameworkで実装してみた

##TL;DR
- 検証環境など複数メンバーで利用していると、管理者不明のリソースが発生することがある。
- LambdaでEC2起動時にタグ付けする解決策が一般的だと思うが、  
複数の環境/リージョンがあると都度登録するのも面倒。
- ServerlessFramework を使って誰でも簡単に実装できるようにした。   

※本稿ではServerlessFrameworkの導入手順は触れていないので
　導入方法は公式ドキュメントを参照してください。

##1. Lambda関数の準備
- 起動者の情報をタグ付けするLambda関数ファイル(コードは後述)を用意します。
- 今回はIAMユーザーとAssumeRoleしているユーザーに対応させています。

```python:handler.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import boto3
import logging
import os

# set the simple logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
region = os.environ['AWS_REGION']
account = os.environ['AWS_ACCOUNT']


def ec2_tagging(event, context):
    logger.info(event)

# Requestor Check
    userType = event['detail']['userIdentity']['type']
    if userType == 'IAMUser':
        Owner = event['detail']['userIdentity']['userName']
    elif userType == 'AssumedRole':
        roleID = event['detail']['userIdentity']['sessionContext']['sessionIssuer']['userName']
        Owner = (event['detail']['userIdentity']['arn'].replace("arn:aws:sts::" + str(account) + ":assumed-role/"+str(roleID)+"/", ""))
    logger.info("Owner: " + Owner)

# Target Resource Check
    instanceIDs = event['detail']['responseElements']['instancesSet']['items']
    logger.info(instanceIDs)
    instance_count = len(instanceIDs)
    try:
        for i in range(0, instance_count):
            instanceID = instanceIDs[i]['instanceId']
            logger.info("target: " + str(instanceID))
            ec2 = boto3.client('ec2')
            r = ec2.create_tags(
                Resources=[instanceID],
                Tags=[
                   {
                        'Key': 'Owner',
                        'Value': Owner
                    },
                ]
            )
    except Exception as e:
        logger.error("error: " + str(e))
```
##2. Lambda実行用のIAM Role作成
- Lambda実行に必要となる下記権限を有するIAMロールを準備します。
  - CloudwachLogへのログ書き込みの権限
  - EC2にタグを付与する権限

```json
  {
      "Version": "2012-10-17",
      "Statement": [
          {
              "Action": [
                  "logs:CreateLogStream",
                  "logs:PutLogEvents"
              ],
              "Resource": [
                  "arn:aws:logs:*"
              ],
              "Effect": "Allow"
          },
          {
              "Action": [
                  "ec2:CreateTags"
              ],
              "Resource": [
                  "*"
              ],
              "Effect": "Allow"
          }
      ]
  }
```

##3. serverless.ymlの作成
- serverless.ymlでLambda関数の設定を定義しています。
- Lambda関数ファイルがあるディレクトリにserverless.yml(内容は後述)を配置します。
- 環境に応じて修正が必要なパラメータは`custom`ブロックにまとめましたので、  
AWS AccountIDやリージョン、手順2で作成したIAMロール名等を修正してください。

```yml:serverless.yml
service: ${self:custom.service}

provider:
  name: aws
  runtime: python3.6
  stage: ${opt:stage, self:custom.defaultStage}
  region: ${opt:region, self:custom.defaultRegion}
  memorySize: 128
  timeout: 60
  role: arn:aws:iam::${self:custom.account}:role/${self:custom.iam}
  environment:
    AWS_ACCOUNT: ${self:custom.account}

custom:
  account: hoge        ## AWS AccountIDを入力してください。
  defaultStage: dev
  defaultRegion: fuga  ## Regionを入力してください。(e.g.ap-northeast-1)
  description: EC2 Auto Tagging Function
  service: Ec2AutoTag
  iam: piyo            ## Lambdaを実行するIAM Role名を入力してください。

functions:
  Ec2AutoTag:
    name: ${self:custom.service}
    handler: handler.ec2_tagging
    description: ${self:custom.description}
    events:
      - cloudwatchEvent:
          event:
            source:
              - "aws.ec2"
            detail-type:
              - "AWS API Call via CloudTrail"
            detail:
              eventSource:
                - "ec2.amazonaws.com"
              eventName:
                - "RunInstances"
```
##4. deploy
Lambda関数ファイルがあるディレクトリにてデプロイコマンド `sls deploy`を実行するだけです。

```
$ ls -l
total 8
-rw-rw-r-- 1 ubuntu ubuntu 1453 Aug 29 02:56 handler.py
-rw-rw-r-- 1 ubuntu ubuntu 1012 Aug 29 03:00 serverless.yml
$
$ sls deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (774 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...................
Serverless: Stack update finished...
Service Information
service: Ec2AutoTag
stage: dev
region: ap-northeast-1
stack: Ec2AutoTag-dev
api keys:
  None
endpoints:
  None
functions:
  Ec2AutoTag: Ec2AutoTag-dev-Ec2AutoTag
```
##5. 確認
インスタンスを起動してみて、起動者のユーザー名が付与されることを確認できました。
![テスト結果.png](https://qiita-image-store.s3.amazonaws.com/0/224467/ae42eeb0-2fc0-32bc-1577-2af1376ebe5b.png)

##6. 最後に
- ServerlessFrameworkでLambdaの登録は簡単にできる。
- StackSetに対応していれば、マルチリージョンに一度にデプロイできて、  
より便利に使えそう。(これから調査:sweat_drops:)

##参考
- [Serverless Framework Document](https://serverless.com/framework/docs/)
- [Serverless Framework Github](https://github.com/serverless/serverless)
- [Serverless Frameworkの使い方まとめ](https://qiita.com/horike37/items/b295a91908fcfd4033a2)