LambdaでEC2作成者をタグ付けする
西澤です。 複数の担当者でAWSの検証環境を使っていると、誰が作ったのかわからないEC2インスタンスが並んでしまうことがよくあると思います。Lambdaどころかnode.jsもjavascriptも全く触ったことがなかったのですが、Lambdaを使って自動化できないかと考え、実際にやってみました。 主に参考にしたのは、下記のドキュメントです。
- AWS Lambda Walkthrough 5: Handling AWS CloudTrail Events Using the AWS CLI (Node.js)
- How to Receive Alerts When Specific APIs Are Called by Using AWS CloudTrail, Amazon SNS, and AWS Lambda
計画
CloudTrailとLambdaを連携して、下記のように動かしてみようと考えました。
- ユーザがEC2を起動
- CloudTrailがEC2起動を検知してS3バケットにログ記録
- Lambdaファンクション起動
- ログから実行ユーザを特定してEC2にタグ付け
事前準備
実装の前に以下の準備を行いました。
- CloudTrail有効化
- 実行契機となるCloudTrailログの意図的な出力
- CloudTrailログ(JSON)の確認
JSONファイルには慣れてきたつもりでしたが、CloudTrailから記録されたJSONを読み解くのも大変でした。 今回JSONから抽出しようとした部位を抜粋しておきます。
"Records": [ { ::: "userIdentity": { ::: "arn": "arn:aws:iam::123456789012:user/iamuser", ::: }, ::: "eventSource": "ec2.amazonaws.com", "eventName": "RunInstances", ::: "responseElements": { ::: "instancesSet": { ::: "items": [ { "instanceId": "i-xxxxxxxx", ::: }, ::: ], ::: }, ::: } ::: }, ::: ]
IAMロールの作成
Lambdaに付与する権限を必要最低限とする為、IAMロールを事前に準備しました。
- 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:::cloudtrail-ap-northeast-1-bucket/*" ] }, { "Effect": "Allow", "Action": [ "ec2:CreateTags" ], "Resource": [ "*" ] } ] }
Lambda Function作成とテスト
node.jsの開発環境は下記手順にて準備しました。
ここからが大変でした。ほとんどの部分ははじめに紹介したページからの切り貼りなのですが、正常に動作させるまでに大いに手こずりました。 下記で紹介されているdriver.jsのような仕組みも無いとテストが辛いです。
ということで、↓こんな感じになりました。このスクリプトとasyncをzipファイルにして、CloudTrailログ用S3バケットへのObjectCreatedをトリガにLambda登録しました。
var aws = require('aws-sdk'); var zlib = require('zlib'); var async = require('async'); //利用するRegionを指定 var DEFAULT_EC2_REGION = 'ap-northeast-1'; var s3 = new aws.S3(); var ec2 = new aws.EC2({ region: DEFAULT_EC2_REGION }); exports.handler = function(event, context) { console.log('Received event:'); console.log(JSON.stringify(event)); var srcBucket = event.Records[0].s3.bucket.name; var srcKey = event.Records[0].s3.object.key; async.waterfall([ function fetchLogFromS3(next){ console.log('Fetching compressed log from S3...'); s3.getObject({ Bucket: srcBucket, Key: srcKey }, next); }, function uncompressLog(response, next){ console.log("Uncompressing log..."); zlib.gunzip(response.Body, next); }, function createTags(jsonBuffer, next) { var json = jsonBuffer.toString(); console.log('CloudTrail JSON from S3:'); console.log(json); var records; try { records = JSON.parse(json); } catch (err) { next('Unable to parse CloudTrail JSON: ' + err); return; } var matchingRecords = records .Records .filter(function(record) { return record.eventSource.match('ec2.amazonaws.com') && record.eventName.match('RunInstances'); }); async.each( matchingRecords, function(record, createTagsComplete) { console.log('Filtered JSON:'); console.log(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; console.log('CreateTags to EC2: ', instanceId); var params = { Resources: [instanceId], Tags: [{Key: 'createUserArn', Value: createUserArn}] }; ec2.createTags(params, createTagsComplete); } }, next ); } ], function (err) { if (err) { console.error('createTags failed: ', err); } else { console.log('createTags normally ended.'); } context.done(err); }); };
動作確認
いよいよ実際のテストです。EC2を起動してCloudTrailログが出力されるまでしばらく待ちます。 場合によって5分程度待つので心配しましたが、無事にEC2インスタンスに作成者の情報をタグ設定することができました!
まとめ
取り立てて目新しい機能を用いた訳ではありませんでしたが、Lambda、CloudTrail等のサービスを上手に組み合わせることで、運用に生かせる余地がまだまだありそうです。個人的にはjavascriptの勉強が必要ですが、これからも役立つユースケースが無いか考えて行こうと思います。