LoginSignup
2
3

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-07-03

目的

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')のように使う

コード

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

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ファンクションを管理する

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

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イベントは以下のように設定しました。

スクリーンショット 2016-07-03 17.08.26.png

動作確認

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

スクリーンショット 2016-07-03 17.11.06.png

しばらく経つと、

スクリーンショット 2016-07-03 17.16.44.png

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

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3