🪄

AWS CDKのGrantsクラスを試してみた

に公開

AWS CDKでは、特定のAWSリソースから他のAWSリソースに対してgrantXxxメソッドを利用して権限を付与できます。
grantXxxメソッドを利用すれば、複雑な権限設定をとても簡単に書けます。

例えば、S3バケットに対してread権限を付与したいときは、次のように書けます。

export class CdkSampleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const func = new NodejsFunction(this, 'Function', {
      entry: path.join(__dirname, 'lambda', 'index.ts'),
      handler: 'handler',
      runtime: Runtime.NODEJS_22_X,
    });
    const bucket = new Bucket(this, 'Bucket');

    // S3バケットがLambda関数に対してread許可を付与
    bucket.grantRead(func);
  }
}

次のようなIAMポリシーが作成され、S3バケットのreadに関する許可設定が付与されています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetBucket*",
                "s3:GetObject*",
                "s3:List*"
            ],
            "Resource": [
                "arn:aws:s3:::cdksample-bucketXXXXXXXXX",
                "arn:aws:s3:::cdksample-bucketXXXXXXXXX/*"
            ],
            "Effect": "Allow"
        }
    ]
}

新たにGrantクラスを利用できるようになったとのことで調査してみました!

Grantsクラスが利用できるようになりました

2025年11月21日にCDKのバージョンがv2.227.0に更新され、S3DynamoDBStep FunctionsGrantsオブジェクトが追加されています。

s3: add BucketGrants
dynamodb: add TableGrants and StreamGrants
stepfunctions: add StateMachineGrants
aws-cdk Releases/v2.227.0

このGrantsオブジェクトがどのように利用できるのか検証していきます。

仕様調査

AWS CDKのAPIリファレンスには、今のところあまり情報が載っていませんでした。

Collection of grant methods for a Bucket.
BucketGrants

A set of permissions to grant on a Table.
TableGrants

Collection of grant methods for a IStateMachineRef.
StateMachineGrants

AWS DevTools Hero 後藤さんによると、GrantsクラスはL1, L2どちらのConstructにも適用できるみたいです。

https://x.com/365_step_tech/status/1991702573297070374?s=20

L1 Constructにも適用できることで、これまでL1しかなかったリソースや、L1のまま利用していたリソースにもGrantが利用できそうです!

また、これまで利用できていたgrantXxxメソッドは、引き続き利用できるみたいです。
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html#grantwbrreadidentity-objectskeypattern

これも後藤さんからの情報ですがgrantXxxメソッドは利用できるものの今後は非推奨になるみたいです。
https://x.com/365_step_tech/status/1991703504285790278?s=20

やってみた

ということで新しい Grants クラスを利用してL1, L2 Constructに対して権限を付与してみます。

L2 Constructへの適用

まずは今までもgrantXxxメソッドが利用できていたL2 ConstructでGrantsクラスを利用してみます。

ドキュメントを見るとL2 Constructにgrantsメソッドが実装されています。

grants
Type: BucketGrants
Collection of grant methods for a Bucket.

Bucket.grants

BucketGrantsクラスには、readdeletepublicAccessなど、今までgrantXxxメソッドとして存在していたメソッドが実装されています。

Collection of grant methods for a Bucket.
BucketGrants

概要がわかったので置き換えてみます。

これまで利用していたgrantXxxメソッドはこのように書けました。
(あとで比較できるように、ここでスナップショットテストを保存しています)

export class CdkSampleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const func = new NodejsFunction(this, 'Function', {
      entry: path.join(__dirname, 'lambda', 'index.ts'),
      handler: 'handler',
      runtime: Runtime.NODEJS_22_X,
    });
    const bucket = new Bucket(this, 'Bucket');

    // read権限の付与
    bucket.grantRead(func);
  }
}

Grantsクラスに置き換えてみましたが、簡単に変更できました。

export class CdkSampleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const func = new NodejsFunction(this, 'Function', {
      entry: path.join(__dirname, 'lambda', 'index.ts'),
      handler: 'handler',
      runtime: Runtime.NODEJS_22_X,
    });
    const bucket = new Bucket(this, 'Bucket');

-   bucket.grantRead(func);
+   bucket.grants.read(func);
  }
}

スナップショットテストを実行してみましたが、変更なしでした。

> vitest

...

 ✓ test/cdk-sample.test.ts (1 test) 119ms
   ✓ Snapshot test 119ms

 Test Files  1 passed (1)
      Tests  1 passed (1)
    ...

L1 Constructへの適用

L1へ適用するためにBucketGrantsクラスのドキュメントをしばらく覗いていましたが、それらしき記述はありませんでした。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.BucketGrants.html

BucketGrantsクラスの実装を見てみると、_fromBucketというstaticメソッドが実装されていました。

/**
 * Creates grants for an IBucketRef
 *
 * @internal
 */
static _fromBucket(bucket: IBucketRef): BucketGrants;

IBucketRefを受け取ってBucketGrantsメソッドを返すので、このメソッドを利用すれば権限が付与できそうです。

しかし、internalメソッドであるため、外部利用は本来想定されていないようです。
もし他に利用できる方法があれば教えてください!

外部利用は想定されていなさそうですが、利用できそうなので試してみます。

まず、S3バケットのL1を利用して、S3バケットread権限がついた状態を実装します。
(ここでスナップショットテストも更新してます)

export class CdkSampleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const cfnBucket = new CfnBucket(this, 'Bucket');

    const role = new Role(this, 'FunctionServiceRole', {
      assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        ManagedPolicy.fromAwsManagedPolicyName(
          'service-role/AWSLambdaBasicExecutionRole',
        ),
      ],
    });
    role.addToPolicy(
      new PolicyStatement({
        actions: ['s3:GetObject*', 's3:GetBucket*', 's3:List*'],
        resources: [cfnBucket.attrArn, `${cfnBucket.attrArn}/*`],
      }),
    );

    const func = new NodejsFunction(this, 'Function', {
      entry: 'lib/lambda/index.ts',
      handler: 'handler',
      runtime: Runtime.NODEJS_22_X,
      // read権限の付与
      role,
    });
  }
}

BucketGrantsクラスの_fromBucketメソッドを利用して実装します。
こちらはかなりスッキリしましたね!

export class CdkSampleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const cfnBucket = new CfnBucket(this, 'Bucket');
    const func = new NodejsFunction(this, 'Function', {
      entry: 'lib/lambda/index.ts',
      handler: 'handler',
      runtime: Runtime.NODEJS_22_X,
    });

    const grants = BucketGrants._fromBucket(cfnBucket);
    // read権限の付与
    grants.read(func);
  }
}

スナップショットテストを実行してみると、変更がないことがわかります。

> vitest

...

 ✓ test/cdk-sample.test.ts (1 test) 119ms
   ✓ Snapshot test 119ms

 Test Files  1 passed (1)
      Tests  1 passed (1)
   ...

まとめ

新しく実装されたGrantsクラスを利用して権限の付与を試してみました。
L2での使い勝手はほぼ変わらず、L1でも使える様になりました!

L1のGrantsクラスは、今後_fromXxxメソッドが公開される形になるのでしょうか?
いずれにせよ続報を待つこととします!

Discussion