TableV2MultiAccountReplica: does not set top-level SSESpecification when using TableEncryptionV2.customerManagedKey(), causing deployment failure
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 74
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`](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-dynamodb-globaltable.html):
> 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](https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts#L1447-L1463). `TableV2` in the same file [sets it here](https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts#L853).
### 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
Research direction
Start in packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts, comparing TableV2's SSESpecification handling near line 853 with TableV2MultiAccountReplica near lines 1447-1463. Synthesize the reproduction stack and inspect the AWS::DynamoDB::GlobalTable resource; done means customer-managed-key replicas render top-level SSEEnabled true and SSEType KMS alongside the replica key settings.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, databases, infrastructure
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100