(core): `crossRegionReferences` generates unnecessarily long SSM parameter names leading to deployment failures
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
When using `crossRegionReferences` the producing stack creates SSM Parameters in the consuming region for each exported value. This is accomplished via this code https://github.com/aws/aws-cdk/blob/2779b5e7b4c962409f977eff2d28b96bc01c6c46/packages/aws-cdk-lib/core/lib/private/refs.ts#L248-L263. For users using longer stack names or nested stacks, the generated parameter name can become unnecessarily long. As a result, this can lead to deployment failures resulting from a response object that exceeds the [4096 byte](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html) limit when the export reader and writer custom resource providers are responding back to CloudFormation.
### Expected Behavior
`crossRegionReferences` doesn't generate unnecessarily long SSM parameter names.
### Current Behavior
`crossRegionReferences` generates SSM parameters with the name `/cdk/exports/{consumingStackName}/{export-name}`. For users using nested stacks or for users with longer stack names (or both), the parameter names can become unnecessarily long which can result in deployment failures.
### Reproduction Steps
```ts
import * as awscdk from 'aws-cdk-lib'
const ACCOUNT_ID = '111222333444'
const STACK_PREFIX = 'testing-cdk-cross-region-references-as-example-for-github-issue'
// 8 or less works, 9 or more fails with "Response object is too long"
const NUMBER_OF_KEYS_TO_CREATE = 9
function createRootStack (app: awscdk.App, region: string): awscdk.Stack {
const stackName = `${STACK_PREFIX}-rootstack-${region}`
const props = {
env: {
account: ACCOUNT_ID,
region: region
},
crossRegionReferences: true,
synthesizer: new awscdk.LegacyStackSynthesizer()
}
const stack = new awscdk.Stack(app, stackName, props)
return stack
}
function createPrimaryKeyStack (rootStack: awscdk.Stack, keyName: string): string {
const nestedStack = new awscdk.NestedStack(rootStack, `${STACK_PREFIX}-nestedstack-${keyName}`)
const keyProps = getKeyProps(keyName)
const kmsCfnKey = new awscdk.aws_kms.CfnKey(nestedStack, `${STACK_PREFIX}-key-${keyName}`, keyProps)
const aliasName = `alias/${STACK_PREFIX}-${keyName}`
const aliasProps = getAliasProps(aliasName, kmsCfnKey.attrKeyId)
new awscdk.aws_kms.CfnAlias(nestedStack, `${STACK_PREFIX}-alias-${keyName}`, aliasProps)
return kmsCfnKey.attrArn
}
function createReplicaKeyStack (rootStack: awscdk.Stack, keyName: string, primaryKeyArn: string) {
const nestedStack = new awscdk.NestedStack(rootStack, `${STACK_PREFIX}-nestedstack-${keyName}`)
const keyProps = getReplicaKeyProps(keyName, primaryKeyArn)
const kmsCfnKey = new awscdk.aws_kms.CfnReplicaKey(nestedStack, `${STACK_PREFIX}-key-${keyName}`, keyProps)
const aliasName = `alias/${STACK_PREFIX}-${keyName}`
const aliasProps = getAliasProps(aliasName, kmsCfnKey.attrKeyId)
new awscdk.aws_kms.CfnAlias(nestedStack, `${STACK_PREFIX}-alias-${keyName}`, aliasProps)
}
function getKeyPolicy (): awscdk.aws_iam.PolicyDocument {
const thisPrincipal = new awscdk.aws_iam.AccountPrincipal(ACCOUNT_ID)
const policyStatement = new awscdk.aws_iam.PolicyStatement({
sid: 'Allow all key access',
effect: awscdk.aws_iam.Effect.ALLOW,
actions: [
'kms:*'
],
resources: ['*'],
principals: [thisPrincipal]
})
const policyDocument = new awscdk.aws_iam.PolicyDocument({ statements: [policyStatement] })
return policyDocument
}
function getKeyProps (keyName: string): awscdk.aws_kms.CfnKeyProps {
const policyDocument = getKeyPolicy()
const keyProps: awscdk.aws_kms.CfnKeyProps = {
description: `Testing cross-region references in CDK - ${keyName}`,
keyPolicy: policyDocument,
multiRegion: true,
enableKeyRotation: false,
enabled: true,
keyUsage: 'ENCRYPT_DECRYPT',
pendingWindowInDays: 7
}
return keyProps
}
function getReplicaKeyProps (keyName: string, primaryKeyArn: string): awscdk.aws_kms.CfnReplicaKeyProps {
const policyDocument = getKeyPolicy()
const keyProps: awscdk.aws_kms.CfnReplicaKeyProps = {
description: `Testing cross-region references in CDK - ${keyName}`,
primaryKeyArn: primaryKeyArn,
keyPolicy: policyDocument,
enabled: true,
pendingWindowInDays: 7
}
return keyProps
}
function getAliasProps (aliasName:string, targetKeyId: string): awscdk.aws_kms.CfnAliasProps {
const aliasProps: awscdk.aws_kms.CfnAliasProps = {
aliasName: aliasName,
targetKeyId: targetKeyId
}
return aliasProps
}
function main (args: Array | undefined = undefined) {
const app = new awscdk.App()
const rootStackEast = createRootStack(app, 'us-east-1')
const rootStackWest = createRootStack(app, 'us-west-2')
for (let i = 1; i <= NUMBER_OF_KEYS_TO_CREATE; i++) {
const primaryKeyArn = createPrimaryKeyStack(rootStackEast, i.toString())
createReplicaKeyStack(rootStackWest, i.toString(), primaryKeyArn)
}
}
if (require.main === module) {
main()
}
```
### Possible Solution
`crossRegionReferences` is an experimental feature. We may want to consider updating the SSM parameter names being generated to be limited to a certain length. This would break existing customers, though. Ideally, we could find a way to give users a choice to optionally limit the parameter name length. While this doesn't help us get around the 4096 byte limitation for custom resource provider response objects, it _would_ reduce the chances of a deployment failure by limiting the response body size.
### Additional Information/Context
_No response_
### CDK CLI Version
2.136.0
### Framework Version
_No response_
### Node.js Version
20.11.0
### OS
MacOS
### Language
TypeScript
### Language Version
_No response_
### Other information
_No response_
Contributor guide
Research direction
Start in packages/aws-cdk-lib/core/lib/private/refs.ts at the cross-region reference code around lines 248-263, then run the provided TypeScript reproduction with nested stacks and multiple keys. Trace how the consuming stack name and export name form SSM parameter names. Done means the generated names avoid the reported deployment failure while the compatibility impact and any required user choice are addressed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- cloud, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100