aws/aws-cdk

TableV2MultiAccountReplica: does not set top-level SSESpecification when using TableEncryptionV2.customerManagedKey(), causing deployment failure

Open

#37783 opened on May 6, 2026

View on GitHub
 (3 comments) (0 reactions) (0 assignees)TypeScript (10,710 stars) (3,530 forks)batch import
@aws-cdk/aws-cloudformationbugeffort/smallgood first issuep2

Description

Describe the bug

When using TableV2MultiAccountReplica with TableEncryptionV2.customerManagedKey(), the generated CloudFormation template is missing the top-level SSESpecification property on the AWS::DynamoDB::GlobalTable resource. This causes CloudFormation to fail with:

Resource handler returned message: "Invalid request provided: ReplicaSSESpecification and SSEType must be null when SSE is set to default."

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Library Version

No response

Expected Behavior

The TableV2MultiAccountReplica construct should render a top-level SSESpecification with SSEEnabled: true and SSEType: "KMS" when using customer-managed key encryption, matching the behavior of TableV2:

 {
   "Type": "AWS::DynamoDB::GlobalTable",
   "Properties": {
     "TableName": "MyGlobalTable",
     "GlobalTableSourceArn": "arn:aws:dynamodb:us-west-2:111111111111:table/MyGlobalTable",
     "SSESpecification": { /* <=== this */
       "SSEEnabled": true,
       "SSEType": "KMS"
     },
     "Replicas": [{
       "Region": "us-east-2",
       "SSESpecification": {
         "KMSMasterKeyId": "arn:aws:kms:us-east-2:222222222222:key/..."
       }
     }]
   }
 }

Current Behavior

TableV2MultiAccountReplica only sets SSESpecification.KMSMasterKeyId at the replica level, but omits the top-level SSESpecification entirely:

{
 "Type": "AWS::DynamoDB::GlobalTable",
 "Properties": {
   "TableName": "MyGlobalTable",
   "GlobalTableSourceArn": "arn:aws:dynamodb:us-west-2:111111111111:table/MyGlobalTable",
   "Replicas": [{
     "Region": "us-east-2",
     "SSESpecification": {
       "KMSMasterKeyId": "arn:aws:kms:us-east-2:222222222222:key/..."
     }
   }]
 }
}

DDB's CFN validator has a rule that says "all settings must be null if SSE is disabled". This lack of SSESpecification at the top-level is being interpreted as SSEEnabled: false. Because the replicas themselves have encryption info, the validator fails.

This seems to be backed up in the docs for SSESpecification:

Specifies the settings to enable server-side encryption. These settings will be applied to all replicas. If you plan to use customer-managed KMS keys, you must provide a key for each replica using the ReplicaSpecification.ReplicaSSESpecification property.

Reproduction Steps

import * as cdk from 'aws-cdk-lib';
import { Stack } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import {
  AttributeType,
  Billing,
  TableEncryptionV2,
  TableV2,
  TableV2MultiAccountReplica,
} from 'aws-cdk-lib/aws-dynamodb';
import { Key } from 'aws-cdk-lib/aws-kms';

const PRIMARY_ACCOUNT = '111111111111';
const PRIMARY_REGION = 'us-west-2';
const REPLICA_ACCOUNT = '222222222222';
const REPLICA_REGION = 'us-east-2';

class PrimaryTableStack extends Stack {
  public readonly table: TableV2;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const kmsKey = new Key(this, 'TableKey', { enableKeyRotation: true });

    this.table = new TableV2(this, 'GlobalTable', {
      tableName: 'MyGlobalTable',
      partitionKey: { name: 'pk', type: AttributeType.STRING },
      billing: Billing.onDemand(),
      encryption: TableEncryptionV2.customerManagedKey(kmsKey),
    });
  }
}

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

    const kmsKey = new Key(this, 'TableKey', { enableKeyRotation: true });

    // BUG: This generates CloudFormation without top-level SSESpecification
    new TableV2MultiAccountReplica(this, 'GlobalTableReplica', {
      replicaSourceTable: primaryTable,
      encryption: TableEncryptionV2.customerManagedKey(kmsKey),
    });
  }
}

const app = new cdk.App();

const primaryStack = new PrimaryTableStack(app, 'PrimaryTableStack', {
  env: { account: PRIMARY_ACCOUNT, region: PRIMARY_REGION },
});

new ReplicaTableStack(app, 'ReplicaTableStack', primaryStack.table, {
  env: { account: REPLICA_ACCOUNT, region: REPLICA_REGION },
});

app.synth();

Possible Solution

The TableV2MultiAccountReplica doesn't have anything setting sseSpecification. TableV2 in the same file sets it here.

Additional Information/Context

I've been able to mitigate this with an escape hatch:

 const cfnTable = replicaTable.node.defaultChild as CfnGlobalTable;
 cfnTable.addPropertyOverride('SSESpecification', {
   SSEEnabled: true,
   SSEType: 'KMS',
 })

AWS CDK Library version (aws-cdk-lib)

2.241.0

AWS CDK CLI version

2.241.0

Node.js Version

20.x

OS

macOs

Language

TypeScript

Language Version

No response

Other information

No response

Contributor guide