0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CDKでマルチリージョン構成のDynamoDBを作成する

Last updated at Posted at 2025-03-07

やろうとしたこと

CDKでTableV2 クラスを使用して DynamoDB Global Table を作成していました。テーブルは複数リージョンにレプリケーションすると同時に各テーブルにはリソースポリシーを設定します。

発生した事象

ここで以下のようなエラーが発生してテーブル作成に失敗しました。

A replica cannot be created in the same stack update as putting a resource-based policy on that new replica

原因

AWSの公式ドキュメントDynamoDB リソースベースのポリシーの考慮事項に以下のような記載があります。どうやらレプリカ作成とそのレプリカへのリソースポリシーを同じスタックでデプロイしようとしていたのがダメだったようです。

AWS::DynamoDB::GlobalTable リソースでは、スタックの更新を行うリージョン以外のリージョンでは、スタックの更新時にレプリカの作成と、そのレプリカへのリソースベースのポリシーの追加を同時に行うことはできません。

解決策

要するにDynamoDBのレプリカ作成とそのレプリカへのリソースポリシー設定を別のスタックでデプロイすれば良いのですが、CDKでは外部スタックで定義したリソースの参照は出来ても更新は出来ないため単純にCDKのみでリソースポリシーの設定まで実装することが出来ません。(ここの認識違っていたらご指摘いただきたいです)
そこで今回はAwsSdkCallというCDKのモジュールを使用してリソースポリシーを後付けすることで対応しました。

サンプルコード

import * as cr from "aws-cdk-lib/custom-resources";
import { Construct } from "constructs";
import { Stack, StackProps } from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";

export interface TenantExtensionStackProps extends StackProps {
  readonly abacRoleArn: string;
  readonly enableDevMode?: boolean;
  readonly allowedAccount?: string[];
}

export class TenantExtensionStack extends Stack {
  constructor(scope: Construct, id: string, props: TenantExtensionStackProps) {
    super(scope, id, props);

    tableArn = "SAMPLE_REPLICA_TABLE"
      const policyStatements: iam.PolicyStatement[] = [
        new iam.PolicyStatement({
          actions: [
            "dynamodb:PutItem",
            "dynamodb:GetItem",
            "dynamodb:Query",
            "dynamodb:UpdateItem",
            "dynamodb:DeleteItem",
            "dynamodb:ConditionCheckItem",
          ],
          principals: [new iam.ArnPrincipal(SAMPLE_ROLE)],
          resources: [tableArn],
        }),
      ];

      function addDevelopmentDynamoDBPermissionsTmp(
        statements: iam.PolicyStatement[],
        tableArn: string,
        accounts: string[]
      ) {
        accounts.forEach((account) => {
          statements.push(
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              principals: [
                new iam.ArnPrincipal(`arn:aws:iam::${account}:root`),
              ],
              actions: ["dynamodb:*"],
              resources: [tableArn],
            })
          );
        });
      }
      if (props.enableDevMode && props.allowedAccount) {
        addDevelopmentDynamoDBPermissionsTmp(
          policyStatements,
          tableArn,
          props.allowedAccount
        );
      }

      const resourcePolicy = JSON.stringify({
        Version: "2012-10-17",
        Statement: policyStatements.map((statement) =>
          statement.toStatementJson()
        ),
      });

      const putResourcePolicy = new cr.AwsCustomResource(
        this,
        `PutDynamoDBPolicy-${tableConfig.name}`,
        {
          onUpdate: {
            service: "DynamoDB",
            action: "putResourcePolicy",
            parameters: {
              ResourceArn: tableArn,
              Policy: resourcePolicy,
            },
            physicalResourceId: cr.PhysicalResourceId.of(
              `put-dynamodb-policy-${tableConfig.name}`
            ),
          },
          onDelete: {
            service: "DynamoDB",
            action: "deleteResourcePolicy",
            parameters: {
              ResourceArn: tableArn,
              Policy: resourcePolicy,
            },
            physicalResourceId: cr.PhysicalResourceId.of(
              `put-dynamodb-policy-${tableConfig.name}`
            ),
          },
          policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
            resources: [tableArn],
          }),
        }
      );
    });
  }
}

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?