はじめに
たまに「S3のデータ量に上限をつけたい」という話を聞きますが、今回はタイトル通り、Lambdaでこれが実現できないかどうか試してみました。
仕組み
S3へデータがアップロードされた時にLambda Functionが呼び出されるので、その呼び出しを使って、Putされたデータ量をDynamoDBに記録していきます。
ある一定以上のデータ量が加算されたところで、LambdaからS3バケットに対してバケットポリシーの変更を行い、以降はPutできなくします。仕組みは以下の通りです。
実装
はじめに、DynamoDBにテーブルを作成します。キーは文字列型のBucketとしておきます。
適当な名前で、S3バケットを作ります。S3バケットとDynamoDBは同じリージョンで作成します。
以下のコードをLambda Functionとして登録して、最後にLambda FunctionとS3を関連づけます。
var aws = require('aws-sdk');
var dynamoTableName = "S3バケット名";
var limitDataSize = 5000;//最大データサイズ
exports.handler = function(event, context) {
var record = event.Records[0];
var size = record.s3.object.size;
var region = record.awsRegion;
var bucket = record.s3.bucket.name;
var dynamo = new aws.DynamoDB({
region : region
});
var params = {
TableName : dynamoTableName,
Key : {
bucket : {
'S' : bucket
}
},
AttributeUpdates : {
size : {
Action : 'ADD',
Value : {
'N' : size
}
}
},
ReturnValues : "UPDATED_NEW"
};
dynamo.updateItem(params, function(err, data) {
if (err) {
context.done(err, "error");
} else {
console.log("data updated," + JSON.stringify(data));
var updatedSize = data.Attributes.size.N;
checkLimit(context, updatedSize, region, bucket);
}
});
};
function checkLimit(context, updatedSize, region, bucket) {
if (updatedSize < limitDataSize) {
context.done(null, "size=" + updatedSize + " limit=" + limitDataSize);
return;
}
console.log("limit exceeded size=" + updatedSize + " limit="
+ limitDataSize);
// change S3 bucket policy
var s3 = new aws.S3({
params : {
region : region
}
});
var policy = {
"Version" : "2012-10-17",
"Statement" : [ {
"Sid" : "ignorePut",
"Effect" : "Deny",
"Principal" : {
"AWS" : "*"
},
"NotAction" : [ "s3:Get*", "s3:List*", "s3:Delete*" ],
"Resource" : [ "arn:aws:s3:::" + bucket + "/*" ]
} ]
};
var param = {
Bucket : bucket,
Policy : JSON.stringify(policy)
};
s3.putBucketPolicy(param, function(err, data) {
if (err) {
context.done(err, "error");
} else {
context.done(null, "done");
}
});
}
PutされるごとにDynamoDBにデータサイズが加算され、一定以上になったときにS3バケットにpolicyが適用されます。
ほかの使い道
Lambdaからポリシーを変えるというのが今回のキモになっていますので、たとえば指定IPから一定量のアクセスがあったらDenyするとか、CloudTrailと連携して、一定回数マネジメントコンソールにログインに失敗したら、翌日までログインできなくする、というようなこともできると思います(これは次回トライしたいです)
オチ
実は、S3からのPut通知は、新規にオブジェクトを作成したときだけでなく、ファイルを上書きしたり、消したときにも通知され、知る限り見分ける方法がありません。ですので実は今の段階では、保存されているデータ量の制限ではなく、操作したデータの総量での制限になってしまいます。今後S3 notificationがより細かく情報を取れるようになれば、削除や更新時に適切にデータ量を足し引きすることで、保存データ量による制限ができるようになると思います。
他のLambda関係Post(外部blog)
Lambdaを使って、CloudTrailログをCloudSearchに入れて検索する
Lambdaで顔認識アプリケーションを作る
S3からJavaを起動するLambda Function
Lambdaで画像に描画する
またこの投稿は会社とは関係ありません。