diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index e0c5ff55c501e..d24d240503b7c 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -594,6 +594,8 @@ new CustomResource(this, 'MyMagicalResource', { resourceType: 'Custom::MyCustomResource', // must start with 'Custom::' // the resource properties + // properties like serviceToken or serviceTimeout are ported into properties automatically + // try not to use key names similar to these or there will be a risk of overwriting those values properties: { Property1: 'foo', Property2: 'bar', diff --git a/packages/aws-cdk-lib/core/lib/custom-resource.ts b/packages/aws-cdk-lib/core/lib/custom-resource.ts index 3ae49c789c86d..d5a8bb5d78d73 100644 --- a/packages/aws-cdk-lib/core/lib/custom-resource.ts +++ b/packages/aws-cdk-lib/core/lib/custom-resource.ts @@ -1,4 +1,5 @@ import { Construct } from 'constructs'; +import { Annotations } from './annotations'; import { CfnResource } from './cfn-resource'; import { Duration } from './duration'; import { addConstructMetadata, MethodMetadata } from './metadata-resource'; @@ -54,6 +55,8 @@ export interface CustomResourceProps { * serviceToken: myTopic.topicArn, * }); * ``` + * + * Maps to [ServiceToken](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html#cfn-cloudformation-customresource-servicetoken) property for the `AWS::CloudFormation::CustomResource` resource */ readonly serviceToken: string; @@ -62,6 +65,8 @@ export interface CustomResourceProps { * * The value must be between 1 second and 3600 seconds. * + * Maps to [ServiceTimeout](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html#cfn-cloudformation-customresource-servicetimeout) property for the `AWS::CloudFormation::CustomResource` resource + * * @default Duration.seconds(3600) */ readonly serviceTimeout?: Duration; @@ -69,6 +74,10 @@ export interface CustomResourceProps { /** * Properties to pass to the Lambda * + * Values in this `properties` dictionary can possibly overwrite other values in `CustomResourceProps` + * E.g. `ServiceToken` and `ServiceTimeout` + * It is recommended to avoid using same keys that exist in `CustomResourceProps` + * * @default - No properties. */ readonly properties?: { [key: string]: any }; @@ -154,11 +163,21 @@ export class CustomResource extends Resource { } } + const constructPropertiesPassed = { + ServiceToken: props.serviceToken, + ServiceTimeout: props.serviceTimeout?.toSeconds().toString(), + }; + + const hasCommonKeys = Object.keys(properties).some(key => key in constructPropertiesPassed); + + if (hasCommonKeys) { + Annotations.of(this).addWarningV2('@aws-cdk/core:customResourcePropConflict', `The following keys will be overwritten as they exist in the 'properties' prop. Keys found: ${Object.keys(properties).filter(key => key in constructPropertiesPassed)}`); + } + this.resource = new CfnResource(this, 'Default', { type, properties: { - ServiceToken: props.serviceToken, - ServiceTimeout: props.serviceTimeout?.toSeconds().toString(), + ...constructPropertiesPassed, ...properties, }, }); diff --git a/packages/aws-cdk-lib/core/test/custom-resource.test.ts b/packages/aws-cdk-lib/core/test/custom-resource.test.ts index ee102ff469880..3334a0782c296 100644 --- a/packages/aws-cdk-lib/core/test/custom-resource.test.ts +++ b/packages/aws-cdk-lib/core/test/custom-resource.test.ts @@ -1,4 +1,5 @@ import { toCloudFormation } from './util'; +import { Annotations } from '../../assertions'; import { CustomResource, Duration, RemovalPolicy, Stack } from '../lib'; describe('custom resource', () => { @@ -212,4 +213,20 @@ describe('custom resource', () => { }); }).toThrow(`serviceTimeout must either be between 1 and 3600 seconds, got ${invalidSeconds}`); }); + + test('send warning if customResource construct property key is added to properties', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new CustomResource(stack, 'MyCustomResource', { + serviceToken: 'MyServiceToken', + properties: { + ServiceToken: 'RepeatedToken', // this is repeated because serviceToken prop above will resolve as property ServiceToken + }, + }); + + // THEN + Annotations.fromStack(stack).hasWarning('/Default/MyCustomResource', 'The following keys will be overwritten as they exist in the \'properties\' prop. Keys found: ServiceToken [ack: @aws-cdk/core:customResourcePropConflict]'); + }); });