diff --git a/packages/@aws-cdk/core/lib/nested-stack.ts b/packages/@aws-cdk/core/lib/nested-stack.ts index 82a123fb9243b..347731894d794 100644 --- a/packages/@aws-cdk/core/lib/nested-stack.ts +++ b/packages/@aws-cdk/core/lib/nested-stack.ts @@ -1,4 +1,5 @@ import * as crypto from 'crypto'; +import * as cxapi from '@aws-cdk/cx-api'; import { Construct, Node } from 'constructs'; import { FileAssetPackaging } from './assets'; import { Fn } from './cfn-fn'; @@ -213,6 +214,8 @@ export class NestedStack extends Stack { fileName: this.templateFile, }); + this.addResourceMetadata(this.resource, 'TemplateURL'); + // if bucketName/objectKey are cfn parameters from a stack other than the parent stack, they will // be resolved as cross-stack references like any other (see "multi" tests). this._templateUrl = `https://s3.${this._parentStack.region}.${this._parentStack.urlSuffix}/${templateLocation.bucketName}/${templateLocation.objectKey}`; @@ -230,6 +233,18 @@ export class NestedStack extends Stack { }, }); } + + private addResourceMetadata(resource: CfnResource, resourceProperty: string) { + if (!this.node.tryGetContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT)) { + return; // not enabled + } + + // tell tools such as SAM CLI that the "TemplateURL" property of this resource + // points to the nested stack template for local emulation + resource.cfnOptions.metadata = resource.cfnOptions.metadata || { }; + resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PATH_KEY] = this.templateFile; + resource.cfnOptions.metadata[cxapi.ASSET_RESOURCE_METADATA_PROPERTY_KEY] = resourceProperty; + } } /** diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 7c67f545d4b87..cd7186fea9f7d 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -661,6 +661,29 @@ describe('stack', () => { })); }); + test('asset metadata added to NestedStack resource that contains asset path and property', () => { + const app = new App(); + + // WHEN + const parentStack = new Stack(app, 'parent'); + parentStack.node.setContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT, true); + const childStack = new NestedStack(parentStack, 'child'); + new CfnResource(childStack, 'ChildResource', { type: 'Resource::Child' }); + + const assembly = app.synth(); + expect(assembly.getStackByName(parentStack.stackName).template).toEqual(expect.objectContaining({ + Resources: { + childNestedStackchildNestedStackResource7408D03F: expect.objectContaining({ + Metadata: { + 'aws:asset:path': 'parentchild13F9359B.nested.template.json', + 'aws:asset:property': 'TemplateURL', + }, + }), + }, + })); + + }); + test('cross-stack reference (substack references parent stack)', () => { // GIVEN const app = new App();