はじめに
みなさんは CDK でリソースに権限を与える時grant*
と書かれたメソッドを使ったことはあるでしょうか?
私は恥ずかしながらこれまで直接ポリシーを記述していたんですが、CDK で用意されている grant メソッドを使うと1行で権限が付与できると聞いたので試してみました。
CloudFormation のノリでポリシーやロールを定義していた方は、紹介する grant メソッドを一度使ってみてください。
最初にまとめ
grant メソッドを使ってみて感じたメリットは大きく2点あります。
- ポリシーを記述しないためコード量が少ない
- 権限を許可する対象がわかりやすい
もし CDK でコードを書いていて、IAM ポリシーやロールが増えてきて複雑になってきたな…という方は是非参考にしてみてください。
具体的には、以下の1行書くだけで「特定のS3バケットへの読み取り権限をLambdaのロールに付与」できます。
bucket.grantRead(lambdaFunction);
実際作成されるポリシーはこんな感じです。
{
"Version": "2012-10-17",
"Statement":[
{
"Action":[
"s3:GetBucket*",
"s3:GetObject*",
"s3:List*"
],
"Resource":[
"arn:aws:s3:::{S3Backet}",
"arn:aws:s3:::{S3Backet}/*"
],
"Effect": "Allow"
}
]
}
1行で読み取り系の権限を付与できていて、対象のバケットへの許可が付与されているのが非常にわかりやすいです。 ただし、ある程度用途に合わせた権限が幅広に設定される点は抑えておきましょう。
やってみる
実際に grant メソッドを使った権限付与と、通常のポリシーを書くパターンで比較してみます。
Lambda に 3 バケットに対する読み取り権限を与えるコードを書いてみました。
grantメソッドを使った権限付与
実際に Lambda へ特定の S3 バケットへの読み取り権限を与えたコードがこちらです。
// S3バケットの作成
const bucket = new cdk.aws_s3.Bucket(this, "MyBucket");
// Lambda関数の作成
const lambdaFunction = new cdk.aws_lambda.Function(
this,
"MyLambdaFunction",
{
runtime: cdk.aws_lambda.Runtime.PYTHON_3_11,
handler: "s3_read.handler",
code: cdk.aws_lambda.Code.fromAsset("app/"),
}
);
// Lambda 関数から S3 バケットへの権限付与
bucket.grantRead(lambdaFunction);
Lambda に権限を与えているのはbucket.grantRead(lambdaFunction);
の部分です。S3 バケットに対し Lambda へ読み取り権限を許可しています。
実際に作成される IAM ポリシーを確認してみると、以下のポリシーが作成されていました。1行でこれが実装できるのは熱いですね。Resource もしっかり対象の S3 バケットに絞られています。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetBucket*",
"s3:GetObject*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::cdkigrantablestack-mybucketf68f3ff0-j6pozrvs7gen",
"arn:aws:s3:::cdkigrantablestack-mybucketf68f3ff0-j6pozrvs7gen/*"
],
"Effect": "Allow"
}
]
}
通常の権限付与
比較用に同じような実装を grant メソッドを使わずに書いた時のコードも載せておきます。ハイライトした部分が grant メソッドを使った時には1行に置き変わります。
// S3バケットの作成
const bucket = new cdk.aws_s3.Bucket(this, "MyBucket");
// IAMポリシーの作成
const s3ReadPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["s3:GetBucket*", "s3:GetObject*", "s3:List*"],
resources: [`${bucket.bucketArn}`, `${bucket.bucketArn}/*`],
});
// IAMロールの作成
const lambdaRole = new iam.Role(this, "LambdaRole", {
assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName(
"service-role/AWSLambdaBasicExecutionRole"
),
],
});
// ポリシーをIAMロールに割り当てる
lambdaRole.addToPolicy(s3ReadPolicy);
// Lambda関数の作成
const lambdaFunction = new cdk.aws_lambda.Function(
this,
"MyLambdaFunction",
{
runtime: cdk.aws_lambda.Runtime.PYTHON_3_11,
handler: "s3_read.handler",
code: cdk.aws_lambda.Code.fromAsset("app/"),
role: lambdaRole, // IAMロールを割り当てる
}
);
こうしてみると、どの程度コードが減るのか一目瞭然ですね。grant メソッドを使うメリットを感じていただけたかと思います。
注意点
非常に便利な grant メソッドですが、厳密な最小権限で運用する際には注意が必要です。
あらかじめ用意してくれている権限を付与してくれるため、Action 単位で全て記述したい場合には適していません。S3 バケットの grantRead で権限を付与する場合は、以下 3 つの読み取り権限に*
をつけた Action が設定されていました。
- s3:GetBucket*
- s3:GetObject*
- s3:List*
S3 バケットを読み取る上で必要な権限を幅広に設定するものなので、この点は意識して利用しましょう。Resource はしっかり対象の S3 バケットに絞られているので、その点は安心して利用できそうです。
その他の使い方
今回は S3 バケットへの読み取り権限grantRead
を使ってみましたが、他にもさまざまな grant メソッドを使った権限付与が可能です。
S3 バケットだけでもドキュメントを確認すると以下のパターンがありました。用途に合わせたものはある程度用意されてそうですね。
- grantDelete
- grantPublicAccess
- grantPut
- grantPutAcl
- grantReadWrite
- grantWrite
他にもDynamoDBテーブルへの権限付与やSQSキューへの送信許可など少し探せば用意されているものがたくさんあったので、grant から始まるものを探せば幸せになれるかもしれません。
まとめ
grant メソッドを使った権限付与を試してみました。コード量が圧倒的に減るため簡潔で分かりやすく、アクセス管理がより簡単にできそうです。 これまで CloudFormation と同じような書き方をしてたという方は、是非 grant メソッドを利用してみてください。