diff --git a/packages/aws-cdk-lib/core/lib/cfn-resource.ts b/packages/aws-cdk-lib/core/lib/cfn-resource.ts index d1796b3411bd7..490dd5b646f81 100644 --- a/packages/aws-cdk-lib/core/lib/cfn-resource.ts +++ b/packages/aws-cdk-lib/core/lib/cfn-resource.ts @@ -7,11 +7,9 @@ import { CfnCreationPolicy, CfnDeletionPolicy, CfnUpdatePolicy } from './cfn-res import { Construct, IConstruct, Node } from 'constructs'; import { addDependency, obtainDependencies, removeDependency } from './deps'; import { CfnReference } from './private/cfn-reference'; -import { CLOUDFORMATION_TOKEN_RESOLVER } from './private/cloudformation-lang'; import { Reference } from './reference'; import { RemovalPolicy, RemovalPolicyOptions } from './removal-policy'; import { TagManager } from './tag-manager'; -import { Tokenization } from './token'; import { capitalizePropertyNames, ignoreEmpty, PostResolveToken } from './util'; import { FeatureFlags } from './feature-flags'; import { ResolutionTypeHint } from './type-hints'; @@ -432,15 +430,13 @@ export class CfnResource extends CfnRefElement { Description: this.cfnOptions.description, Metadata: ignoreEmpty(this.cfnOptions.metadata), Condition: this.cfnOptions.condition && this.cfnOptions.condition.logicalId, - }, resourceDef => { + }, (resourceDef, context) => { const renderedProps = this.renderProperties(resourceDef.Properties || {}); if (renderedProps) { const hasDefined = Object.values(renderedProps).find(v => v !== undefined); resourceDef.Properties = hasDefined !== undefined ? renderedProps : undefined; } - const resolvedRawOverrides = Tokenization.resolve(this.rawOverrides, { - scope: this, - resolver: CLOUDFORMATION_TOKEN_RESOLVER, + const resolvedRawOverrides = context.resolve(this.rawOverrides, { // we need to preserve the empty elements here, // as that's how removing overrides are represented as removeEmpty: false, diff --git a/packages/aws-cdk-lib/core/lib/resolvable.ts b/packages/aws-cdk-lib/core/lib/resolvable.ts index 3feb37383a378..fe41ee78d20a1 100644 --- a/packages/aws-cdk-lib/core/lib/resolvable.ts +++ b/packages/aws-cdk-lib/core/lib/resolvable.ts @@ -44,6 +44,13 @@ export interface ResolveChangeContextOptions { * @default - Unchanged */ readonly allowIntrinsicKeys?: boolean; + + /** + * Whether to remove undefined elements from arrays and objects when resolving. + * + * @default - Unchanged + */ + readonly removeEmpty?: boolean; } /** diff --git a/packages/aws-cdk-lib/core/lib/util.ts b/packages/aws-cdk-lib/core/lib/util.ts index 499ca5e5eb660..c536cf535d45c 100644 --- a/packages/aws-cdk-lib/core/lib/util.ts +++ b/packages/aws-cdk-lib/core/lib/util.ts @@ -79,7 +79,7 @@ export function filterUndefined(obj: any): any { * A Token that applies a function AFTER resolve resolution */ export class PostResolveToken extends Intrinsic implements IPostProcessor { - constructor(value: any, private readonly processor: (x: any) => any) { + constructor(value: any, private readonly processor: (x: any, context: IResolveContext) => any) { super(value, { stackTrace: false }); } @@ -88,8 +88,8 @@ export class PostResolveToken extends Intrinsic implements IPostProcessor { return super.resolve(context); } - public postProcess(o: any, _context: IResolveContext): any { - return this.processor(o); + public postProcess(o: any, context: IResolveContext): any { + return this.processor(o, context); } } diff --git a/packages/aws-cdk-lib/core/test/resource.test.ts b/packages/aws-cdk-lib/core/test/resource.test.ts index 5212314043f7d..f32acba97d324 100644 --- a/packages/aws-cdk-lib/core/test/resource.test.ts +++ b/packages/aws-cdk-lib/core/test/resource.test.ts @@ -821,6 +821,36 @@ describe('resource', () => { }); }); + test('overrides allow cross-stack references', () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1'); + const stack2 = new Stack(app, 'Stack2'); + const res1 = new CfnResource(stack1, 'SomeResource1', { + type: 'Some::Resource1', + }); + const res2 = new CfnResource(stack2, 'SomeResource2', { + type: 'Some::Resource2', + }); + + // WHEN + res2.addPropertyOverride('Key', res1.getAtt('Value')); + + // THEN + expect( + app.synth().getStackByName(stack2.stackName).template?.Resources, + ).toEqual({ + SomeResource2: { + Properties: { + Key: { + 'Fn::ImportValue': 'Stack1:ExportsOutputFnGetAttSomeResource1Value50DD3EF0', + }, + }, + Type: 'Some::Resource2', + }, + }); + }); + describe('using mutable properties', () => { test('can be used by derived classes to specify overrides before render()', () => { const stack = new Stack(); diff --git a/packages/aws-cdk-lib/core/test/stack.test.ts b/packages/aws-cdk-lib/core/test/stack.test.ts index 7373502dc1cbf..0c1943d5f970a 100644 --- a/packages/aws-cdk-lib/core/test/stack.test.ts +++ b/packages/aws-cdk-lib/core/test/stack.test.ts @@ -1498,7 +1498,7 @@ describe('stack', () => { public _toCloudFormation() { return new PostResolveToken({ xoo: 1234, - }, props => { + }, (props, _context) => { validateString(props).assertSuccess(); }); }