この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
西澤です。 複数の担当者で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の勉強が必要ですが、これからも役立つユースケースが無いか考えて行こうと思います。