目的
EC2を起動する際にデフォルトではNameタグはあるけど、それだけだと誰が作成したかわからなくなってしまいます。
当社でもインスタンスの数が増えて、「これだれが作ったの?消していい?」みたいな会話が日常茶飯事です。
そこで、今回は以下記事を参考に、最近学んだGeneratorを使って書き直してみました!
AWS LambdaでEC2インスタンスに作成者を自動タグ付け
LambdaでEC2作成者をタグ付けする
Javascriptの非同期処理
最近現場でJavascriptという言語を使ってコードを書かなければならなくなったのでLambdaからDynamoDBにQueryしてその結果を使ってー。。。みたいな処理を書いていました。
ところが、Javascriptの非同期処理のせいで、Queryが終わる前にその先の処理を実行してしまうんですね!
非同期処理ってどういうこと?JavaScriptで一から学ぶ
JavaScriptコールバックを整理してみた【再入門】
Generatorとは?
以下の記事が参考になります。
ES6 Generatorを使ってasync/awaitを実装するメモ
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')
のように使う
コード
以下が作成したコードです。
/* ====================================================================== */
/**
* インスタンス起動時に自動でタグつけるやつ
*
*/
/* ====================================================================== */
'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ファンクションを管理する
これは初心者ながらすごく便利だなと思いました。
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
handlerの問題も設定ファイルでなんとかなるかも。
S3イベントの設定
S3イベントは以下のように設定しました。
動作確認
早速動作確認してみます。
インスタンスを適当に起動します。
しばらく経つと、
無事タグが付与されました!!!!!これで完成です!
参考にさせていただいたサイトの皆さまありがとうございました。