Lambdaでインスタンス作成時に自動タグ付け 

## 目的 
EC2を起動する際にデフォルトではNameタグはあるけど、それだけだと誰が作成したかわからなくなってしまいます。
当社でもインスタンスの数が増えて、「これだれが作ったの？消していい？」みたいな会話が日常茶飯事です。 
そこで、今回は以下記事を参考に、最近学んだGeneratorを使って書き直してみました！ 

[AWS LambdaでEC2インスタンスに作成者を自動タグ付け](http://qiita.com/khiraiwa/items/2e43d0f07b582ccb3e78) 
[LambdaでEC2作成者をタグ付けする](http://dev.classmethod.jp/cloud/aws/lambda_create_tags/) 

## Javascriptの非同期処理 
最近現場でJavascriptという言語を使ってコードを書かなければならなくなったのでLambdaからDynamoDBにQueryしてその結果を使ってー。。。みたいな処理を書いていました。 
ところが、Javascriptの非同期処理のせいで、Queryが終わる前にその先の処理を実行してしまうんですね！ 

[非同期処理ってどういうこと？JavaScriptで一から学ぶ](http://qiita.com/kiyodori/items/da434d169755cbb20447) 
[JavaScriptコールバックを整理してみた【再入門】](http://qiita.com/nekoneko-wanwan/items/f6979f687246ba089a35) 

## Generatorとは？ 
以下の記事が参考になります。
[ES6 Generatorを使ってasync/awaitを実装するメモ](http://blog.h13i32maru.jp/entry/2015/01/02/225457)

>ES6のGeneratorとは「任意の時点で処理を中断/再開することができる関数」というもの。一般的にはコルーチン(coroutine)と呼ばれるもので、サブルーチン(通常の関数)を一般化したもの。ES6でGeneratorを理解するには3つのキーワードがある。

>- Generator Function
> - 処理の中断/再開が行われる特殊な関数
> - ```function* generatorFunction(){}```のように```function*```を使って定義する
>- Generator Object
> - 中断された処理を再開したり、値を取得し対するオブジェクト
> - ```var generatorObject = generatorFunction()```のように取得する
>- yield
> - Generator Functionの中で使われる処理の中断を指定するキーワード
> - ```var result = yield request('http://example.com')```のように使う

## コード 
以下が作成したコードです。

```node.js:index.js 
/* ====================================================================== */
/**
 *  インスタンス起動時に自動でタグつけるやつ 
 *
 */
/* ====================================================================== */
'use strict';

console.log('Loading function');
var aws  = require('aws-sdk');
var region_name;

exports.handle = function(event, context) {

	var recordList = [];

	var generator  = (function *() {
		try {
			console.log('Received event:');
		        console.log(JSON.stringify(event));

			// ログをフェッチ
			var s3Log = yield fetchLogFromS3(event, generator);
			// ログ解凍処理
			var uncompressedLog = yield uncompressLog(s3Log, generator);
			// レコード抽出
			var matchingRecords = pickupRecords(uncompressedLog);

		  for (var i = 0; i < matchingRecords.length; i++) {
				console.log('Filtered JSON:');
				console.log(JSON.stringify(matchingRecords[i])); //0
				var createUserName = matchingRecords[i].userIdentity.userName;
				var items = matchingRecords[i].responseElements.instancesSet.items;
				var index = 0;
				//利用するRegionを指定
				var EC2 = new aws.EC2({
					region: region_name
				});
				while (index < items.length) {
					var instanceId = items[index].instanceId;
					createTags(createUserName, instanceId, EC2, generator);
					yield index++;
				}
			}
			context.succeed('succeed');
 		} catch (e) {
			console.log("event:" + JSON.stringify(event, null, 2));
			context.fail(e.message);
		}
	})();

	/* 処理開始 */
	generator.next();
};

function fetchLogFromS3 (event, generator) {

	var S3 = new aws.S3();

	var params = {
		Bucket: event.Records[0].s3.bucket.name,
		Key: event.Records[0].s3.object.key
	};

	region_name = params.Key.split('/')[3];

	console.log('Fetching compressed log from S3...');
	S3.getObject(params, function(err, data) {
		if (err) {
			generator.throw(new Error('500:Unable to Fetching S3'));
			return;
		}
		generator.next(data);
    });
}

function uncompressLog (log, generator) {
	var zlib = require('zlib');

	console.log('Uncompressing log...');
	zlib.gunzip(log.Body, function(err, data){
		if (err) {
			generator.throw(new Error('500:Unable to Uncompress Log'));
			return;
		}
		generator.next(data);
	});
}

function pickupRecords (jsonBuffer) {

	var json = jsonBuffer.toString();
  console.log('CloudTrail JSON from S3:');
  console.log(json);
  var records;
  try {
      records = JSON.parse(json);
  } catch (err) {
  	throw(new Error('500:Unable to parse CloudTrail JSON'));
  }

　var matchingRecords = records
      .Records
      .filter(function(record) {
          return record.eventSource.match('ec2.amazonaws.com') && record.eventName.match('RunInstances');
      });

	return matchingRecords;
}

function createTags(createUserName,instanceId, EC2, generator) {
    console.log('CreateTags to EC2: ', instanceId);
    var params = {
        Resources: [instanceId],
        Tags: [{Key: 'createUserName', Value: createUserName}]
    };
    EC2.createTags(params, function(err, data){
    	if (err) {
    		generator.throw(new Error('500:Unable to Create Tags'));
    		return;
    	}
    	generator.next();
    });
}
``` 

## Lambdaにアタッチするロール 
Lambdaにアタッチするロールには以下の権限が必要です。 

- CloudWatchLogsへのログ書出 
- CloudTrailログ用S3バケットの読取 
- EC2へのタグ付与 

よって、今回のポリシーは以下としました。 

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

## デプロイ 
今回のデプロイにはApexというツールを利用してみました。 
[ApexでAWS Lambdaファンクションを管理する](http://dev.classmethod.jp/cloud/aws/how-to-manage-aws-lambda-functions-with-apex/) 

これは初心者ながらすごく便利だなと思いました。 

**apex deploy Lambdaファンクション名** 

みたいな感じで簡単にデプロイできます！ 

はじめ、Lambdaのファイル名をAutoSetUserTags.jsみたいな形にしていたら、Lambda実行時にindexがnot foundだよーという感じのエラーが出たので、ファイル名をindex.jsに変えたらうまく動きました。 

Apexでデプロイするとnode.jsのバージョンが0.10でデプロイされるので手動で4.3に変更しています。 
~~→いい方法があればどなたか教えてください。~~
**function.jsonのruntimeにnodejs4.3とすればいいみたいです！**
[http://apex.run](http://apex.run)
handlerの問題も設定ファイルでなんとかなるかも。

## S3イベントの設定 
S3イベントは以下のように設定しました。

<img width="660" alt="スクリーンショット 2016-07-03 17.08.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/40136/fdc1b277-2bd3-cabe-3d1d-3470f39df7a4.png">

## 動作確認
早速動作確認してみます。
インスタンスを適当に起動します。

<img width="1067" alt="スクリーンショット 2016-07-03 17.11.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/40136/5892585f-2355-edf4-55ea-a8ada511a33e.png">

しばらく経つと、

<img width="1053" alt="スクリーンショット 2016-07-03 17.16.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/40136/0b73cc47-d763-29fa-40f8-3443eb993624.png">

無事タグが付与されました！！！！！これで完成です！
参考にさせていただいたサイトの皆さまありがとうございました。