LoginSignup
9
8

More than 5 years have passed since last update.

AWS LambdaでEC2インスタンスに作成者を自動タグ付け

Last updated at Posted at 2016-03-20

AWSのEC2インスタンスは、Descriptionに作成者の名前が残らないので、作った人の名前をタグ付けできないかなと調べてみました。

すると、クラスメソッドさんのDeveopers.IOがすぐにヒット。こちらを用いさせていただくことにしました。

この記事を元に、Node.jsの勉強がてら、Primiseを用いて作ってみました。

変更点

ロジックは変更していませんが、その他の変更点は下記。

  • asyncの代わりにPromiseを使ってみました。
  • ec2オブジェクトを作る際に、EC2インスタンスがあるリージョンの名前が必要になるのですが、これはS3のキーから取得できるので、これを用いてみました。
    例えば、下記のようになっているので、us-west-2を抜き出すようにしました。
    "AWSLogs/[account id]/CloudTrail/us-west-2/2016/..."
  • ログ出力にlog4jsを使ってみました。
  • ローカル実行用のコードを追加しました。

実際のコード

var Log4js = require('log4js');
Log4js.configure('log-config.json');
var systemLogger = Log4js.getLogger('system');

var AWS = require('aws-sdk');
var s3 = new AWS.S3();

var zlib = require('zlib');
var Promise = require('bluebird');
var fs = require('fs');

exports.handler = function(event, context) {
    systemLogger.info('Received event:');
    systemLogger.info(JSON.stringify(event, null, "  "));
    var srcBucket = event.Records[0].s3.bucket.name;
    var srcKey = event.Records[0].s3.object.key;
    var region_name = srcKey.split('/')[3];
    systemLogger.debug('bucket.name:', srcBucket);
    systemLogger.debug('s3.object.key:', srcKey);
    systemLogger.debug('region:', region_name);

    // ec2はリージョンセット後に作成
    AWS.config.update({region: region_name});
    var ec2 = new AWS.EC2();

    var params = {
        Bucket: srcBucket,
        Key: srcKey
    };

    new Promise(function(resolve, reject){
        systemLogger.info('Fetching compressed log from S3...:');
        s3.getObject(params, function(err, data){
            if (err) {
                reject(err);
                return;
            }
            resolve(data);
        });
    }).then(function(response) {
        systemLogger.info("Uncompressing log...:");
        return new Promise(function(resolve, reject){
            zlib.gunzip(response.Body, function(err, data){
                if (err) {
                    reject(err);
                    return;
                }
                resolve(data);
            });
        });
    }).then(function(data) {
        var json = data.toString();
        systemLogger.info('CloudTrail JSON from S3:');
        var records;
        try {
            records = JSON.parse(json);
        } catch (err) {
            return Promise.reject('Unable to parse CloudTrail JSON: ' +  err);
        }
        var matchingRecords = records
                .Records
                .filter(function(record) {
                    return record.eventSource.match('ec2.amazonaws.com')
                        && record.eventName.match('RunInstances');
                });
        systemLogger.debug('Size:', matchingRecords.length);
        return Promise.all(matchingRecords.map(function(record){
            systemLogger.info('Filtered JSON:');
            systemLogger.debug(JSON.stringify(record));
            var createUserArn = record.userIdentity.arn;
            var items = record.responseElements.instancesSet.items;
            for(var i = 0; i < items.length; i++) {
                var instanceId = items[i].instanceId;
                systemLogger.info('CreateTags to EC2: ', instanceId);
                var params = {
                    Resources: [instanceId],
                    Tags: [{Key: 'createUserArn', Value: createUserArn}]
                };
                return new Promise(function(resolve, reject){
                    ec2.createTags(params, function(err, data){
                        if (err) {
                            reject(err);
                            return;
                        }
                        resolve(params);
                    });
                });
            }
        }));
    }).then(function(result) {
        systemLogger.info('Finish:', JSON.stringify(result, null, "  "));
        context.succeed();
    }).catch(function(err) {
        systemLogger.error('Reject:', err);
        context.fail();
    });
};

// ローカル実行用
if (!module.parent) {
    var file = './input.json';
    new Promise(function(resolve, reject){
        fs.readFile(file, function(err, data){
            if (err) {
                reject(err);
                return;
            }
            resolve(data);
        });
    }).then(function(data){
        var hoge = (function() {
            var hoge = function() {};
            var p = hoge.prototype;
            p.succeed = function() {};
            p.done = function() {};
            return hoge;
        })();

        var inputJson = JSON.parse(data);
        var mockedContext = new hoge();
        exports.handler(inputJson, mockedContext);
    }).catch(function(err) {
        systemLogger.error(err);
    });
}

GitHubにあげておきました。
https://github.com/khiraiwa/aws-ec2-created-usertag
以下、実行方法など。

ローカルで試す

前準備

  • AWSのCredentialsは設定済みとします。
  • package.jsonのあるディレクトリで下記のコマンドで必要なモジュールをインストールします。
    $ npm install
  • input.jsonにテスト用のパラメータを入力します。
    デフォルトだと"xxxx"のような値が入っています。

ちなみに、下記のようにcreateTagsに渡すパラメータでDryRunを有効化しておけば、実際にインスタンスの変更は行われません。(試すのに便利です。)


var params = {
    Resources: [instanceId],
    Tags: [{Key: 'createUserArn', Value: createUserArn}],
    DryRun: true
};

実行

下記コマンドで実行できます。

$ node aws-ec2-created-usertag.js

AWS Lambdaにインストール

続いて、Lambda上で実行する場合です。

ポリシーの作成

名前は任意です。例えばLambdaCreatedUserTagToEC2のように設定してください。

中身は下記のように設定しましました。(Developers.IOを参考)

{
    "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:::[CloudTrailのログを保存しているバケット]/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateTags"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

ロールの作成

続いてロールを作成します。

こちらも名前は任意です。例えばLambdaCreatedUserTagToEC2Roleのように設定します。
RoleTypeはAWS Lambdaを選択します。
中身は先ほど作成したロールをアタッチしてください。
例だとLambdaCreatedUserTagToEC2Role。

Lamda Functionの作成

下記のコマンドを実行して作成します。

$ npm install
$ zip aws-ec2-created-usertag.zip -r aws-ec2-created-usertag.js log-config.json node_modules
$ aws --region [作成するリージョン名] lambda create-function --function-name EC2CreatedUserTag --zip-file fileb://[Zipファイルへのパス]/aws-ec2-created-usertag.zip --role [上記で作成したロールのRole ARN] --handler aws-ec2-created-usertag.handler --runtime nodejs --timeout 60 --memory-size 128

すると、指定したリージョンのAWS Lambda FuncitonにEC2CreatedUserTagというFunctionが追加されています。

Event Sourceの設定

最後にLambda Functionを自動実行するためのEvent Sourceを設定しておきます。

AWSのマネジメントコンソール上で、作成したEC2CreatedUserTagに対して、下記を設定しておきます。

  • Event source type: S3
  • Bucket: CloudTrailのログをためているバケット
  • Event type: Put

これで完了です。
試したところ、実際にタグ付けされていました!
(クラスメソッドさん、ありがとうございます。)

9
8
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
9
8