From 7f47baa22631e9f2bc6bbf8505f1b29415bf742f Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 12 Sep 2022 18:37:22 +0000 Subject: [PATCH 01/30] feat(core): automatic cross stack, cross region references This PR adds the ability to automatically create references in cross-region stacks. You can now do something like ```ts const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); const cert = new certificatemanager.Certificate(stack1, 'Cert', {...}); const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); new cloudfront.Distribution(stack2, 'Distro', { certificate: cert, }) ``` To accomplish this, I've added a new construct `ExportsReader` which creates a Lambda backed custom resource. This custom resource will return all of the CloudFormation exports in a given region. Given the above example, this would create an output in `stack1` ```json { "Outputs": { "ExportsOutputRefCert5C9FAEC110AE5C6A": { "Value": { "Ref": "Cert5C9FAEC1" }, "Export": { "Name": "East1:ExportsOutputRefCert5C9FAEC110AE5C6A" } } } } ``` And then an "import" in `stack2` which references the `ExportReader` ```json { "Resources": { "Distro87EBE6BA": { "Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { "ViewerCertificate": { "AcmCertificateArn": { "Fn::GetAtt": [ "ExportsReaderuseast1D746CBDB", "East1:ExportsOutputRefCert5C9FAEC110AE5C6A" ] } } } } } } ``` Currently this will create a single ExportsReader per region, but we could potentially update this to just use a single ExportsReader which can list exports from a list of regions. Future extensions: - Could be updated to do cross account references as well - Could be used to implement weak references --- .../export-reader-provider.ts | 90 +++++ .../get-cfn-exports-handler/index.ts | 26 ++ packages/@aws-cdk/core/lib/private/refs.ts | 104 +++++- packages/@aws-cdk/core/lib/stack.ts | 60 +--- packages/@aws-cdk/core/package.json | 1 + .../export-reader-provider.test.ts | 105 ++++++ .../get-cfn-exports-handler.test.ts | 151 +++++++++ packages/@aws-cdk/core/test/stack.test.ts | 309 +++++++++++++++++- 8 files changed, 783 insertions(+), 63 deletions(-) create mode 100644 packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts create mode 100644 packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts create mode 100644 packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts create mode 100644 packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts new file mode 100644 index 0000000000000..ce2008d20d1fb --- /dev/null +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts @@ -0,0 +1,90 @@ +import * as path from 'path'; +import { Construct } from 'constructs'; +import { CustomResource } from '../custom-resource'; +import { Intrinsic } from '../private/intrinsic'; +import { Stack } from '../stack'; +import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; + +/** + * Properties for an ExportReader + */ +export interface ExportReaderProps { + /** + * The AWS region to read Stack exports from + * + * @default - the stack region + */ + readonly region?: string; +} + +/** + * Creates a custom resource that will return a list of stack exports from a given + * AWS region. The export can then be referenced by the export name. + * + * + * @example + * declare const app: App; + * const stack1 = new Stack(app, 'East1Stack', { env: { region: 'us-east-1' } }); + * new CfnOutput(stack1, 'Output', { value: 'someValue', exportName: 'someName' }); + * + * const stack2 = new Stack(app, 'East2Stack', { env: { region: 'us-east-2' } }); + * const exportReader = new ExportReader(stack2, 'ExportReader', { region: 'us-east-1' }); + * const anotherResource = new CfnResource(stack2, 'AnotherResource', { + * Parameters: { + * SomeParam: exportReader.importValue('someName'), + * }, + * }); + */ +export class ExportReader extends Construct { + private readonly resource: CustomResource; + constructor(scope: Construct, id: string, props: ExportReaderProps) { + super(scope, id); + const stack = Stack.of(this); + const region = props.region ?? stack.region; + + const resourceType = 'Custom::CrossRegionExportReader'; + const serviceToken = CustomResourceProvider.getOrCreate(this, resourceType, { + codeDirectory: path.join(__dirname, 'get-cfn-exports-handler'), + runtime: CustomResourceProviderRuntime.NODEJS_14_X, + policyStatements: [{ + Effect: 'Allow', + Resource: '*', + Action: ['cloudformation:ListExports'], + }], + }); + + this.resource = new CustomResource(this, 'Default', { + resourceType: resourceType, + serviceToken, + properties: { + Region: region, + // This is used to determine when the function has changed. + // + // We want to lookup the exports every time + // changed for it to take effect - a good candidate for RefreshToken. + RefreshToken: Date.now().toString(), + }, + }); + + } + + /** + * Get a CloudFormation Stack export by name + * + * @example + * declare const app: App; + * const stack1 = new Stack(app, 'East1Stack', { env: { region: 'us-east-1' } }); + * new CfnOutput(stack1, 'Output', { value: 'someValue', exportName: 'someName' }); + * + * const stack2 = new Stack(app, 'East2Stack', { env: { region: 'us-east-2' } }); + * const exportReader = new ExportReader(stack2, 'ExportReader', { region: 'us-east-1' }); + * const anotherResource = new CfnResource(stack2, 'AnotherResource', { + * Parameters: { + * SomeParam: exportReader.importValue('someName'), + * }, + * }); + */ + public importValue(exportName: string): Intrinsic { + return this.resource.getAtt(exportName); + } +} diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts new file mode 100644 index 0000000000000..bde33a8a1913b --- /dev/null +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts @@ -0,0 +1,26 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { CloudFormation } from 'aws-sdk'; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + console.info(`Reading CFN Stack exports in region ${props.Region}`); + + if (event.RequestType === 'Create' || event.RequestType === 'Update') { + const cfn = new CloudFormation({ region: props.Region }); + const exports = await cfn.listExports().promise(); + const values = exports.Exports?.reduce((prev: { [key: string]: string }, curr) => { + if (curr.Name && curr.Value) { + prev[curr.Name] = curr.Value; + } + return prev; + }, {}); + console.info('Response: %j', values); + return { + Data: { + ...values, + }, + }; + } + return; +}; diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index 550af6c39127d..c6f6c54419fc2 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -2,10 +2,13 @@ // CROSS REFERENCES // ---------------------------------------------------- -import { IConstruct } from 'constructs'; +import * as cxapi from '@aws-cdk/cx-api'; +import { IConstruct, Construct } from 'constructs'; import { CfnElement } from '../cfn-element'; import { CfnOutput } from '../cfn-output'; import { CfnParameter } from '../cfn-parameter'; +import { ExportReader } from '../custom-resource-provider/get-parameter-provider'; +import { FeatureFlags } from '../feature-flags'; import { Names } from '../names'; import { Reference } from '../reference'; import { IResolvable } from '../resolvable'; @@ -14,6 +17,7 @@ import { Token, Tokenization } from '../token'; import { CfnReference } from './cfn-reference'; import { Intrinsic } from './intrinsic'; import { findTokens } from './resolve'; +import { makeUniqueId } from './uniqueid'; /** * This is called from the App level to resolve all references defined. Each @@ -33,11 +37,16 @@ export function resolveReferences(scope: IConstruct): void { } } + /** * Resolves the value for `reference` in the context of `consumer`. */ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { const producer = Stack.of(reference.target); + const producerAccount = !Token.isUnresolved(producer.account) ? producer.account : cxapi.UNKNOWN_ACCOUNT; + const producerRegion = !Token.isUnresolved(producer.region) ? producer.region : cxapi.UNKNOWN_REGION; + const consumerAccount = !Token.isUnresolved(consumer.account) ? consumer.account : cxapi.UNKNOWN_ACCOUNT; + const consumerRegion = !Token.isUnresolved(consumer.region) ? consumer.region : cxapi.UNKNOWN_REGION; // produce and consumer stacks are the same, we can just return the value itself. if (producer === consumer) { @@ -49,13 +58,20 @@ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { throw new Error('Cannot reference across apps. Consuming and producing stacks must be defined within the same CDK app.'); } - // unsupported: stacks are not in the same environment - if (producer.environment !== consumer.environment) { + // unsupported: stacks are not in the same account + if (producerAccount !== consumerAccount) { throw new Error( `Stack "${consumer.node.path}" cannot consume a cross reference from stack "${producer.node.path}". ` + 'Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack'); } + // Stacks are in the same account, but different regions + if (producerRegion !== consumerRegion) { + consumer.addDependency(producer, + `${consumer.node.path} -> ${reference.target.node.path}.${reference.displayName}`); + return createCrossRegionImportValue(reference, consumer); + } + // ---------------------------------------------------------------------- // consumer is nested in the producer (directly or indirectly) // ---------------------------------------------------------------------- @@ -170,6 +186,88 @@ function createImportValue(reference: Reference): Intrinsic { return Tokenization.reverseCompleteString(importExpr) as Intrinsic; } +/** + * Imports a value from another stack in a different region by creating an "Output" with an "ExportName" + * in the producing stack, and a "ExportsReader" custom resource in the consumer stack + * + * Returns a reference to the ExportsReader attribute which contains the exported value + */ +function createCrossRegionImportValue(reference: Reference, importStack: Stack): Intrinsic { + const exportingStack = Stack.of(reference.target); + const exportName = generateExport(exportingStack, reference); + + const constructName = makeUniqueId(['ExportsReader', exportingStack.region]); + const existing = importStack.node.tryFindChild(constructName); + const exportReader = existing + ? existing as ExportReader + : new ExportReader(importStack, constructName, { + region: exportingStack.region, + }); + + return exportReader.importValue(exportName); +} + +function getCreateExportsScope(stack: Stack) { + const exportsName = 'Exports'; + let stackExports = stack.node.tryFindChild(exportsName) as Construct; + if (stackExports === undefined) { + stackExports = new Construct(stack, exportsName); + } + + return stackExports; +} + +export function generateExport(stack: Stack, reference: Reference): string { // if exportValue is being called manually (which is pre onPrepare) then the logicalId + // could potentially be changed by a call to overrideLogicalId. This would cause our Export/Import + // to have an incorrect id. For a better user experience, lock the logicalId and throw an error + // if the user tries to override the id _after_ calling exportValue + if (CfnElement.isCfnElement(reference.target)) { + reference.target._lockLogicalId(); + } + + // "teleport" the value here, in case it comes from a nested stack. This will also + // ensure the value is from our own scope. + const exportable = referenceNestedStackValueInParent(reference, stack); + + // Ensure a singleton "Exports" scoping Construct + // This mostly exists to trigger LogicalID munging, which would be + // disabled if we parented constructs directly under Stack. + // Also it nicely prevents likely construct name clashes + const exportScope = getCreateExportsScope(stack); + + // Ensure a singleton CfnOutput for this value + const resolved = stack.resolve(exportable); + const id = 'Output' + JSON.stringify(resolved); + const exportName = generateExportName(exportScope, id); + + if (Token.isUnresolved(exportName)) { + throw new Error(`unresolved token in generated export name: ${JSON.stringify(stack.resolve(exportName))}`); + } + + const output = exportScope.node.tryFindChild(id) as CfnOutput; + if (!output) { + new CfnOutput(exportScope, id, { value: Token.asString(exportable), exportName }); + } + + return exportName; +} + +function generateExportName(stackExports: Construct, id: string) { + const stackRelativeExports = FeatureFlags.of(stackExports).isEnabled(cxapi.STACK_RELATIVE_EXPORTS_CONTEXT); + const stack = Stack.of(stackExports); + + const components = [ + ...stackExports.node.scopes + .slice(stackRelativeExports ? stack.node.scopes.length : 2) + .map(c => c.node.id), + id, + ]; + const prefix = stack.stackName ? stack.stackName + ':' : ''; + const localPart = makeUniqueId(components); + const maxLength = 255; + return prefix + localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); +} + // ------------------------------------------------------------------------------------------------ // nested stacks // ------------------------------------------------------------------------------------------------ diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 8ca911b83b8aa..8227fd069fd30 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -907,37 +907,7 @@ export class Stack extends Construct implements ITaggable { throw new Error('exportValue: either supply \'name\' or make sure to export a resource attribute (like \'bucket.bucketName\')'); } - // if exportValue is being called manually (which is pre onPrepare) then the logicalId - // could potentially be changed by a call to overrideLogicalId. This would cause our Export/Import - // to have an incorrect id. For a better user experience, lock the logicalId and throw an error - // if the user tries to override the id _after_ calling exportValue - if (CfnElement.isCfnElement(resolvable.target)) { - resolvable.target._lockLogicalId(); - } - - // "teleport" the value here, in case it comes from a nested stack. This will also - // ensure the value is from our own scope. - const exportable = referenceNestedStackValueInParent(resolvable, this); - - // Ensure a singleton "Exports" scoping Construct - // This mostly exists to trigger LogicalID munging, which would be - // disabled if we parented constructs directly under Stack. - // Also it nicely prevents likely construct name clashes - const exportsScope = getCreateExportsScope(this); - - // Ensure a singleton CfnOutput for this value - const resolved = this.resolve(exportable); - const id = 'Output' + JSON.stringify(resolved); - const exportName = generateExportName(exportsScope, id); - - if (Token.isUnresolved(exportName)) { - throw new Error(`unresolved token in generated export name: ${JSON.stringify(this.resolve(exportName))}`); - } - - const output = exportsScope.node.tryFindChild(id) as CfnOutput; - if (!output) { - new CfnOutput(exportsScope, id, { value: Token.asString(exportable), exportName }); - } + const exportName = generateExport(this, resolvable); return Fn.importValue(exportName); } @@ -1323,32 +1293,6 @@ function makeStackName(components: string[]) { return makeUniqueResourceName(components, { maxLength: 128 }); } -function getCreateExportsScope(stack: Stack) { - const exportsName = 'Exports'; - let stackExports = stack.node.tryFindChild(exportsName) as Construct; - if (stackExports === undefined) { - stackExports = new Construct(stack, exportsName); - } - - return stackExports; -} - -function generateExportName(stackExports: Construct, id: string) { - const stackRelativeExports = FeatureFlags.of(stackExports).isEnabled(cxapi.STACK_RELATIVE_EXPORTS_CONTEXT); - const stack = Stack.of(stackExports); - - const components = [ - ...stackExports.node.scopes - .slice(stackRelativeExports ? stack.node.scopes.length : 2) - .map(c => c.node.id), - id, - ]; - const prefix = stack.stackName ? stack.stackName + ':' : ''; - const localPart = makeUniqueId(components); - const maxLength = 255; - return prefix + localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); -} - interface StackDependency { stack: Stack; reasons: string[]; @@ -1390,7 +1334,7 @@ import { DefaultStackSynthesizer, IStackSynthesizer, ISynthesisSession, LegacySt import { Stage } from './stage'; import { ITaggable, TagManager } from './tag-manager'; import { Token, Tokenization } from './token'; -import { referenceNestedStackValueInParent } from './private/refs'; +import { generateExport } from './private/refs'; import { Fact, RegionInfo } from '@aws-cdk/region-info'; import { deployTimeLookup } from './private/region-lookup'; import { makeUniqueResourceName } from './private/unique-resource-name'; diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index c053496635001..0165faff4cf37 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -189,6 +189,7 @@ "@types/jest": "^27.5.2", "@types/lodash": "^4.14.184", "@types/minimatch": "^3.0.5", + "aws-sdk": "^2.848.0", "@types/node": "^14.18.27", "@types/sinon": "^9.0.11", "fast-check": "^2.25.0", diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts new file mode 100644 index 0000000000000..a6f7d9d2f0a1d --- /dev/null +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts @@ -0,0 +1,105 @@ +import { App, Stack, AssetStaging } from '../../lib'; +import { ExportReader } from '../../lib/custom-resource-provider/export-reader-provider'; +import { toCloudFormation } from '../util'; + + +describe('export reader provider', () => { + test('basic configuration', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app); + + // WHEN + new ExportReader(stack, 'ExportReader', { + region: 'us-east-1', + }); + + // THEN + const cfn = toCloudFormation(stack); + const staging = stack.node.tryFindChild('Custom::CrossRegionExportReaderCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; + const assetHash = staging.assetHash; + + expect(cfn).toEqual({ + Resources: { + CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD: { + Type: 'AWS::IAM::Role', + Properties: { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + }, + ], + }, + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Action: [ + 'cloudformation:ListExports', + ], + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Inline', + }, + ], + ManagedPolicyArns: [ + { + 'Fn::Sub': 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', + }, + ], + }, + }, + ExportReader: { + DeletionPolicy: 'Delete', + Properties: { + RefreshToken: expect.any(String), + Region: 'us-east-1', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', + 'Arn', + ], + }, + }, + Type: 'Custom::CrossRegionExportReader', + UpdateReplacePolicy: 'Delete', + }, + CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: { + 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', + }, + S3Key: `${assetHash}.zip`, + }, + Timeout: 900, + MemorySize: 128, + Handler: '__entrypoint__.handler', + Role: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + 'Arn', + ], + }, + Runtime: 'nodejs14.x', + }, + DependsOn: [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + ], + }, + }, + }); + }); +}); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts new file mode 100644 index 0000000000000..aa37b88b61bc7 --- /dev/null +++ b/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts @@ -0,0 +1,151 @@ +import { handler } from '../../lib/custom-resource-provider/get-cfn-exports-handler'; + +const mockListExports = jest.fn(); +jest.mock('aws-sdk', () => { + return { + CloudFormation: jest.fn(() => { + return { + listExports: jest.fn(() => { + return { + promise: () => mockListExports(), + }; + }), + }; + }), + }; +}); + +describe('get-cfn-exports entrypoint', () => { + beforeEach(() => { + mockListExports.mockReset(); + }); + afterEach(() => { + jest.restoreAllMocks(); + }); + test('Create event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + }, + }); + mockListExports.mockImplementation(() => { + return { + Exports: [{ + Name: 'ExportName', + Value: 'ExportValue', + }], + }; + }); + + // WHEN + const response = await handler(event); + + // THEN + expect(mockListExports).toHaveBeenCalledTimes(1); + expect(response).toEqual({ + Data: { + ExportName: 'ExportValue', + }, + }); + }); + + test('Update event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + }, + }); + mockListExports.mockImplementation(() => { + return { + Exports: [{ + Name: 'ExportName', + Value: 'ExportValue', + }], + }; + }); + + // WHEN + const response = await handler(event); + + // THEN + expect(mockListExports).toHaveBeenCalledTimes(1); + expect(response).toEqual({ + Data: { + ExportName: 'ExportValue', + }, + }); + }); + + test('Delete event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Delete', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + }, + }); + mockListExports.mockImplementation(() => { + return { + Exports: [{ + Name: 'ExportName', + Value: 'ExportValue', + }], + }; + }); + + // WHEN + const response = await handler(event); + + // THEN + expect(mockListExports).toHaveBeenCalledTimes(0); + expect(response).toBeUndefined; + }); + + test('no exports', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + }, + }); + mockListExports.mockImplementationOnce(() => { + return { + Exports: [], + }; + }); + + // WHEN + const response = await handler(event); + + // THEN + expect(mockListExports).toHaveBeenCalledTimes(1); + expect(response).toEqual({ + Data: {}, + }); + }); +}); + +function makeEvent(req: Partial): AWSLambda.CloudFormationCustomResourceEvent { + return { + LogicalResourceId: '', + RequestId: '', + ResourceType: '', + ResponseURL: '', + ServiceToken: '', + StackId: '', + ResourceProperties: { + ServiceToken: '', + ...req.ResourceProperties, + }, + ...req, + } as any; +} diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index b2fc01b337a87..af9174fdbee99 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -462,6 +462,311 @@ describe('stack', () => { }); }); + test('cross-region stack references', () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const exportResource = new CfnResource(stack1, 'SomeResourceExport', { + type: 'AWS::S3::Bucket', + }); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); + + // WHEN - used in another stack + new CfnResource(stack2, 'SomeResource', { + type: 'AWS::S3::Bucket', + properties: { + Name: exportResource.getAtt('name'), + }, + }); + + const assembly = app.synth(); + const template2 = assembly.getStackByName(stack2.stackName).template; + const template1 = assembly.getStackByName(stack1.stackName).template; + + // THEN + expect(template1).toMatchObject({ + Resources: { + SomeResourceExport: { + Type: 'AWS::S3::Bucket', + }, + }, + Outputs: { + ExportsOutputFnGetAttSomeResourceExportname33D556F7: { + Export: { + Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', + }, + Value: { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + }, + }, + }); + + expect(template2).toMatchObject({ + Resources: { + SomeResource: { + Type: 'AWS::S3::Bucket', + Properties: { + Name: { + 'Fn::GetAtt': [ + 'ExportsReaderuseast1D746CBDB', + 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', + ], + }, + }, + }, + CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + Type: 'AWS::Lambda::Function', + }, + ExportsReaderuseast1D746CBDB: { + Type: 'Custom::CrossRegionExportReader', + }, + }, + }); + }); + + test('cross region stack references with multiple stacks', () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const exportResource = new CfnResource(stack1, 'SomeResourceExport', { + type: 'AWS::S3::Bucket', + }); + const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-east-1' } }); + const exportResource3 = new CfnResource(stack3, 'SomeResourceExport', { + type: 'AWS::S3::Bucket', + }); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); + + // WHEN - used in another stack + new CfnResource(stack2, 'SomeResource', { + type: 'AWS::S3::Bucket', + properties: { + Name: exportResource.getAtt('name'), + Other: exportResource.getAtt('other'), + Other2: exportResource3.getAtt('other2'), + }, + }); + + const assembly = app.synth(); + const template2 = assembly.getStackByName(stack2.stackName).template; + const template3 = assembly.getStackByName(stack3.stackName).template; + const template1 = assembly.getStackByName(stack1.stackName).template; + + // THEN + const exportReaders = Object.entries(template2.Resources).filter((res: [string, any]) => res[1].Type === 'Custom::CrossRegionExportReader'); + expect(exportReaders.length).toEqual(1); + expect(template3).toMatchObject({ + Resources: { + SomeResourceExport: { + Type: 'AWS::S3::Bucket', + }, + }, + Outputs: { + ExportsOutputFnGetAttSomeResourceExportother2AC0F424D: { + Export: { + Name: 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', + }, + Value: { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other2', + ], + }, + }, + }, + }); + expect(template1).toMatchObject({ + Resources: { + SomeResourceExport: { + Type: 'AWS::S3::Bucket', + }, + }, + Outputs: { + ExportsOutputFnGetAttSomeResourceExportname33D556F7: { + Export: { + Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', + }, + Value: { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + }, + ExportsOutputFnGetAttSomeResourceExportotherA189B4B9: { + Export: { + Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', + }, + Value: { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other', + ], + }, + }, + }, + }); + + expect(template2).toMatchObject({ + Resources: { + SomeResource: { + Type: 'AWS::S3::Bucket', + Properties: { + Name: { + 'Fn::GetAtt': [ + 'ExportsReaderuseast1D746CBDB', + 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', + ], + }, + Other: { + 'Fn::GetAtt': [ + 'ExportsReaderuseast1D746CBDB', + 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', + ], + }, + Other2: { + 'Fn::GetAtt': [ + 'ExportsReaderuseast1D746CBDB', + 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', + ], + }, + }, + }, + CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + Type: 'AWS::Lambda::Function', + }, + ExportsReaderuseast1D746CBDB: { + Type: 'Custom::CrossRegionExportReader', + }, + }, + }); + }); + + test('cross region stack references with multiple stacks and multiple regions', () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const exportResource = new CfnResource(stack1, 'SomeResourceExport', { + type: 'AWS::S3::Bucket', + }); + const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-west-1' } }); + const exportResource3 = new CfnResource(stack3, 'SomeResourceExport', { + type: 'AWS::S3::Bucket', + }); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); + + // WHEN - used in another stack + new CfnResource(stack2, 'SomeResource', { + type: 'AWS::S3::Bucket', + properties: { + Name: exportResource.getAtt('name'), + Other: exportResource.getAtt('other'), + Other2: exportResource3.getAtt('other2'), + }, + }); + + const assembly = app.synth(); + const template2 = assembly.getStackByName(stack2.stackName).template; + const template3 = assembly.getStackByName(stack3.stackName).template; + const template1 = assembly.getStackByName(stack1.stackName).template; + + // THEN + const exportReaders = Object.entries(template2.Resources).filter((res: [string, any]) => res[1].Type === 'Custom::CrossRegionExportReader'); + expect(exportReaders.length).toEqual(2); + expect(template3).toMatchObject({ + Resources: { + SomeResourceExport: { + Type: 'AWS::S3::Bucket', + }, + }, + Outputs: { + ExportsOutputFnGetAttSomeResourceExportother2AC0F424D: { + Export: { + Name: 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', + }, + Value: { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other2', + ], + }, + }, + }, + }); + expect(template1).toMatchObject({ + Resources: { + SomeResourceExport: { + Type: 'AWS::S3::Bucket', + }, + }, + Outputs: { + ExportsOutputFnGetAttSomeResourceExportname33D556F7: { + Export: { + Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', + }, + Value: { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + }, + ExportsOutputFnGetAttSomeResourceExportotherA189B4B9: { + Export: { + Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', + }, + Value: { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other', + ], + }, + }, + }, + }); + + expect(template2).toMatchObject({ + Resources: { + SomeResource: { + Type: 'AWS::S3::Bucket', + Properties: { + Name: { + 'Fn::GetAtt': [ + 'ExportsReaderuseast1D746CBDB', + 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', + ], + }, + Other: { + 'Fn::GetAtt': [ + 'ExportsReaderuseast1D746CBDB', + 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', + ], + }, + Other2: { + 'Fn::GetAtt': [ + 'ExportsReaderuswest1619FF90A', + 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', + ], + }, + }, + }, + CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + Type: 'AWS::Lambda::Function', + }, + ExportsReaderuseast1D746CBDB: { + Type: 'Custom::CrossRegionExportReader', + Properties: { + Region: 'us-east-1', + }, + }, + }, + }); + }); + test('cross stack references and dependencies work within child stacks (non-nested)', () => { // GIVEN const app = new App({ @@ -826,12 +1131,12 @@ describe('stack', () => { expect(stack2.dependencies.map(s => s.node.id)).toEqual(['Stack1']); }); - test('cannot create references to stacks in other regions/accounts', () => { + test('cannot create references to stacks in other accounts', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { env: { account: '123456789012', region: 'es-norst-1' } }); const account1 = new ScopedAws(stack1).accountId; - const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789012', region: 'es-norst-2' } }); + const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789013', region: 'es-norst-2' } }); // WHEN new CfnParameter(stack2, 'SomeParameter', { type: 'String', default: account1 }); From 4ce8290767829cff9528d1843d7ba4e0770edc0c Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Tue, 13 Sep 2022 16:41:01 +0000 Subject: [PATCH 02/30] fixing build --- packages/@aws-cdk/core/test/stack.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index af9174fdbee99..7de34ce909410 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -1136,7 +1136,7 @@ describe('stack', () => { const app = new App(); const stack1 = new Stack(app, 'Stack1', { env: { account: '123456789012', region: 'es-norst-1' } }); const account1 = new ScopedAws(stack1).accountId; - const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789013', region: 'es-norst-2' } }); + const stack2 = new Stack(app, 'Stack2', { env: { account: '11111111111', region: 'es-norst-2' } }); // WHEN new CfnParameter(stack2, 'SomeParameter', { type: 'String', default: account1 }); From 97416cc50fd171d917e57e79a1153748cffdcd17 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Wed, 14 Sep 2022 13:17:52 +0000 Subject: [PATCH 03/30] adding some tests and integration tests --- .../@aws-cdk/aws-cloudformation/package.json | 1 + .../cdk.out | 1 + .../cross-region-consumer.assets.json | 48 + .../cross-region-consumer.template.json | 159 +++ .../cross-region-producer.assets.json | 34 + .../cross-region-producer.template.json | 86 ++ ...erIntegNested815BEF8A.nested.template.json | 106 ++ ...erIntegNested3342EBEB.nested.template.json | 19 + ...efaultTestDeployAssertAB7415FD.assets.json | 19 + ...aultTestDeployAssertAB7415FD.template.json | 36 + .../integ.json | 13 + .../manifest.json | 249 +++++ .../integ.core-cross-region-references.ts | 65 ++ .../test/integ.pipeline-with-replication.ts | 68 ++ .../cdk.out | 1 + ...efaultTestDeployAssert88EAAC45.assets.json | 19 + ...aultTestDeployAssert88EAAC45.template.json | 36 + .../integ.json | 13 + .../manifest.json | 327 +++++++ .../stack1.assets.json | 34 + .../stack1.template.json | 233 +++++ .../stack2.assets.json | 48 + .../stack2.template.json | 913 ++++++++++++++++++ packages/@aws-cdk/core/lib/private/refs.ts | 21 +- packages/@aws-cdk/core/lib/resource.ts | 10 +- .../core/test/cross-environment-token.test.ts | 97 ++ .../get-cfn-exports-handler.test.ts | 1 + .../@aws-cdk/core/test/nested-stack.test.ts | 138 ++- packages/@aws-cdk/core/test/stack.test.ts | 32 +- packages/@aws-cdk/cx-api/README.md | 19 + packages/@aws-cdk/cx-api/lib/features.ts | 8 + 31 files changed, 2841 insertions(+), 13 deletions(-) create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionproducerIntegNested3342EBEB.nested.template.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.template.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.template.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.assets.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.template.json diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index b141331a3a3fb..3d71944062137 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -85,6 +85,7 @@ "@aws-cdk/aws-ssm": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/aws-lambda": "^8.10.103", diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json new file mode 100644 index 0000000000000..fe3f26024eef9 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -0,0 +1,48 @@ +{ + "version": "21.0.0", + "files": { + "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf": { + "source": { + "path": "asset.1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf", + "packaging": "zip" + }, + "destinations": { + "580348834564-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "objectKey": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + } + } + }, + "4835de933cab0e010b7250e075bf55e0432d724c682a5a4658bac31d2fd35178": { + "source": { + "path": "crossregionconsumerIntegNested815BEF8A.nested.template.json", + "packaging": "file" + }, + "destinations": { + "580348834564-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "objectKey": "4835de933cab0e010b7250e075bf55e0432d724c682a5a4658bac31d2fd35178.json", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + } + } + }, + "8fb619a5eea2e71b6943472a86e001a77df08f312a80b3038c556fa3510491a1": { + "source": { + "path": "cross-region-consumer.template.json", + "packaging": "file" + }, + "destinations": { + "580348834564-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "objectKey": "8fb619a5eea2e71b6943472a86e001a77df08f312a80b3038c556fa3510491a1.json", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json new file mode 100644 index 0000000000000..9eda26a0d8d26 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -0,0 +1,159 @@ +{ + "Resources": { + "IntegNestedNestedStackIntegNestedNestedStackResource168C5881": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.us-east-2.", + { + "Ref": "AWS::URLSuffix" + }, + "/cdk-hnb659fds-assets-580348834564-us-east-2/4835de933cab0e010b7250e075bf55e0432d724c682a5a4658bac31d2fd35178.json" + ] + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "IntegParameter02A1817A4": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": { + "Fn::GetAtt": [ + "ExportsReaderuseast1D746CBDB", + "cross-region-producer:ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" + ] + }, + "Name": "integ-parameter0" + } + }, + "IntegParameter1EDBEF1C6": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": { + "Fn::GetAtt": [ + "ExportsReaderuseast1D746CBDB", + "cross-region-producer:ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" + ] + }, + "Name": "integ-parameter1" + } + }, + "ExportsReaderuseast1D746CBDB": { + "Type": "Custom::CrossRegionExportReader", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", + "Arn" + ] + }, + "Region": "us-east-1", + "RefreshToken": "1663094629919" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": "*", + "Action": [ + "cloudformation:ListExports" + ] + } + ] + } + } + ] + } + }, + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json new file mode 100644 index 0000000000000..dd54e15cb259f --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -0,0 +1,34 @@ +{ + "version": "21.0.0", + "files": { + "db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc": { + "source": { + "path": "crossregionproducerIntegNested3342EBEB.nested.template.json", + "packaging": "file" + }, + "destinations": { + "580348834564-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", + "objectKey": "db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" + } + } + }, + "8c7abc3c42e024d8c1d7fcf2a5f9bcfb13b3eaf75334cffef6d4564b22a2a209": { + "source": { + "path": "cross-region-producer.template.json", + "packaging": "file" + }, + "destinations": { + "580348834564-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", + "objectKey": "8c7abc3c42e024d8c1d7fcf2a5f9bcfb13b3eaf75334cffef6d4564b22a2a209.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json new file mode 100644 index 0000000000000..56087aa324c26 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -0,0 +1,86 @@ +{ + "Resources": { + "IntegNestedNestedStackIntegNestedNestedStackResource168C5881": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": { + "Fn::Join": [ + "", + [ + "https://s3.us-east-1.", + { + "Ref": "AWS::URLSuffix" + }, + "/cdk-hnb659fds-assets-580348834564-us-east-1/db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json" + ] + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "IntegQueue3A18718A": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Outputs": { + "ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F": { + "Value": { + "Fn::GetAtt": [ + "IntegQueue3A18718A", + "QueueName" + ] + }, + "Export": { + "Name": "cross-region-producer:ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" + } + }, + "ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE": { + "Value": { + "Fn::GetAtt": [ + "IntegNestedNestedStackIntegNestedNestedStackResource168C5881", + "Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" + ] + }, + "Export": { + "Name": "cross-region-producer:ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json new file mode 100644 index 0000000000000..6e36d442eebd3 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json @@ -0,0 +1,106 @@ +{ + "Resources": { + "IntegNestedParameter04B9B8A01": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": { + "Fn::GetAtt": [ + "ExportsReaderuseast1D746CBDB", + "cross-region-producer:ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" + ] + }, + "Name": "integ-nested-parameter0" + } + }, + "IntegNestedParameter1DE6274D4": { + "Type": "AWS::SSM::Parameter", + "Properties": { + "Type": "String", + "Value": { + "Fn::GetAtt": [ + "ExportsReaderuseast1D746CBDB", + "cross-region-producer:ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" + ] + }, + "Name": "integ-nested-parameter1" + } + }, + "ExportsReaderuseast1D746CBDB": { + "Type": "Custom::CrossRegionExportReader", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", + "Arn" + ] + }, + "Region": "us-east-1", + "RefreshToken": "1663094629916" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": "*", + "Action": [ + "cloudformation:ListExports" + ] + } + ] + } + } + ] + } + }, + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionproducerIntegNested3342EBEB.nested.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionproducerIntegNested3342EBEB.nested.template.json new file mode 100644 index 0000000000000..4dbe064e0bc4a --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionproducerIntegNested3342EBEB.nested.template.json @@ -0,0 +1,19 @@ +{ + "Resources": { + "NestedIntegQueue0DFF7C28": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Outputs": { + "crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName": { + "Value": { + "Fn::GetAtt": [ + "NestedIntegQueue0DFF7C28", + "QueueName" + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json new file mode 100644 index 0000000000000..5a6db16e6a7ef --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/integ.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/integ.json new file mode 100644 index 0000000000000..42ba38db2813c --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "21.0.0", + "testCases": { + "cross-region-references/DefaultTest": { + "stacks": [ + "cross-region-consumer" + ], + "stackUpdateWorkflow": false, + "assertionStack": "cross-region-references/DefaultTest/DeployAssert", + "assertionStackName": "crossregionreferencesDefaultTestDeployAssertAB7415FD" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..dd4be22840eaf --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -0,0 +1,249 @@ +{ + "version": "21.0.0", + "artifacts": { + "cross-region-producer.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cross-region-producer.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cross-region-producer": { + "type": "aws:cloudformation:stack", + "environment": "aws://580348834564/us-east-1", + "properties": { + "templateFile": "cross-region-producer.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-1/8c7abc3c42e024d8c1d7fcf2a5f9bcfb13b3eaf75334cffef6d4564b22a2a209.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cross-region-producer.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cross-region-producer.assets" + ], + "metadata": { + "/cross-region-producer/IntegNested/NestedIntegQueue/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NestedIntegQueue0DFF7C28" + } + ], + "/cross-region-producer/IntegNested/crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName": [ + { + "type": "aws:cdk:logicalId", + "data": "crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" + } + ], + "/cross-region-producer/IntegNested.NestedStack/IntegNested.NestedStackResource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegNestedNestedStackIntegNestedNestedStackResource168C5881" + } + ], + "/cross-region-producer/IntegQueue/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegQueue3A18718A" + } + ], + "/cross-region-producer/Exports/Output{\"Fn::GetAtt\":[\"IntegQueue3A18718A\",\"QueueName\"]}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" + } + ], + "/cross-region-producer/Exports/Output{\"Fn::GetAtt\":[\"IntegNestedNestedStackIntegNestedNestedStackResource168C5881\",\"Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName\"]}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" + } + ], + "/cross-region-producer/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cross-region-producer/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cross-region-producer" + }, + "cross-region-consumer.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "cross-region-consumer.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "cross-region-consumer": { + "type": "aws:cloudformation:stack", + "environment": "aws://580348834564/us-east-2", + "properties": { + "templateFile": "cross-region-consumer.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-2/8fb619a5eea2e71b6943472a86e001a77df08f312a80b3038c556fa3510491a1.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "cross-region-consumer.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-2", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "cross-region-producer", + "cross-region-consumer.assets" + ], + "metadata": { + "/cross-region-consumer/IntegNested/IntegNestedParameter0/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegNestedParameter04B9B8A01" + } + ], + "/cross-region-consumer/IntegNested/IntegNestedParameter1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegNestedParameter1DE6274D4" + } + ], + "/cross-region-consumer/IntegNested/ExportsReaderuseast1D746CBDB/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsReaderuseast1D746CBDB" + } + ], + "/cross-region-consumer/IntegNested/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + } + ], + "/cross-region-consumer/IntegNested/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" + } + ], + "/cross-region-consumer/IntegNested.NestedStack/IntegNested.NestedStackResource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegNestedNestedStackIntegNestedNestedStackResource168C5881" + } + ], + "/cross-region-consumer/IntegParameter0/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegParameter02A1817A4" + } + ], + "/cross-region-consumer/IntegParameter1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "IntegParameter1EDBEF1C6" + } + ], + "/cross-region-consumer/ExportsReaderuseast1D746CBDB/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsReaderuseast1D746CBDB" + } + ], + "/cross-region-consumer/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + } + ], + "/cross-region-consumer/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" + } + ], + "/cross-region-consumer/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cross-region-consumer/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cross-region-consumer" + }, + "crossregionreferencesDefaultTestDeployAssertAB7415FD.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "crossregionreferencesDefaultTestDeployAssertAB7415FD": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "crossregionreferencesDefaultTestDeployAssertAB7415FD.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "crossregionreferencesDefaultTestDeployAssertAB7415FD.assets" + ], + "metadata": { + "/cross-region-references/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "cross-region-references/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts new file mode 100644 index 0000000000000..7df8d77b604e1 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts @@ -0,0 +1,65 @@ +import { Queue, IQueue } from '@aws-cdk/aws-sqs'; +import { StringParameter } from '@aws-cdk/aws-ssm'; +import { App, Stack, StackProps, NestedStack } from '@aws-cdk/core'; +import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; + +// GIVEN +const app = new App({ + treeMetadata: false, +}); +app.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); + +class ProducerStack extends Stack { + public readonly queue: IQueue; + public readonly nestedQueue: IQueue; + constructor(scope: Construct, id: string) { + super(scope, id, { + env: { + region: 'us-east-1', + account: process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, + }, + }); + const nested = new NestedStack(this, 'IntegNested'); + this.queue = new Queue(this, 'IntegQueue'); + this.nestedQueue = new Queue(nested, 'NestedIntegQueue'); + } +} + +interface ConsumerStackProps extends StackProps { + readonly queues: IQueue[]; +} +class ConsumerStack extends Stack { + constructor(scope: Construct, id: string, props: ConsumerStackProps) { + super(scope, id, { + ...props, + env: { + region: 'us-east-2', + account: process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, + }, + }); + + const nested = new NestedStack(this, 'IntegNested'); + props.queues.forEach((queue, i) => { + new StringParameter(this, 'IntegParameter'+i, { + parameterName: 'integ-parameter'+i, + stringValue: queue.queueName, + }); + new StringParameter(nested, 'IntegNestedParameter'+i, { + parameterName: 'integ-nested-parameter'+i, + stringValue: queue.queueName, + }); + }); + } +} +const producer = new ProducerStack(app, 'cross-region-producer'); +const testCase = new ConsumerStack(app, 'cross-region-consumer', { + queues: [producer.queue, producer.nestedQueue], +}); + +// THEN +new IntegTest(app, 'cross-region-references', { + testCases: [testCase], + stackUpdateWorkflow: false, +}); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts new file mode 100644 index 0000000000000..38f1c5e575204 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts @@ -0,0 +1,68 @@ +import { PipelineProject } from '@aws-cdk/aws-codebuild'; +import { Pipeline, Artifact } from '@aws-cdk/aws-codepipeline'; +import { Key } from '@aws-cdk/aws-kms'; +import { Bucket } from '@aws-cdk/aws-s3'; +import { App, Stack, RemovalPolicy } from '@aws-cdk/core'; +import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import { S3SourceAction, CodeBuildAction } from '../lib'; + + +const account = process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT; +const app = new App({ + treeMetadata: false, +}); +app.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); +const stack1 = new Stack(app, 'stack1', { + env: { + region: 'us-east-1', + account, + }, +}); +const stack2 = new Stack(app, 'stack2', { + env: { + region: 'us-east-2', + account, + }, +}); + + +const key = new Key(stack1, 'ReplicationKey'); +const bucket = new Bucket(stack1, 'ReplicationBucket', { + encryptionKey: key, + autoDeleteObjects: true, + removalPolicy: RemovalPolicy.DESTROY, +}); + +const artifact = new Artifact(); +const pipeline = new Pipeline(stack2, 'Pipeline', { + crossRegionReplicationBuckets: { + 'us-east-1': bucket, + }, +}); +const sourceBucket = new Bucket(stack2, 'SourceBucket', { + autoDeleteObjects: true, + removalPolicy: RemovalPolicy.DESTROY, +}); +pipeline.addStage({ + stageName: 'source', + actions: [new S3SourceAction({ + bucket: sourceBucket, + output: artifact, + bucketKey: '/somepath', + actionName: 'Source', + })], +}); +pipeline.addStage({ + stageName: 'stage2', + actions: [new CodeBuildAction({ + input: artifact, + actionName: 'Build', + project: new PipelineProject(stack2, 'Build'), + })], +}); + +new IntegTest(app, 'codepipeline-integ-test', { + testCases: [stack2], + stackUpdateWorkflow: false, +}); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets.json new file mode 100644 index 0000000000000..94406e02ec986 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "codepipelineintegtestDefaultTestDeployAssert88EAAC45.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/codepipelineintegtestDefaultTestDeployAssert88EAAC45.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json new file mode 100644 index 0000000000000..e5fdb50dd8612 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "21.0.0", + "testCases": { + "codepipeline-integ-test/DefaultTest": { + "stacks": [ + "stack2" + ], + "stackUpdateWorkflow": false, + "assertionStack": "codepipeline-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "codepipelineintegtestDefaultTestDeployAssert88EAAC45" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..878c62becb229 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json @@ -0,0 +1,327 @@ +{ + "version": "21.0.0", + "artifacts": { + "stack1.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "stack1.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "stack1": { + "type": "aws:cloudformation:stack", + "environment": "aws://580348834564/us-east-1", + "properties": { + "templateFile": "stack1.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-1/145afe7f6542ca5ca27fff75095a0699c976f6ac6e2644709c87c677935d409f.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "stack1.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "stack1.assets" + ], + "metadata": { + "/stack1/ReplicationKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ReplicationKeyFCE40BF4" + } + ], + "/stack1/ReplicationBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ReplicationBucket70D68737" + } + ], + "/stack1/ReplicationBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ReplicationBucketPolicyADD8A584" + } + ], + "/stack1/ReplicationBucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ReplicationBucketAutoDeleteObjectsCustomResourceF7D32567" + } + ], + "/stack1/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + } + ], + "/stack1/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + } + ], + "/stack1/Exports/Output{\"Ref\":\"ReplicationBucket70D68737\"}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputRefReplicationBucket70D68737E331A47A" + } + ], + "/stack1/Exports/Output{\"Fn::GetAtt\":[\"ReplicationKeyFCE40BF4\",\"Arn\"]}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" + } + ], + "/stack1/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/stack1/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "stack1" + }, + "stack2.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "stack2.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "stack2": { + "type": "aws:cloudformation:stack", + "environment": "aws://580348834564/us-east-2", + "properties": { + "templateFile": "stack2.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-2/e694369aee44edaa5736dd32253bc027102b27eef2b11d044330aea055806fbd.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "stack2.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-2", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "stack1", + "stack2.assets" + ], + "metadata": { + "/stack2/Pipeline/ArtifactsBucketEncryptionKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucketEncryptionKey01D58D69" + } + ], + "/stack2/Pipeline/ArtifactsBucketEncryptionKeyAlias/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucketEncryptionKeyAlias5C510EEE" + } + ], + "/stack2/Pipeline/ArtifactsBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucket22248F97" + } + ], + "/stack2/Pipeline/ArtifactsBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucketPolicyD4F9712A" + } + ], + "/stack2/Pipeline/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineRoleD68726F7" + } + ], + "/stack2/Pipeline/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineRoleDefaultPolicyC7A05455" + } + ], + "/stack2/Pipeline/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineC660917D" + } + ], + "/stack2/Pipeline/source/Source/CodePipelineActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelinesourceSourceCodePipelineActionRoleC03B7ECA" + } + ], + "/stack2/Pipeline/source/Source/CodePipelineActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelinesourceSourceCodePipelineActionRoleDefaultPolicy6B296460" + } + ], + "/stack2/Pipeline/stage2/Build/CodePipelineActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Pipelinestage2BuildCodePipelineActionRole6D7E5309" + } + ], + "/stack2/Pipeline/stage2/Build/CodePipelineActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Pipelinestage2BuildCodePipelineActionRoleDefaultPolicy4431A4F5" + } + ], + "/stack2/SourceBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketDDD2130A" + } + ], + "/stack2/SourceBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketPolicy703DFBF9" + } + ], + "/stack2/SourceBucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketAutoDeleteObjectsCustomResourceC68FC040" + } + ], + "/stack2/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + } + ], + "/stack2/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + } + ], + "/stack2/Build/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "BuildRoleB7C66CB2" + } + ], + "/stack2/Build/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "BuildRoleDefaultPolicyEAC4E6D6" + } + ], + "/stack2/Build/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Build45A36621" + } + ], + "/stack2/ExportsReaderuseast1D746CBDB/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsReaderuseast1D746CBDB" + } + ], + "/stack2/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + } + ], + "/stack2/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" + } + ], + "/stack2/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/stack2/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "stack2" + }, + "codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "codepipelineintegtestDefaultTestDeployAssert88EAAC45": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "codepipelineintegtestDefaultTestDeployAssert88EAAC45.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets" + ], + "metadata": { + "/codepipeline-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/codepipeline-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "codepipeline-integ-test/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json new file mode 100644 index 0000000000000..9dcaab6104f96 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json @@ -0,0 +1,34 @@ +{ + "version": "21.0.0", + "files": { + "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { + "source": { + "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", + "packaging": "zip" + }, + "destinations": { + "580348834564-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", + "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" + } + } + }, + "145afe7f6542ca5ca27fff75095a0699c976f6ac6e2644709c87c677935d409f": { + "source": { + "path": "stack1.template.json", + "packaging": "file" + }, + "destinations": { + "580348834564-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", + "objectKey": "145afe7f6542ca5ca27fff75095a0699c976f6ac6e2644709c87c677935d409f.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.template.json new file mode 100644 index 0000000000000..a0aea4d38b6b2 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.template.json @@ -0,0 +1,233 @@ +{ + "Resources": { + "ReplicationKeyFCE40BF4": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::580348834564:root" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "ReplicationBucket70D68737": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "ReplicationKeyFCE40BF4", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ReplicationBucketPolicyADD8A584": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "ReplicationBucket70D68737" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "ReplicationBucket70D68737", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ReplicationBucket70D68737", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "ReplicationBucketAutoDeleteObjectsCustomResourceF7D32567": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "ReplicationBucket70D68737" + } + }, + "DependsOn": [ + "ReplicationBucketPolicyADD8A584" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-1", + "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs14.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "ReplicationBucket70D68737" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + } + }, + "Outputs": { + "ExportsOutputRefReplicationBucket70D68737E331A47A": { + "Value": { + "Ref": "ReplicationBucket70D68737" + }, + "Export": { + "Name": "stack1:ExportsOutputRefReplicationBucket70D68737E331A47A" + } + }, + "ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6": { + "Value": { + "Fn::GetAtt": [ + "ReplicationKeyFCE40BF4", + "Arn" + ] + }, + "Export": { + "Name": "stack1:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.assets.json new file mode 100644 index 0000000000000..b377806a075a0 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.assets.json @@ -0,0 +1,48 @@ +{ + "version": "21.0.0", + "files": { + "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { + "source": { + "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", + "packaging": "zip" + }, + "destinations": { + "580348834564-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + } + } + }, + "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf": { + "source": { + "path": "asset.1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf", + "packaging": "zip" + }, + "destinations": { + "580348834564-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "objectKey": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + } + } + }, + "e694369aee44edaa5736dd32253bc027102b27eef2b11d044330aea055806fbd": { + "source": { + "path": "stack2.template.json", + "packaging": "file" + }, + "destinations": { + "580348834564-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "objectKey": "e694369aee44edaa5736dd32253bc027102b27eef2b11d044330aea055806fbd.json", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.template.json new file mode 100644 index 0000000000000..071bf013036dc --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.template.json @@ -0,0 +1,913 @@ +{ + "Resources": { + "PipelineArtifactsBucketEncryptionKey01D58D69": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::580348834564:root" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "PipelineArtifactsBucketEncryptionKeyAlias5C510EEE": { + "Type": "AWS::KMS::Alias", + "Properties": { + "AliasName": "alias/codepipeline-stack2-pipeline-d266f8ac", + "TargetKeyId": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKey01D58D69", + "Arn" + ] + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "PipelineArtifactsBucket22248F97": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "KMSMasterKeyID": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKey01D58D69", + "Arn" + ] + }, + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "PipelineArtifactsBucketPolicyD4F9712A": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "PipelineArtifactsBucket22248F97" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleD68726F7": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleDefaultPolicyC7A05455": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKey01D58D69", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelinesourceSourceCodePipelineActionRoleC03B7ECA", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "Pipelinestage2BuildCodePipelineActionRole6D7E5309", + "Arn" + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineRoleDefaultPolicyC7A05455", + "Roles": [ + { + "Ref": "PipelineRoleD68726F7" + } + ] + } + }, + "PipelineC660917D": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleD68726F7", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "AWS", + "Provider": "S3", + "Version": "1" + }, + "Configuration": { + "S3Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "S3ObjectKey": "/somepath" + }, + "Name": "Source", + "OutputArtifacts": [ + { + "Name": "Artifact_source_Source" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "PipelinesourceSourceCodePipelineActionRoleC03B7ECA", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "Build45A36621" + } + }, + "InputArtifacts": [ + { + "Name": "Artifact_source_Source" + } + ], + "Name": "Build", + "RoleArn": { + "Fn::GetAtt": [ + "Pipelinestage2BuildCodePipelineActionRole6D7E5309", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "stage2" + } + ], + "ArtifactStores": [ + { + "ArtifactStore": { + "EncryptionKey": { + "Id": { + "Fn::GetAtt": [ + "ExportsReaderuseast1D746CBDB", + "stack1:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" + ] + }, + "Type": "KMS" + }, + "Location": { + "Fn::GetAtt": [ + "ExportsReaderuseast1D746CBDB", + "stack1:ExportsOutputRefReplicationBucket70D68737E331A47A" + ] + }, + "Type": "S3" + }, + "Region": "us-east-1" + }, + { + "ArtifactStore": { + "EncryptionKey": { + "Id": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKey01D58D69", + "Arn" + ] + }, + "Type": "KMS" + }, + "Location": { + "Ref": "PipelineArtifactsBucket22248F97" + }, + "Type": "S3" + }, + "Region": "us-east-2" + } + ] + }, + "DependsOn": [ + "PipelineRoleDefaultPolicyC7A05455", + "PipelineRoleD68726F7" + ] + }, + "PipelinesourceSourceCodePipelineActionRoleC03B7ECA": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::580348834564:root" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelinesourceSourceCodePipelineActionRoleDefaultPolicy6B296460": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "//somepath" + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKey01D58D69", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelinesourceSourceCodePipelineActionRoleDefaultPolicy6B296460", + "Roles": [ + { + "Ref": "PipelinesourceSourceCodePipelineActionRoleC03B7ECA" + } + ] + } + }, + "Pipelinestage2BuildCodePipelineActionRole6D7E5309": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::580348834564:root" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "Pipelinestage2BuildCodePipelineActionRoleDefaultPolicy4431A4F5": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "Build45A36621", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Pipelinestage2BuildCodePipelineActionRoleDefaultPolicy4431A4F5", + "Roles": [ + { + "Ref": "Pipelinestage2BuildCodePipelineActionRole6D7E5309" + } + ] + } + }, + "SourceBucketDDD2130A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SourceBucketPolicy703DFBF9": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "SourceBucketAutoDeleteObjectsCustomResourceC68FC040": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "SourceBucketDDD2130A" + } + }, + "DependsOn": [ + "SourceBucketPolicy703DFBF9" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs14.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "SourceBucketDDD2130A" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + }, + "BuildRoleB7C66CB2": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "BuildRoleDefaultPolicyEAC4E6D6": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:aws:logs:us-east-2:580348834564:log-group:/aws/codebuild/", + { + "Ref": "Build45A36621" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:aws:logs:us-east-2:580348834564:log-group:/aws/codebuild/", + { + "Ref": "Build45A36621" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:codebuild:us-east-2:580348834564:report-group/", + { + "Ref": "Build45A36621" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucket22248F97", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKey01D58D69", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "BuildRoleDefaultPolicyEAC4E6D6", + "Roles": [ + { + "Ref": "BuildRoleB7C66CB2" + } + ] + } + }, + "Build45A36621": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:1.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "BuildRoleB7C66CB2", + "Arn" + ] + }, + "Source": { + "Type": "CODEPIPELINE" + }, + "Cache": { + "Type": "NO_CACHE" + }, + "EncryptionKey": { + "Fn::GetAtt": [ + "PipelineArtifactsBucketEncryptionKey01D58D69", + "Arn" + ] + } + } + }, + "ExportsReaderuseast1D746CBDB": { + "Type": "Custom::CrossRegionExportReader", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", + "Arn" + ] + }, + "Region": "us-east-1", + "RefreshToken": "1663160447324" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": "*", + "Action": [ + "cloudformation:ListExports" + ] + } + ] + } + } + ] + } + }, + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index c6f6c54419fc2..ad59b4b3dfbe4 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -7,7 +7,7 @@ import { IConstruct, Construct } from 'constructs'; import { CfnElement } from '../cfn-element'; import { CfnOutput } from '../cfn-output'; import { CfnParameter } from '../cfn-parameter'; -import { ExportReader } from '../custom-resource-provider/get-parameter-provider'; +import { ExportReader } from '../custom-resource-provider/export-reader-provider'; import { FeatureFlags } from '../feature-flags'; import { Names } from '../names'; import { Reference } from '../reference'; @@ -62,14 +62,16 @@ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { if (producerAccount !== consumerAccount) { throw new Error( `Stack "${consumer.node.path}" cannot consume a cross reference from stack "${producer.node.path}". ` + - 'Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack'); + 'Cross stack references are only supported for stacks deployed to the same account or between nested stacks and their parent stack'); } + // Stacks are in the same account, but different regions - if (producerRegion !== consumerRegion) { - consumer.addDependency(producer, - `${consumer.node.path} -> ${reference.target.node.path}.${reference.displayName}`); - return createCrossRegionImportValue(reference, consumer); + if (producerRegion !== consumerRegion && !FeatureFlags.of(consumer).isEnabled(cxapi.ENABLE_CROSS_REGION_REFERENCES)) { + throw new Error( + `Stack "${consumer.node.path}" cannot consume a cross reference from stack "${producer.node.path}". ` + + 'Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack. ' + + `Set ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true to enable cross region references`); } // ---------------------------------------------------------------------- @@ -107,6 +109,13 @@ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { // export/import // ---------------------------------------------------------------------- + // Stacks are in the same account, but different regions + if (producerRegion !== consumerRegion && FeatureFlags.of(consumer).isEnabled(cxapi.ENABLE_CROSS_REGION_REFERENCES)) { + consumer.addDependency(producer, + `${consumer.node.path} -> ${reference.target.node.path}.${reference.displayName}`); + return createCrossRegionImportValue(reference, consumer); + } + // export the value through a cloudformation "export name" and use an // Fn::ImportValue in the consumption site. diff --git a/packages/@aws-cdk/core/lib/resource.ts b/packages/@aws-cdk/core/lib/resource.ts index 9d407f4030ef2..319d4ac728def 100644 --- a/packages/@aws-cdk/core/lib/resource.ts +++ b/packages/@aws-cdk/core/lib/resource.ts @@ -1,5 +1,7 @@ +import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { ArnComponents, ArnFormat } from './arn'; import { CfnResource } from './cfn-resource'; +import { FeatureFlags } from './feature-flags'; import { IStringProducer, Lazy } from './lazy'; import { generatePhysicalName, isGeneratedWhenNeededMarker } from './private/physical-name-generator'; import { Reference } from './reference'; @@ -256,7 +258,9 @@ export abstract class Resource extends Construct implements IResource { produce: (context: IResolveContext) => { const consumingStack = Stack.of(context.scope); - if (this.stack.environment !== consumingStack.environment) { + if (this.stack.account !== consumingStack.account || + (this.stack.region !== consumingStack.region && + !FeatureFlags.of(consumingStack).isEnabled(ENABLE_CROSS_REGION_REFERENCES))) { this._enableCrossEnvironment(); return this.physicalName; } else { @@ -287,7 +291,9 @@ export abstract class Resource extends Construct implements IResource { return mimicReference(arnAttr, { produce: (context: IResolveContext) => { const consumingStack = Stack.of(context.scope); - if (this.stack.environment !== consumingStack.environment) { + if (this.stack.account !== consumingStack.account || + (this.stack.region !== consumingStack.region && + !FeatureFlags.of(consumingStack).isEnabled(ENABLE_CROSS_REGION_REFERENCES))) { this._enableCrossEnvironment(); return this.stack.formatArn(arnComponents); } else { diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index a84da4714ba41..795e78eea4a04 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -1,3 +1,4 @@ +import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; import { App, CfnOutput, CfnResource, PhysicalName, Resource, Stack } from '../lib'; import { toCloudFormation } from './util'; @@ -186,6 +187,102 @@ describe('cross environment', () => { /Cannot use resource 'Stack1\/MyResource' in a cross-environment fashion/); }); + test(`can reference a deploy-time physical name across regions, when ${ENABLE_CROSS_REGION_REFERENCES}=true`, () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { + env: { + account: '123456789012', + region: 'bermuda-triangle-1337', + }, + }); + const stack2 = new Stack(app, 'Stack2', { + env: { + account: '123456789012', + region: 'bermuda-triangle-42', + }, + }); + stack2.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); + + // WHEN + const myResource = new MyResource(stack1, 'MyResource'); + new CfnOutput(stack2, 'Output', { + value: myResource.name, + }); + + // THEN + const assembly = app.synth(); + const template1 = assembly.getStackByName(stack1.stackName).template; + const template2 = assembly.getStackByName(stack2.stackName).template; + + expect(template1?.Outputs).toEqual({ + 'ExportsOutputRefMyResource6073B41F0296C218': { + 'Export': { + 'Name': 'Stack1:ExportsOutputRefMyResource6073B41F0296C218', + }, + 'Value': { + 'Ref': 'MyResource6073B41F', + }, + }, + }); + expect(template2?.Resources).toMatchObject({ + 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68': { + 'DependsOn': [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + ], + 'Type': 'AWS::Lambda::Function', + }, + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD': { + 'Type': 'AWS::IAM::Role', + }, + 'ExportsReaderbermudatriangle1337E63A6E15': { + 'DeletionPolicy': 'Delete', + 'Properties': { + 'Region': 'bermuda-triangle-1337', + }, + 'Type': 'Custom::CrossRegionExportReader', + 'UpdateReplacePolicy': 'Delete', + }, + }); + expect(template2?.Outputs).toEqual({ + 'Output': { + 'Value': { + 'Fn::GetAtt': [ + 'ExportsReaderbermudatriangle1337E63A6E15', + 'Stack1:ExportsOutputRefMyResource6073B41F0296C218', + ], + }, + }, + }); + }); + + test(`cannot reference a deploy-time physical name across regions, when ${ENABLE_CROSS_REGION_REFERENCES}=false`, () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { + env: { + account: '123456789012', + region: 'bermuda-triangle-1337', + }, + }); + const stack2 = new Stack(app, 'Stack2', { + env: { + account: '123456789012', + region: 'bermuda-triangle-42', + }, + }); + + // WHEN + const myResource = new MyResource(stack1, 'MyResource'); + new CfnOutput(stack2, 'Output', { + value: myResource.name, + }); + + // THEN + expect(() => toCloudFormation(stack2)).toThrow( + /Cannot use resource 'Stack1\/MyResource' in a cross-environment fashion/); + }); + test('cross environment when stack is a substack', () => { const app = new App(); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts index aa37b88b61bc7..0e8b6b9519358 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts @@ -18,6 +18,7 @@ jest.mock('aws-sdk', () => { describe('get-cfn-exports entrypoint', () => { beforeEach(() => { mockListExports.mockReset(); + jest.spyOn(console, 'info').mockImplementation(() => {}); }); afterEach(() => { jest.restoreAllMocks(); diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 697253fe5f7fd..5824ade52d8f1 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -1,5 +1,9 @@ +import * as path from 'path'; +import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; +import { Construct } from 'constructs'; +import { readFileSync } from 'fs-extra'; import { - Stack, NestedStack, CfnStack, + Stack, NestedStack, CfnStack, Resource, CfnResource, App, CfnOutput, } from '../lib'; import { toCloudFormation } from './util'; @@ -31,4 +35,134 @@ describe('nested-stack', () => { expect(nestedStack.templateOptions.description).toEqual(description); }); -}); \ No newline at end of file + + test(`can create cross region references when ${ENABLE_CROSS_REGION_REFERENCES}=true`, () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { + env: { + account: '123456789012', + region: 'bermuda-triangle-1337', + }, + }); + const stack2 = new Stack(app, 'Stack2', { + env: { + account: '123456789012', + region: 'bermuda-triangle-42', + }, + }); + stack2.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); + const nestedStack = new NestedStack(stack1, 'MyNestedStack'); + const nestedStack2 = new NestedStack(stack2, 'MyNestedStack'); + + // WHEN + const myResource = new MyResource(nestedStack, 'MyResource'); + + new CfnOutput(nestedStack2, 'Output', { + value: myResource.name, + }); + + // THEN + const assembly = app.synth(); + const template2 = JSON.parse(readFileSync(path.join(assembly.directory, `${nestedStack2.artifactId}.nested.template.json`), 'utf8')); + expect(template2).toMatchObject({ + Resources: { + CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + DependsOn: [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + ], + Type: 'AWS::Lambda::Function', + }, + CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD: { + Type: 'AWS::IAM::Role', + }, + ExportsReaderbermudatriangle1337E63A6E15: { + DeletionPolicy: 'Delete', + Properties: { + Region: 'bermuda-triangle-1337', + }, + Type: 'Custom::CrossRegionExportReader', + UpdateReplacePolicy: 'Delete', + }, + }, + Outputs: { + Output: { + Value: { + 'Fn::GetAtt': [ + 'ExportsReaderbermudatriangle1337E63A6E15', + 'Stack1:ExportsOutputFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref16CD9A2F', + ], + }, + }, + }, + }); + const template1 = assembly.getStackByName(stack1.stackName).template; + + expect(template1?.Outputs).toEqual({ + ExportsOutputFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref16CD9A2F: { + Export: { + Name: 'Stack1:ExportsOutputFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref16CD9A2F', + }, + Value: { + 'Fn::GetAtt': [ + 'MyNestedStackNestedStackMyNestedStackNestedStackResource9C617903', + 'Outputs.Stack1MyNestedStackMyResourceEDA18296Ref', + ], + }, + }, + }); + }); + + test(`cannot create cross region references when ${ENABLE_CROSS_REGION_REFERENCES}=false`, () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { + env: { + account: '123456789012', + region: 'bermuda-triangle-1337', + }, + }); + const stack2 = new Stack(app, 'Stack2', { + env: { + account: '123456789012', + region: 'bermuda-triangle-42', + }, + }); + const nestedStack = new NestedStack(stack1, 'MyNestedStack'); + + // WHEN + const myResource = new MyResource(nestedStack, 'MyResource'); + new CfnOutput(stack2, 'Output', { + value: myResource.name, + }); + + // THEN + expect(() => toCloudFormation(stack2)).toThrow( + /Cannot use resource 'Stack1\/MyNestedStack\/MyResource' in a cross-environment fashion/); + }); +}); + +class MyResource extends Resource { + public readonly arn: string; + public readonly name: string; + + constructor(scope: Construct, id: string, physicalName?: string) { + super(scope, id, { physicalName }); + + const res = new CfnResource(this, 'Resource', { + type: 'My::Resource', + properties: { + resourceName: this.physicalName, + }, + }); + + this.name = this.getResourceNameAttribute(res.ref.toString()); + this.arn = this.getResourceArnAttribute(res.getAtt('Arn').toString(), { + region: '', + account: '', + resource: 'my-resource', + resourceName: this.physicalName, + service: 'myservice', + }); + } +} diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 7de34ce909410..ca757f9c1ec2a 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -462,7 +462,7 @@ describe('stack', () => { }); }); - test('cross-region stack references', () => { + test(`cross-region stack references, ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true`, () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); @@ -470,6 +470,7 @@ describe('stack', () => { type: 'AWS::S3::Bucket', }); const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); + stack2.node.setContext(cxapi.ENABLE_CROSS_REGION_REFERENCES, true); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { @@ -528,7 +529,30 @@ describe('stack', () => { }); }); - test('cross region stack references with multiple stacks', () => { + test('cross-region stack references throws error', () => { + // GIVEN + const app = new App(); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const exportResource = new CfnResource(stack1, 'SomeResourceExport', { + type: 'AWS::S3::Bucket', + }); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); + + // WHEN - used in another stack + new CfnResource(stack2, 'SomeResource', { + type: 'AWS::S3::Bucket', + properties: { + Name: exportResource.getAtt('name'), + }, + }); + + // THEN + expect(() => { + app.synth(); + }).toThrow(/Set @aws-cdk\/core:enableCrossRegionReferencesUsingCustomResources=true to enable cross region references/); + }); + + test(`cross region stack references with multiple stacks, ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true`, () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); @@ -540,6 +564,7 @@ describe('stack', () => { type: 'AWS::S3::Bucket', }); const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); + stack2.node.setContext(cxapi.ENABLE_CROSS_REGION_REFERENCES, true); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { @@ -646,7 +671,7 @@ describe('stack', () => { }); }); - test('cross region stack references with multiple stacks and multiple regions', () => { + test(`cross region stack references with multiple stacks and multiple regions, ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true`, () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); @@ -658,6 +683,7 @@ describe('stack', () => { type: 'AWS::S3::Bucket', }); const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); + stack2.node.setContext(cxapi.ENABLE_CROSS_REGION_REFERENCES, true); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { diff --git a/packages/@aws-cdk/cx-api/README.md b/packages/@aws-cdk/cx-api/README.md index ba2b89724a96d..bf28b153cf3a8 100644 --- a/packages/@aws-cdk/cx-api/README.md +++ b/packages/@aws-cdk/cx-api/README.md @@ -104,3 +104,22 @@ becomes: Principal: AWS: "arn:aws:iam::123456789876:root" ``` + +* `@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources` + + +Enable this feature flag to allow native cross region stack references. This will use a CloudFormation +Custom Resource to perform the cross region lookup. + +This is disabled by default. + +_cdk.json_ + +```json +{ + "context": { + "@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources": true + } +} +``` + diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index e991c1e285b45..3762900f72cfe 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -352,6 +352,14 @@ export const APIGATEWAY_DISABLE_CLOUDWATCH_ROLE = '@aws-cdk/aws-apigateway:disab */ export const ENABLE_PARTITION_LITERALS = '@aws-cdk/core:enablePartitionLiterals'; +/** + * Enable this feature flag to allow native cross region stack references. This will use a CloudFormation + * Custom Resource to perform the cross region lookup. + * + * @default false + */ +export const ENABLE_CROSS_REGION_REFERENCES = '@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources'; + /** * Flag values that should apply for new projects * From cfcdd08ba59fa678e47e3d5eb3f14f3d529ae030 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Wed, 14 Sep 2022 16:51:42 +0000 Subject: [PATCH 04/30] fixing integration tests --- .../cross-region-consumer.assets.json | 8 +- .../cross-region-consumer.template.json | 4 +- ...erIntegNested815BEF8A.nested.template.json | 2 +- .../manifest.json | 2 +- .../test/integ.pipeline-with-replication.ts | 4 +- ...integ-pipeline-consumer-stack.assets.json} | 24 ++-- ...teg-pipeline-consumer-stack.template.json} | 24 ++-- .../integ-pipeline-producer-stack.assets.json | 34 +++++ ...teg-pipeline-producer-stack.template.json} | 8 +- .../integ.json | 2 +- .../manifest.json | 118 +++++++++--------- .../stack1.assets.json | 34 ----- .../export-reader-provider.ts | 37 +++++- packages/@aws-cdk/core/lib/private/refs.ts | 1 + 14 files changed, 166 insertions(+), 136 deletions(-) rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{stack2.assets.json => integ-pipeline-consumer-stack.assets.json} (51%) rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{stack2.template.json => integ-pipeline-consumer-stack.template.json} (95%) create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{stack1.template.json => integ-pipeline-producer-stack.template.json} (94%) delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json index fe3f26024eef9..c3615af74dde3 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -15,7 +15,7 @@ } } }, - "4835de933cab0e010b7250e075bf55e0432d724c682a5a4658bac31d2fd35178": { + "433a88eab9a43c81da7b3f1feb8fbd766c8c8631e8d1f0b6cd063a2f3ee51ff9": { "source": { "path": "crossregionconsumerIntegNested815BEF8A.nested.template.json", "packaging": "file" @@ -23,13 +23,13 @@ "destinations": { "580348834564-us-east-2": { "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", - "objectKey": "4835de933cab0e010b7250e075bf55e0432d724c682a5a4658bac31d2fd35178.json", + "objectKey": "433a88eab9a43c81da7b3f1feb8fbd766c8c8631e8d1f0b6cd063a2f3ee51ff9.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" } } }, - "8fb619a5eea2e71b6943472a86e001a77df08f312a80b3038c556fa3510491a1": { + "d85a69e61fe0660625995983d694ec6f35e9801a028164cc2d031726b3afb35d": { "source": { "path": "cross-region-consumer.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "580348834564-us-east-2": { "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", - "objectKey": "8fb619a5eea2e71b6943472a86e001a77df08f312a80b3038c556fa3510491a1.json", + "objectKey": "d85a69e61fe0660625995983d694ec6f35e9801a028164cc2d031726b3afb35d.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json index 9eda26a0d8d26..414c50a0db4f5 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -11,7 +11,7 @@ { "Ref": "AWS::URLSuffix" }, - "/cdk-hnb659fds-assets-580348834564-us-east-2/4835de933cab0e010b7250e075bf55e0432d724c682a5a4658bac31d2fd35178.json" + "/cdk-hnb659fds-assets-580348834564-us-east-2/433a88eab9a43c81da7b3f1feb8fbd766c8c8631e8d1f0b6cd063a2f3ee51ff9.json" ] ] } @@ -55,7 +55,7 @@ ] }, "Region": "us-east-1", - "RefreshToken": "1663094629919" + "RefreshToken": "d24d89c84d6bb022f6cc6b49a2218fdd" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json index 6e36d442eebd3..7cbdd0dcaa795 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json @@ -36,7 +36,7 @@ ] }, "Region": "us-east-1", - "RefreshToken": "1663094629916" + "RefreshToken": "d24d89c84d6bb022f6cc6b49a2218fdd" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index dd4be22840eaf..83af7c976ea18 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -100,7 +100,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-2/8fb619a5eea2e71b6943472a86e001a77df08f312a80b3038c556fa3510491a1.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-2/d85a69e61fe0660625995983d694ec6f35e9801a028164cc2d031726b3afb35d.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts index 38f1c5e575204..a49d0e3b15546 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts @@ -13,13 +13,13 @@ const app = new App({ treeMetadata: false, }); app.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); -const stack1 = new Stack(app, 'stack1', { +const stack1 = new Stack(app, 'integ-pipeline-producer-stack', { env: { region: 'us-east-1', account, }, }); -const stack2 = new Stack(app, 'stack2', { +const stack2 = new Stack(app, 'integ-pipeline-consumer-stack', { env: { region: 'us-east-2', account, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json similarity index 51% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.assets.json rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json index b377806a075a0..b1c6210254e04 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json @@ -7,11 +7,11 @@ "packaging": "zip" }, "destinations": { - "580348834564-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } }, @@ -21,25 +21,25 @@ "packaging": "zip" }, "destinations": { - "580348834564-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", "objectKey": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } }, - "e694369aee44edaa5736dd32253bc027102b27eef2b11d044330aea055806fbd": { + "a0e7b5e05681c4b759e7ec2d86e5ffa92067757e6a136c7516438235b9ea2a4c": { "source": { - "path": "stack2.template.json", + "path": "integ-pipeline-consumer-stack.template.json", "packaging": "file" }, "destinations": { - "580348834564-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", - "objectKey": "e694369aee44edaa5736dd32253bc027102b27eef2b11d044330aea055806fbd.json", + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", + "objectKey": "a0e7b5e05681c4b759e7ec2d86e5ffa92067757e6a136c7516438235b9ea2a4c.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json similarity index 95% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.template.json rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json index 071bf013036dc..21169dcc01b96 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack2.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json @@ -9,7 +9,7 @@ "Action": "kms:*", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::580348834564:root" + "AWS": "arn:aws:iam::12345678:root" }, "Resource": "*" } @@ -23,7 +23,7 @@ "PipelineArtifactsBucketEncryptionKeyAlias5C510EEE": { "Type": "AWS::KMS::Alias", "Properties": { - "AliasName": "alias/codepipeline-stack2-pipeline-d266f8ac", + "AliasName": "alias/codepipeline-integ-pipeline-consumer-stack-pipeline-9f1db34e", "TargetKeyId": { "Fn::GetAtt": [ "PipelineArtifactsBucketEncryptionKey01D58D69", @@ -294,7 +294,7 @@ "Id": { "Fn::GetAtt": [ "ExportsReaderuseast1D746CBDB", - "stack1:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" + "integ-pipeline-producer-stack:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" ] }, "Type": "KMS" @@ -302,7 +302,7 @@ "Location": { "Fn::GetAtt": [ "ExportsReaderuseast1D746CBDB", - "stack1:ExportsOutputRefReplicationBucket70D68737E331A47A" + "integ-pipeline-producer-stack:ExportsOutputRefReplicationBucket70D68737E331A47A" ] }, "Type": "S3" @@ -343,7 +343,7 @@ "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::580348834564:root" + "AWS": "arn:aws:iam::12345678:root" } } ], @@ -455,7 +455,7 @@ "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::580348834564:root" + "AWS": "arn:aws:iam::12345678:root" } } ], @@ -602,7 +602,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" }, "Timeout": 900, @@ -666,7 +666,7 @@ "Fn::Join": [ "", [ - "arn:aws:logs:us-east-2:580348834564:log-group:/aws/codebuild/", + "arn:aws:logs:us-east-2:12345678:log-group:/aws/codebuild/", { "Ref": "Build45A36621" }, @@ -678,7 +678,7 @@ "Fn::Join": [ "", [ - "arn:aws:logs:us-east-2:580348834564:log-group:/aws/codebuild/", + "arn:aws:logs:us-east-2:12345678:log-group:/aws/codebuild/", { "Ref": "Build45A36621" } @@ -700,7 +700,7 @@ "Fn::Join": [ "", [ - "arn:aws:codebuild:us-east-2:580348834564:report-group/", + "arn:aws:codebuild:us-east-2:12345678:report-group/", { "Ref": "Build45A36621" }, @@ -809,7 +809,7 @@ ] }, "Region": "us-east-1", - "RefreshToken": "1663160447324" + "RefreshToken": "98880eb177b602d9fd70c7d737108d84" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -857,7 +857,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" }, "Timeout": 900, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json new file mode 100644 index 0000000000000..5339f36592f7c --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json @@ -0,0 +1,34 @@ +{ + "version": "21.0.0", + "files": { + "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { + "source": { + "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", + "packaging": "zip" + }, + "destinations": { + "12345678-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + } + } + }, + "cd81916f97f443b595350f121a34cc07641fedfc3e39d80b64772fbf5e7a48be": { + "source": { + "path": "integ-pipeline-producer-stack.template.json", + "packaging": "file" + }, + "destinations": { + "12345678-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "objectKey": "cd81916f97f443b595350f121a34cc07641fedfc3e39d80b64772fbf5e7a48be.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json similarity index 94% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.template.json rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json index a0aea4d38b6b2..949be2203e697 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json @@ -9,7 +9,7 @@ "Action": "kms:*", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::580348834564:root" + "AWS": "arn:aws:iam::12345678:root" }, "Resource": "*" } @@ -144,7 +144,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-1", + "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-1", "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" }, "Timeout": 900, @@ -181,7 +181,7 @@ "Ref": "ReplicationBucket70D68737" }, "Export": { - "Name": "stack1:ExportsOutputRefReplicationBucket70D68737E331A47A" + "Name": "integ-pipeline-producer-stack:ExportsOutputRefReplicationBucket70D68737E331A47A" } }, "ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6": { @@ -192,7 +192,7 @@ ] }, "Export": { - "Name": "stack1:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" + "Name": "integ-pipeline-producer-stack:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" } } }, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json index e5fdb50dd8612..84aa7d89d58a9 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ.json @@ -3,7 +3,7 @@ "testCases": { "codepipeline-integ-test/DefaultTest": { "stacks": [ - "stack2" + "integ-pipeline-consumer-stack" ], "stackUpdateWorkflow": false, "assertionStack": "codepipeline-integ-test/DefaultTest/DeployAssert", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json index 878c62becb229..7f4e150473faf 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json @@ -1,280 +1,280 @@ { "version": "21.0.0", "artifacts": { - "stack1.assets": { + "integ-pipeline-producer-stack.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "stack1.assets.json", + "file": "integ-pipeline-producer-stack.assets.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, - "stack1": { + "integ-pipeline-producer-stack": { "type": "aws:cloudformation:stack", - "environment": "aws://580348834564/us-east-1", + "environment": "aws://12345678/us-east-1", "properties": { - "templateFile": "stack1.template.json", + "templateFile": "integ-pipeline-producer-stack.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-1", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-1/145afe7f6542ca5ca27fff75095a0699c976f6ac6e2644709c87c677935d409f.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/cd81916f97f443b595350f121a34cc07641fedfc3e39d80b64772fbf5e7a48be.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ - "stack1.assets" + "integ-pipeline-producer-stack.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-1", + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-1", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, "dependencies": [ - "stack1.assets" + "integ-pipeline-producer-stack.assets" ], "metadata": { - "/stack1/ReplicationKey/Resource": [ + "/integ-pipeline-producer-stack/ReplicationKey/Resource": [ { "type": "aws:cdk:logicalId", "data": "ReplicationKeyFCE40BF4" } ], - "/stack1/ReplicationBucket/Resource": [ + "/integ-pipeline-producer-stack/ReplicationBucket/Resource": [ { "type": "aws:cdk:logicalId", "data": "ReplicationBucket70D68737" } ], - "/stack1/ReplicationBucket/Policy/Resource": [ + "/integ-pipeline-producer-stack/ReplicationBucket/Policy/Resource": [ { "type": "aws:cdk:logicalId", "data": "ReplicationBucketPolicyADD8A584" } ], - "/stack1/ReplicationBucket/AutoDeleteObjectsCustomResource/Default": [ + "/integ-pipeline-producer-stack/ReplicationBucket/AutoDeleteObjectsCustomResource/Default": [ { "type": "aws:cdk:logicalId", "data": "ReplicationBucketAutoDeleteObjectsCustomResourceF7D32567" } ], - "/stack1/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + "/integ-pipeline-producer-stack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ { "type": "aws:cdk:logicalId", "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" } ], - "/stack1/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + "/integ-pipeline-producer-stack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ { "type": "aws:cdk:logicalId", "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" } ], - "/stack1/Exports/Output{\"Ref\":\"ReplicationBucket70D68737\"}": [ + "/integ-pipeline-producer-stack/Exports/Output{\"Ref\":\"ReplicationBucket70D68737\"}": [ { "type": "aws:cdk:logicalId", "data": "ExportsOutputRefReplicationBucket70D68737E331A47A" } ], - "/stack1/Exports/Output{\"Fn::GetAtt\":[\"ReplicationKeyFCE40BF4\",\"Arn\"]}": [ + "/integ-pipeline-producer-stack/Exports/Output{\"Fn::GetAtt\":[\"ReplicationKeyFCE40BF4\",\"Arn\"]}": [ { "type": "aws:cdk:logicalId", "data": "ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" } ], - "/stack1/BootstrapVersion": [ + "/integ-pipeline-producer-stack/BootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "BootstrapVersion" } ], - "/stack1/CheckBootstrapVersion": [ + "/integ-pipeline-producer-stack/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } ] }, - "displayName": "stack1" + "displayName": "integ-pipeline-producer-stack" }, - "stack2.assets": { + "integ-pipeline-consumer-stack.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "stack2.assets.json", + "file": "integ-pipeline-consumer-stack.assets.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, - "stack2": { + "integ-pipeline-consumer-stack": { "type": "aws:cloudformation:stack", - "environment": "aws://580348834564/us-east-2", + "environment": "aws://12345678/us-east-2", "properties": { - "templateFile": "stack2.template.json", + "templateFile": "integ-pipeline-consumer-stack.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-2", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-2/e694369aee44edaa5736dd32253bc027102b27eef2b11d044330aea055806fbd.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/a0e7b5e05681c4b759e7ec2d86e5ffa92067757e6a136c7516438235b9ea2a4c.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ - "stack2.assets" + "integ-pipeline-consumer-stack.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-2", + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-2", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, "dependencies": [ - "stack1", - "stack2.assets" + "integ-pipeline-producer-stack", + "integ-pipeline-consumer-stack.assets" ], "metadata": { - "/stack2/Pipeline/ArtifactsBucketEncryptionKey/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/ArtifactsBucketEncryptionKey/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelineArtifactsBucketEncryptionKey01D58D69" } ], - "/stack2/Pipeline/ArtifactsBucketEncryptionKeyAlias/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/ArtifactsBucketEncryptionKeyAlias/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelineArtifactsBucketEncryptionKeyAlias5C510EEE" } ], - "/stack2/Pipeline/ArtifactsBucket/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/ArtifactsBucket/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelineArtifactsBucket22248F97" } ], - "/stack2/Pipeline/ArtifactsBucket/Policy/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/ArtifactsBucket/Policy/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelineArtifactsBucketPolicyD4F9712A" } ], - "/stack2/Pipeline/Role/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/Role/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelineRoleD68726F7" } ], - "/stack2/Pipeline/Role/DefaultPolicy/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/Role/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelineRoleDefaultPolicyC7A05455" } ], - "/stack2/Pipeline/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelineC660917D" } ], - "/stack2/Pipeline/source/Source/CodePipelineActionRole/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/source/Source/CodePipelineActionRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelinesourceSourceCodePipelineActionRoleC03B7ECA" } ], - "/stack2/Pipeline/source/Source/CodePipelineActionRole/DefaultPolicy/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/source/Source/CodePipelineActionRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", "data": "PipelinesourceSourceCodePipelineActionRoleDefaultPolicy6B296460" } ], - "/stack2/Pipeline/stage2/Build/CodePipelineActionRole/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/stage2/Build/CodePipelineActionRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "Pipelinestage2BuildCodePipelineActionRole6D7E5309" } ], - "/stack2/Pipeline/stage2/Build/CodePipelineActionRole/DefaultPolicy/Resource": [ + "/integ-pipeline-consumer-stack/Pipeline/stage2/Build/CodePipelineActionRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", "data": "Pipelinestage2BuildCodePipelineActionRoleDefaultPolicy4431A4F5" } ], - "/stack2/SourceBucket/Resource": [ + "/integ-pipeline-consumer-stack/SourceBucket/Resource": [ { "type": "aws:cdk:logicalId", "data": "SourceBucketDDD2130A" } ], - "/stack2/SourceBucket/Policy/Resource": [ + "/integ-pipeline-consumer-stack/SourceBucket/Policy/Resource": [ { "type": "aws:cdk:logicalId", "data": "SourceBucketPolicy703DFBF9" } ], - "/stack2/SourceBucket/AutoDeleteObjectsCustomResource/Default": [ + "/integ-pipeline-consumer-stack/SourceBucket/AutoDeleteObjectsCustomResource/Default": [ { "type": "aws:cdk:logicalId", "data": "SourceBucketAutoDeleteObjectsCustomResourceC68FC040" } ], - "/stack2/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + "/integ-pipeline-consumer-stack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ { "type": "aws:cdk:logicalId", "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" } ], - "/stack2/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + "/integ-pipeline-consumer-stack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ { "type": "aws:cdk:logicalId", "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" } ], - "/stack2/Build/Role/Resource": [ + "/integ-pipeline-consumer-stack/Build/Role/Resource": [ { "type": "aws:cdk:logicalId", "data": "BuildRoleB7C66CB2" } ], - "/stack2/Build/Role/DefaultPolicy/Resource": [ + "/integ-pipeline-consumer-stack/Build/Role/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", "data": "BuildRoleDefaultPolicyEAC4E6D6" } ], - "/stack2/Build/Resource": [ + "/integ-pipeline-consumer-stack/Build/Resource": [ { "type": "aws:cdk:logicalId", "data": "Build45A36621" } ], - "/stack2/ExportsReaderuseast1D746CBDB/Default/Default": [ + "/integ-pipeline-consumer-stack/ExportsReaderuseast1D746CBDB/Default/Default": [ { "type": "aws:cdk:logicalId", "data": "ExportsReaderuseast1D746CBDB" } ], - "/stack2/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ + "/integ-pipeline-consumer-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ { "type": "aws:cdk:logicalId", "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" } ], - "/stack2/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ + "/integ-pipeline-consumer-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ { "type": "aws:cdk:logicalId", "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" } ], - "/stack2/BootstrapVersion": [ + "/integ-pipeline-consumer-stack/BootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "BootstrapVersion" } ], - "/stack2/CheckBootstrapVersion": [ + "/integ-pipeline-consumer-stack/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } ] }, - "displayName": "stack2" + "displayName": "integ-pipeline-consumer-stack" }, "codepipelineintegtestDefaultTestDeployAssert88EAAC45.assets": { "type": "cdk:asset-manifest", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json deleted file mode 100644 index 9dcaab6104f96..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/stack1.assets.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": "21.0.0", - "files": { - "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { - "source": { - "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", - "packaging": "zip" - }, - "destinations": { - "580348834564-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", - "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" - } - } - }, - "145afe7f6542ca5ca27fff75095a0699c976f6ac6e2644709c87c677935d409f": { - "source": { - "path": "stack1.template.json", - "packaging": "file" - }, - "destinations": { - "580348834564-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", - "objectKey": "145afe7f6542ca5ca27fff75095a0699c976f6ac6e2644709c87c677935d409f.json", - "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" - } - } - } - }, - "dockerImages": {} -} \ No newline at end of file diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts index ce2008d20d1fb..58ee85efe02b6 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts @@ -1,7 +1,11 @@ +import * as crypto from 'crypto'; import * as path from 'path'; import { Construct } from 'constructs'; +import { CfnResource } from '../cfn-resource'; import { CustomResource } from '../custom-resource'; +import { Lazy } from '../lazy'; import { Intrinsic } from '../private/intrinsic'; +import { Reference } from '../reference'; import { Stack } from '../stack'; import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; @@ -34,9 +38,12 @@ export interface ExportReaderProps { * SomeParam: exportReader.importValue('someName'), * }, * }); + * + * @internal - this is intentionally not exported from core */ export class ExportReader extends Construct { private readonly resource: CustomResource; + private readonly _references: Reference[] = []; constructor(scope: Construct, id: string, props: ExportReaderProps) { super(scope, id); const stack = Stack.of(this); @@ -58,11 +65,24 @@ export class ExportReader extends Construct { serviceToken, properties: { Region: region, - // This is used to determine when the function has changed. + // This is used to determine when custom resource should be executed again. // - // We want to lookup the exports every time - // changed for it to take effect - a good candidate for RefreshToken. - RefreshToken: Date.now().toString(), + // We want to lookup the resources whenever any of the references + // change. The only reliable way to tell whether we need to perform another lookup + // is to check if _any_ property of the referenced resource changes + RefreshToken: Lazy.string({ + produce: () => { + const hash = crypto.createHash('md5'); + this._references.forEach(reference => { + const referenceStack = Stack.of(reference.target); + if (CfnResource.isCfnResource(reference.target)) { + const cfn = JSON.stringify(referenceStack.resolve(reference.target._toCloudFormation())); + hash.update(cfn); + } + }); + return hash.digest('hex'); + }, + }), }, }); @@ -87,4 +107,13 @@ export class ExportReader extends Construct { public importValue(exportName: string): Intrinsic { return this.resource.getAtt(exportName); } + + /** + * Register a reference with the reader. + * + * @internal + */ + public _registerExport(reference: Reference): void { + this._references.push(reference); + } } diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index ad59b4b3dfbe4..d3d754b919e68 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -212,6 +212,7 @@ function createCrossRegionImportValue(reference: Reference, importStack: Stack): : new ExportReader(importStack, constructName, { region: exportingStack.region, }); + exportReader._registerExport(reference); return exportReader.importValue(exportName); } From 7a3186845b140e585118bb2326d4a8727e3a9265 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Wed, 14 Sep 2022 17:19:41 +0000 Subject: [PATCH 05/30] hopefully fixing integ tests --- .../cross-region-consumer.assets.json | 26 +++++++++---------- .../cross-region-consumer.template.json | 6 ++--- .../cross-region-producer.assets.json | 16 ++++++------ .../cross-region-producer.template.json | 2 +- ...erIntegNested815BEF8A.nested.template.json | 4 +-- .../manifest.json | 20 +++++++------- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json index c3615af74dde3..b2a79150b208a 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -7,39 +7,39 @@ "packaging": "zip" }, "destinations": { - "580348834564-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", "objectKey": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } }, - "433a88eab9a43c81da7b3f1feb8fbd766c8c8631e8d1f0b6cd063a2f3ee51ff9": { + "f18a7bd6678575898edf76b4c96e9f9dee4f6ef1269ff16aa9d3c93a9862fd51": { "source": { "path": "crossregionconsumerIntegNested815BEF8A.nested.template.json", "packaging": "file" }, "destinations": { - "580348834564-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", - "objectKey": "433a88eab9a43c81da7b3f1feb8fbd766c8c8631e8d1f0b6cd063a2f3ee51ff9.json", + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", + "objectKey": "f18a7bd6678575898edf76b4c96e9f9dee4f6ef1269ff16aa9d3c93a9862fd51.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } }, - "d85a69e61fe0660625995983d694ec6f35e9801a028164cc2d031726b3afb35d": { + "e26c2dfd5f9b7ba5526b3f009caa48d05359ab46c53a1b9652d10a9a8b01161e": { "source": { "path": "cross-region-consumer.template.json", "packaging": "file" }, "destinations": { - "580348834564-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-2", - "objectKey": "d85a69e61fe0660625995983d694ec6f35e9801a028164cc2d031726b3afb35d.json", + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", + "objectKey": "e26c2dfd5f9b7ba5526b3f009caa48d05359ab46c53a1b9652d10a9a8b01161e.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json index 414c50a0db4f5..a570b39e5d55a 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -11,7 +11,7 @@ { "Ref": "AWS::URLSuffix" }, - "/cdk-hnb659fds-assets-580348834564-us-east-2/433a88eab9a43c81da7b3f1feb8fbd766c8c8631e8d1f0b6cd063a2f3ee51ff9.json" + "/cdk-hnb659fds-assets-12345678-us-east-2/f18a7bd6678575898edf76b4c96e9f9dee4f6ef1269ff16aa9d3c93a9862fd51.json" ] ] } @@ -55,7 +55,7 @@ ] }, "Region": "us-east-1", - "RefreshToken": "d24d89c84d6bb022f6cc6b49a2218fdd" + "RefreshToken": "232744ad70da548e96b56a2d625574f9" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -103,7 +103,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" }, "Timeout": 900, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json index dd54e15cb259f..e7d81fc5854d4 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -7,25 +7,25 @@ "packaging": "file" }, "destinations": { - "580348834564-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", + "12345678-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", "objectKey": "db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" } } }, - "8c7abc3c42e024d8c1d7fcf2a5f9bcfb13b3eaf75334cffef6d4564b22a2a209": { + "1b51dc5002497b083a035e23c4860bcb5629e1bc736934175db5d6e44994d6a0": { "source": { "path": "cross-region-producer.template.json", "packaging": "file" }, "destinations": { - "580348834564-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-580348834564-us-east-1", - "objectKey": "8c7abc3c42e024d8c1d7fcf2a5f9bcfb13b3eaf75334cffef6d4564b22a2a209.json", + "12345678-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "objectKey": "1b51dc5002497b083a035e23c4860bcb5629e1bc736934175db5d6e44994d6a0.json", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-file-publishing-role-580348834564-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" } } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json index 56087aa324c26..05142330017b8 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -11,7 +11,7 @@ { "Ref": "AWS::URLSuffix" }, - "/cdk-hnb659fds-assets-580348834564-us-east-1/db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json" + "/cdk-hnb659fds-assets-12345678-us-east-1/db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json" ] ] } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json index 7cbdd0dcaa795..ec22df2c5dac1 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json @@ -36,7 +36,7 @@ ] }, "Region": "us-east-1", - "RefreshToken": "d24d89c84d6bb022f6cc6b49a2218fdd" + "RefreshToken": "232744ad70da548e96b56a2d625574f9" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -84,7 +84,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-580348834564-us-east-2", + "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" }, "Timeout": 900, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index 83af7c976ea18..a9979b099d7b5 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -11,20 +11,20 @@ }, "cross-region-producer": { "type": "aws:cloudformation:stack", - "environment": "aws://580348834564/us-east-1", + "environment": "aws://12345678/us-east-1", "properties": { "templateFile": "cross-region-producer.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-1", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-1/8c7abc3c42e024d8c1d7fcf2a5f9bcfb13b3eaf75334cffef6d4564b22a2a209.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/1b51dc5002497b083a035e23c4860bcb5629e1bc736934175db5d6e44994d6a0.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "cross-region-producer.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-1", + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-1", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } @@ -94,20 +94,20 @@ }, "cross-region-consumer": { "type": "aws:cloudformation:stack", - "environment": "aws://580348834564/us-east-2", + "environment": "aws://12345678/us-east-2", "properties": { "templateFile": "cross-region-consumer.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-deploy-role-580348834564-us-east-2", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-cfn-exec-role-580348834564-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-580348834564-us-east-2/d85a69e61fe0660625995983d694ec6f35e9801a028164cc2d031726b3afb35d.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/e26c2dfd5f9b7ba5526b3f009caa48d05359ab46c53a1b9652d10a9a8b01161e.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "cross-region-consumer.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::580348834564:role/cdk-hnb659fds-lookup-role-580348834564-us-east-2", + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-2", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } From 80f4092cb227cb588b9279b27fdff41ca5964573 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:17:56 +0000 Subject: [PATCH 06/30] updating README --- packages/@aws-cdk/core/README.md | 33 ++++++++++++++++++- .../@aws-cdk/core/rosetta/default.ts-fixture | 4 +++ packages/aws-cdk-lib/README.md | 33 ++++++++++++++++++- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 54a0e38446481..e56a89aa4c7ec 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -136,7 +136,8 @@ Nested stacks also support the use of Docker image and file assets. ## Accessing resources in a different stack You can access resources in a different stack, as long as they are in the -same account and AWS Region. The following example defines the stack `stack1`, +same account and AWS Region (see [next section](#accessing-resources-in-a-different-stack-and-region) for an exception). +The following example defines the stack `stack1`, which defines an Amazon S3 bucket. Then it defines a second stack, `stack2`, which takes the bucket from stack1 as a constructor property. @@ -161,6 +162,36 @@ in the producing stack and an in the consuming stack to transfer that information from one stack to the other. +## Accessing resources in a different stack and region + +You can enable the feature flag `@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources` +in order to access resources in a different stack _and_ region. With this feature flag +enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and +an ACM certificate in `us-east-1`. + +```ts +const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); +const cert = new acm.Certificate(stack1, 'Cert', { + domainName: '*.example.com', + validation: acm.CertificateValidation.fromDns(route53.PublicHostedZone.fromHostedZoneId(stack1, 'Zone', 'Z0329774B51CGXTDQV3X')), +}); + +const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); +new cloudfront.Distribution(stack2, 'Distribution', { + defaultBehavior: { + origin: new origins.HttpOrigin('example.com'), + }, + domainNames: ['dev.example.com'], + certificate: cert, +}); +``` + +When the AWS CDK determines that the resource is in a different stack _and_ is in a different +region, it automatically synthesizes AWS +CloudFormation [Exports](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html) +in the producing stack. In order to "import" the exports into the consuming stack a CloudFormation +Custom Resource is created which "imports" the values from the cross region stack. + ### Removing automatic cross-stack references The automatic references created by CDK when you use resources across stacks diff --git a/packages/@aws-cdk/core/rosetta/default.ts-fixture b/packages/@aws-cdk/core/rosetta/default.ts-fixture index 23d992a8629a0..cc57d5981d90c 100644 --- a/packages/@aws-cdk/core/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/core/rosetta/default.ts-fixture @@ -1,4 +1,8 @@ import * as cfn from '@aws-cdk/aws-cloudformation'; +import * as cloudfront from '@aws-cdk/aws-cloudfront'; +import * as acm from '@aws-cdk/aws-certificatemanager'; +import * as route53 from '@aws-cdk/aws-route53'; +import * as origins from '@aws-cdk/aws-cloudfront-origins'; import * as customresources from '@aws-cdk/custom-resources'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 63be77ed01bbb..31acd1b61c911 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -167,7 +167,8 @@ Nested stacks also support the use of Docker image and file assets. ## Accessing resources in a different stack You can access resources in a different stack, as long as they are in the -same account and AWS Region. The following example defines the stack `stack1`, +same account and AWS Region (see [next section](#accessing-resources-in-a-different-stack-and-region) for an exception). +The following example defines the stack `stack1`, which defines an Amazon S3 bucket. Then it defines a second stack, `stack2`, which takes the bucket from stack1 as a constructor property. @@ -192,6 +193,36 @@ in the producing stack and an in the consuming stack to transfer that information from one stack to the other. +## Accessing resources in a different stack and region + +You can enable the feature flag `@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources` +in order to access resources in a different stack _and_ region. With this feature flag +enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and +an ACM certificate in `us-east-1`. + +```ts +const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); +const cert = new acm.Certificate(stack1, 'Cert', { + domainName: '*.example.com', + validation: acm.CertificateValidation.fromDns(route53.PublicHostedZone.fromHostedZoneId(stack1, 'Zone', 'Z0329774B51CGXTDQV3X')), +}); + +const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); +new cloudfront.Distribution(stack2, 'Distribution', { + defaultBehavior: { + origin: new origins.HttpOrigin('example.com'), + }, + domainNames: ['dev.example.com'], + certificate: cert, +}); +``` + +When the AWS CDK determines that the resource is in a different stack _and_ is in a different +region, it automatically synthesizes AWS +CloudFormation [Exports](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html) +in the producing stack. In order to "import" the exports into the consuming stack a CloudFormation +Custom Resource is created which "imports" the values from the cross region stack. + ### Removing automatic cross-stack references The automatic references created by CDK when you use resources across stacks From 783cb026506a6b30c8343a96853ab9543ae9026c Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 23 Sep 2022 17:45:37 +0000 Subject: [PATCH 07/30] switching to ssm writer --- .../__entrypoint__.js | 118 ++++++++ .../index.d.ts | 1 + .../index.js | 97 +++++++ .../index.ts | 98 +++++++ .../cross-region-consumer.assets.json | 34 +-- .../cross-region-consumer.template.json | 92 +----- .../cross-region-producer.assets.json | 30 +- .../cross-region-producer.template.json | 109 +++++-- ...erIntegNested815BEF8A.nested.template.json | 90 +----- .../manifest.json | 70 ++--- .../cross-region-ssm-writer-handler/index.ts | 98 +++++++ .../export-reader-provider.ts | 82 ++---- packages/@aws-cdk/core/lib/private/refs.ts | 94 +++--- packages/@aws-cdk/core/lib/stack.ts | 52 +++- .../core/test/cross-environment-token.test.ts | 45 ++- .../cross-region-ssm-writer-handler.test.ts | 238 ++++++++++++++++ .../export-reader-provider.test.ts | 60 +++- .../@aws-cdk/core/test/nested-stack.test.ts | 69 +++-- packages/@aws-cdk/core/test/stack.test.ts | 267 ++++++++---------- 19 files changed, 1127 insertions(+), 617 deletions(-) create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts create mode 100644 packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts create mode 100644 packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js new file mode 100644 index 0000000000000..9df94382cc74e --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + await exports.external.sendHttpRequest(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts new file mode 100644 index 0000000000000..3554dc94d4617 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts @@ -0,0 +1 @@ +export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js new file mode 100644 index 0000000000000..a45e08eb17a6d --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js @@ -0,0 +1,97 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +const SSM_EXPORT_PATH = '/cdk/exports/'; +async function handler(event) { + const props = event.ResourceProperties; + const exports = props.Exports; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Update': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existing = await getExistingParameters(ssm); + const paramsToDelete = returnMissing(existing, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (paramsToDelete.length > 0) { + await ssm.deleteParameters({ + Names: paramsToDelete, + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Delete': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existingParams = await getExistingParameters(ssm); + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(existingParams)), + }).promise(); + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: `${SSM_EXPORT_PATH}${name}`, + Value: value, + Type: 'String', + }).promise(); + })); +} +function returnMissing(a, b) { + const missing = []; + for (const name of Object.keys(a)) { + if (!b.hasOwnProperty(name)) { + missing.push(name); + } + } + return missing; +} +/** + * Get existing exports from SSM parameters + */ +async function getExistingParameters(ssm) { + const existingExports = {}; + function recordParameters(parameters) { + parameters.forEach(param => { + if (param.Name && param.Value) { + existingExports[param.Name] = param.Value; + } + }); + } + const res = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + }).promise(); + recordParameters(res.Parameters ?? []); + while (res.NextToken) { + const nextRes = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + NextToken: res.NextToken, + }).promise(); + recordParameters(nextRes.Parameters ?? []); + res.NextToken = nextRes.NextToken; + } + return existingExports; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUM5QixNQUFNLGVBQWUsR0FBRyxlQUFlLENBQUM7QUFHakMsS0FBSyxVQUFVLE9BQU8sQ0FBQyxLQUFrRDtJQUM5RSxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUM7SUFDdkMsTUFBTSxPQUFPLEdBQXVCLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFFbEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLG9EQUFvRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDakYsTUFBTSxRQUFRLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDbEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDeEQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUksY0FBYyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7b0JBQzdCLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO3dCQUN6QixLQUFLLEVBQUUsY0FBYztxQkFDdEIs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bUIsQ0FBQztZQUM1QyxJQUFJLEVBQUUsR0FBRyxlQUFlLEVBQUU7WUFDMUIsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTO1NBQ3pCLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUM7UUFDM0MsR0FBRyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDO0tBQ25DO0lBQ0QsT0FBTyxlQUFlLENBQUM7QUFDekIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xuY29uc3QgU1NNX0VYUE9SVF9QQVRIID0gJy9jZGsvZXhwb3J0cy8nO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBSZWFkaW5nIGV4aXN0aW5nIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbSk7XG4gICAgICAgIGNvbnN0IHBhcmFtc1RvRGVsZXRlID0gcmV0dXJuTWlzc2luZyhleGlzdGluZywgZXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGlmIChwYXJhbXNUb0RlbGV0ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgYXdhaXQgc3NtLmRlbGV0ZVBhcmFtZXRlcnMoe1xuICAgICAgICAgICAgTmFtZXM6IHBhcmFtc1RvRGVsZXRlLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgUmVhZGluZyBleGlzdGluZyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBjb25zdCBleGlzdGluZ1BhcmFtcyA9IGF3YWl0IGdldEV4aXN0aW5nUGFyYW1ldGVycyhzc20pO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIGFsbCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IEFycmF5LmZyb20oT2JqZWN0LmtleXMoZXhpc3RpbmdQYXJhbXMpKSxcbiAgICAgICAgfSkucHJvbWlzZSgpO1xuICAgICAgICByZXR1cm47XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm47XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc29sZS5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBldmVudDogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBDcmVhdGUgcGFyYW1ldGVycyBmb3IgZXhpc3RpbmcgZXhwb3J0c1xuICovXG5hc3luYyBmdW5jdGlvbiBwdXRQYXJhbWV0ZXJzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgUHJvbWlzZS5hbGwoQXJyYXkuZnJvbShPYmplY3QuZW50cmllcyhwYXJhbWV0ZXJzKSwgKFtuYW1lLCB2YWx1ZV0pID0+IHtcbiAgICByZXR1cm4gc3NtLnB1dFBhcmFtZXRlcih7XG4gICAgICBOYW1lOiBgJHtTU01fRVhQT1JUX1BBVEh9JHtuYW1lfWAsXG4gICAgICBWYWx1ZTogdmFsdWUsXG4gICAgICBUeXBlOiAnU3RyaW5nJyxcbiAgICB9KS5wcm9taXNlKCk7XG4gIH0pKTtcbn1cblxuZnVuY3Rpb24gcmV0dXJuTWlzc2luZyhhOiBDcm9zc1JlZ2lvbkV4cG9ydHMsIGI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IHN0cmluZ1tdIHtcbiAgY29uc3QgbWlzc2luZzogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChjb25zdCBuYW1lIG9mIE9iamVjdC5rZXlzKGEpKSB7XG4gICAgaWYgKCFiLmhhc093blByb3BlcnR5KG5hbWUpKSB7XG4gICAgICBtaXNzaW5nLnB1c2gobmFtZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBtaXNzaW5nO1xufVxuXG4vKipcbiAqIEdldCBleGlzdGluZyBleHBvcnRzIGZyb20gU1NNIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbTogU1NNKTogUHJvbWlzZTxDcm9zc1JlZ2lvbkV4cG9ydHM+IHtcbiAgY29uc3QgZXhpc3RpbmdFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7fTtcbiAgZnVuY3Rpb24gcmVjb3JkUGFyYW1ldGVycyhwYXJhbWV0ZXJzOiBTU00uUGFyYW1ldGVyTGlzdCkge1xuICAgIHBhcmFtZXRlcnMuZm9yRWFjaChwYXJhbSA9PiB7XG4gICAgICBpZiAocGFyYW0uTmFtZSAmJiBwYXJhbS5WYWx1ZSkge1xuICAgICAgICBleGlzdGluZ0V4cG9ydHNbcGFyYW0uTmFtZV0gPSBwYXJhbS5WYWx1ZTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBjb25zdCByZXMgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVyc0J5UGF0aCh7XG4gICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gIH0pLnByb21pc2UoKTtcbiAgcmVjb3JkUGFyYW1ldGVycyhyZXMuUGFyYW1ldGVycyA/PyBbXSk7XG5cbiAgd2hpbGUgKHJlcy5OZXh0VG9rZW4pIHtcbiAgICBjb25zdCBuZXh0UmVzID0gYXdhaXQgc3NtLmdldFBhcmFtZXRlcnNCeVBhdGgoe1xuICAgICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gICAgICBOZXh0VG9rZW46IHJlcy5OZXh0VG9rZW4sXG4gICAgfSkucHJvbWlzZSgpO1xuICAgIHJlY29yZFBhcmFtZXRlcnMobmV4dFJlcy5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICByZXMuTmV4dFRva2VuID0gbmV4dFJlcy5OZXh0VG9rZW47XG4gIH1cbiAgcmV0dXJuIGV4aXN0aW5nRXhwb3J0cztcbn1cblxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts new file mode 100644 index 0000000000000..40c505eb55657 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts @@ -0,0 +1,98 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; +const SSM_EXPORT_PATH = '/cdk/exports/'; +type CrossRegionExports = { [exportName: string]: string }; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const exports: CrossRegionExports = props.Exports; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Update': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existing = await getExistingParameters(ssm); + const paramsToDelete = returnMissing(existing, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (paramsToDelete.length > 0) { + await ssm.deleteParameters({ + Names: paramsToDelete, + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Delete': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existingParams = await getExistingParameters(ssm); + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(existingParams)), + }).promise(); + return; + default: + return; + } + } catch (e) { + console.error('Error processing event: ', e); + throw e; + } +}; + +/** + * Create parameters for existing exports + */ +async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: `${SSM_EXPORT_PATH}${name}`, + Value: value, + Type: 'String', + }).promise(); + })); +} + +function returnMissing(a: CrossRegionExports, b: CrossRegionExports): string[] { + const missing: string[] = []; + for (const name of Object.keys(a)) { + if (!b.hasOwnProperty(name)) { + missing.push(name); + } + } + return missing; +} + +/** + * Get existing exports from SSM parameters + */ +async function getExistingParameters(ssm: SSM): Promise { + const existingExports: CrossRegionExports = {}; + function recordParameters(parameters: SSM.ParameterList) { + parameters.forEach(param => { + if (param.Name && param.Value) { + existingExports[param.Name] = param.Value; + } + }); + } + const res = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + }).promise(); + recordParameters(res.Parameters ?? []); + + while (res.NextToken) { + const nextRes = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + NextToken: res.NextToken, + }).promise(); + recordParameters(nextRes.Parameters ?? []); + res.NextToken = nextRes.NextToken; + } + return existingExports; +} + diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json index b2a79150b208a..43d35abeb77cd 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -1,45 +1,31 @@ { "version": "21.0.0", "files": { - "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf": { - "source": { - "path": "asset.1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf", - "packaging": "zip" - }, - "destinations": { - "12345678-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip", - "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" - } - } - }, - "f18a7bd6678575898edf76b4c96e9f9dee4f6ef1269ff16aa9d3c93a9862fd51": { + "61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404": { "source": { "path": "crossregionconsumerIntegNested815BEF8A.nested.template.json", "packaging": "file" }, "destinations": { - "12345678-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "f18a7bd6678575898edf76b4c96e9f9dee4f6ef1269ff16aa9d3c93a9862fd51.json", + "539334897376-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-2", + "objectKey": "61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-2" } } }, - "e26c2dfd5f9b7ba5526b3f009caa48d05359ab46c53a1b9652d10a9a8b01161e": { + "3fd74b699f4b03ce328ef95703bb87b2b203a4ea68994f06947d0d1c46130a55": { "source": { "path": "cross-region-consumer.template.json", "packaging": "file" }, "destinations": { - "12345678-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "e26c2dfd5f9b7ba5526b3f009caa48d05359ab46c53a1b9652d10a9a8b01161e.json", + "539334897376-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-2", + "objectKey": "3fd74b699f4b03ce328ef95703bb87b2b203a4ea68994f06947d0d1c46130a55.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-2" } } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json index a570b39e5d55a..7cd3f279d3da4 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -11,7 +11,7 @@ { "Ref": "AWS::URLSuffix" }, - "/cdk-hnb659fds-assets-12345678-us-east-2/f18a7bd6678575898edf76b4c96e9f9dee4f6ef1269ff16aa9d3c93a9862fd51.json" + "/cdk-hnb659fds-assets-539334897376-us-east-2/61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404.json" ] ] } @@ -23,12 +23,7 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": { - "Fn::GetAtt": [ - "ExportsReaderuseast1D746CBDB", - "cross-region-producer:ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" - ] - }, + "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429}}", "Name": "integ-parameter0" } }, @@ -36,90 +31,9 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": { - "Fn::GetAtt": [ - "ExportsReaderuseast1D746CBDB", - "cross-region-producer:ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" - ] - }, + "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698}}", "Name": "integ-parameter1" } - }, - "ExportsReaderuseast1D746CBDB": { - "Type": "Custom::CrossRegionExportReader", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", - "Arn" - ] - }, - "Region": "us-east-1", - "RefreshToken": "232744ad70da548e96b56a2d625574f9" - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ] - }, - "ManagedPolicyArns": [ - { - "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } - ], - "Policies": [ - { - "PolicyName": "Inline", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Resource": "*", - "Action": [ - "cloudformation:ListExports" - ] - } - ] - } - } - ] - } - }, - "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", - "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" - }, - "Timeout": 900, - "MemorySize": 128, - "Handler": "__entrypoint__.handler", - "Role": { - "Fn::GetAtt": [ - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", - "Arn" - ] - }, - "Runtime": "nodejs14.x" - }, - "DependsOn": [ - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" - ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json index e7d81fc5854d4..a660cc46566e8 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -1,31 +1,45 @@ { "version": "21.0.0", "files": { + "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989": { + "source": { + "path": "asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989", + "packaging": "zip" + }, + "destinations": { + "539334897376-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-1", + "objectKey": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-1" + } + } + }, "db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc": { "source": { "path": "crossregionproducerIntegNested3342EBEB.nested.template.json", "packaging": "file" }, "destinations": { - "12345678-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "539334897376-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-1", "objectKey": "db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-1" } } }, - "1b51dc5002497b083a035e23c4860bcb5629e1bc736934175db5d6e44994d6a0": { + "53d01d6d16adb4ed7a288c6b057ad8862a46c20a65e8d446381b09ca57dc7570": { "source": { "path": "cross-region-producer.template.json", "packaging": "file" }, "destinations": { - "12345678-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", - "objectKey": "1b51dc5002497b083a035e23c4860bcb5629e1bc736934175db5d6e44994d6a0.json", + "539334897376-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-1", + "objectKey": "53d01d6d16adb4ed7a288c6b057ad8862a46c20a65e8d446381b09ca57dc7570.json", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-1" } } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json index 05142330017b8..3ed613b7dd170 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -11,7 +11,7 @@ { "Ref": "AWS::URLSuffix" }, - "/cdk-hnb659fds-assets-12345678-us-east-1/db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json" + "/cdk-hnb659fds-assets-539334897376-us-east-1/db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json" ] ] } @@ -23,30 +23,97 @@ "Type": "AWS::SQS::Queue", "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" - } - }, - "Outputs": { - "ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F": { - "Value": { - "Fn::GetAtt": [ - "IntegQueue3A18718A", - "QueueName" - ] + }, + "ExportsWriteruseast2828FA26B": { + "Type": "Custom::CrossRegionExportWriter", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A", + "Arn" + ] + }, + "Region": "us-east-2", + "Exports": { + "cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429": { + "Fn::GetAtt": [ + "IntegQueue3A18718A", + "QueueName" + ] + }, + "cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698": { + "Fn::GetAtt": [ + "IntegNestedNestedStackIntegNestedNestedStackResource168C5881", + "Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" + ] + } + } }, - "Export": { - "Name": "cross-region-producer:ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" - } + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, - "ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE": { - "Value": { - "Fn::GetAtt": [ - "IntegNestedNestedStackIntegNestedNestedStackResource168C5881", - "Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": "arn:aws:ssm:us-east-2:539334897376:parameter/cdk/exports/*", + "Action": [ + "ssm:GetParametersByPath", + "ssm:PutParameter", + "ssm:DeleteParameters" + ] + } + ] + } + } ] - }, - "Export": { - "Name": "cross-region-producer:ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" } + }, + "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-539334897376-us-east-1", + "S3Key": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json index ec22df2c5dac1..616364407930f 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json @@ -4,12 +4,7 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": { - "Fn::GetAtt": [ - "ExportsReaderuseast1D746CBDB", - "cross-region-producer:ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" - ] - }, + "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429}}", "Name": "integ-nested-parameter0" } }, @@ -17,90 +12,9 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": { - "Fn::GetAtt": [ - "ExportsReaderuseast1D746CBDB", - "cross-region-producer:ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" - ] - }, + "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698}}", "Name": "integ-nested-parameter1" } - }, - "ExportsReaderuseast1D746CBDB": { - "Type": "Custom::CrossRegionExportReader", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", - "Arn" - ] - }, - "Region": "us-east-1", - "RefreshToken": "232744ad70da548e96b56a2d625574f9" - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ] - }, - "ManagedPolicyArns": [ - { - "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } - ], - "Policies": [ - { - "PolicyName": "Inline", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Resource": "*", - "Action": [ - "cloudformation:ListExports" - ] - } - ] - } - } - ] - } - }, - "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", - "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" - }, - "Timeout": 900, - "MemorySize": 128, - "Handler": "__entrypoint__.handler", - "Role": { - "Fn::GetAtt": [ - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", - "Arn" - ] - }, - "Runtime": "nodejs14.x" - }, - "DependsOn": [ - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" - ] } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index a9979b099d7b5..5f91fae1486a7 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -11,20 +11,20 @@ }, "cross-region-producer": { "type": "aws:cloudformation:stack", - "environment": "aws://12345678/us-east-1", + "environment": "aws://539334897376/us-east-1", "properties": { "templateFile": "cross-region-producer.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/1b51dc5002497b083a035e23c4860bcb5629e1bc736934175db5d6e44994d6a0.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-deploy-role-539334897376-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-cfn-exec-role-539334897376-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-539334897376-us-east-1/53d01d6d16adb4ed7a288c6b057ad8862a46c20a65e8d446381b09ca57dc7570.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "cross-region-producer.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-1", + "arn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-lookup-role-539334897376-us-east-1", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } @@ -57,16 +57,22 @@ "data": "IntegQueue3A18718A" } ], - "/cross-region-producer/Exports/Output{\"Fn::GetAtt\":[\"IntegQueue3A18718A\",\"QueueName\"]}": [ + "/cross-region-producer/ExportsWriteruseast2828FA26B/Default/Default": [ { "type": "aws:cdk:logicalId", - "data": "ExportsOutputFnGetAttIntegQueue3A18718AQueueName46C58A8F" + "data": "ExportsWriteruseast2828FA26B" } ], - "/cross-region-producer/Exports/Output{\"Fn::GetAtt\":[\"IntegNestedNestedStackIntegNestedNestedStackResource168C5881\",\"Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName\"]}": [ + "/cross-region-producer/Custom::CrossRegionExportWriterCustomResourceProvider/Role": [ { "type": "aws:cdk:logicalId", - "data": "ExportsOutputFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName7C64F4AE" + "data": "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1" + } + ], + "/cross-region-producer/Custom::CrossRegionExportWriterCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A" } ], "/cross-region-producer/BootstrapVersion": [ @@ -94,20 +100,20 @@ }, "cross-region-consumer": { "type": "aws:cloudformation:stack", - "environment": "aws://12345678/us-east-2", + "environment": "aws://539334897376/us-east-2", "properties": { "templateFile": "cross-region-consumer.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/e26c2dfd5f9b7ba5526b3f009caa48d05359ab46c53a1b9652d10a9a8b01161e.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-deploy-role-539334897376-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-cfn-exec-role-539334897376-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-539334897376-us-east-2/3fd74b699f4b03ce328ef95703bb87b2b203a4ea68994f06947d0d1c46130a55.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "cross-region-consumer.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-2", + "arn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-lookup-role-539334897376-us-east-2", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } @@ -129,24 +135,6 @@ "data": "IntegNestedParameter1DE6274D4" } ], - "/cross-region-consumer/IntegNested/ExportsReaderuseast1D746CBDB/Default/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "ExportsReaderuseast1D746CBDB" - } - ], - "/cross-region-consumer/IntegNested/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" - } - ], - "/cross-region-consumer/IntegNested/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" - } - ], "/cross-region-consumer/IntegNested.NestedStack/IntegNested.NestedStackResource": [ { "type": "aws:cdk:logicalId", @@ -165,24 +153,6 @@ "data": "IntegParameter1EDBEF1C6" } ], - "/cross-region-consumer/ExportsReaderuseast1D746CBDB/Default/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "ExportsReaderuseast1D746CBDB" - } - ], - "/cross-region-consumer/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" - } - ], - "/cross-region-consumer/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" - } - ], "/cross-region-consumer/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts new file mode 100644 index 0000000000000..40c505eb55657 --- /dev/null +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts @@ -0,0 +1,98 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; +const SSM_EXPORT_PATH = '/cdk/exports/'; +type CrossRegionExports = { [exportName: string]: string }; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const exports: CrossRegionExports = props.Exports; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Update': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existing = await getExistingParameters(ssm); + const paramsToDelete = returnMissing(existing, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (paramsToDelete.length > 0) { + await ssm.deleteParameters({ + Names: paramsToDelete, + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Delete': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existingParams = await getExistingParameters(ssm); + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(existingParams)), + }).promise(); + return; + default: + return; + } + } catch (e) { + console.error('Error processing event: ', e); + throw e; + } +}; + +/** + * Create parameters for existing exports + */ +async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: `${SSM_EXPORT_PATH}${name}`, + Value: value, + Type: 'String', + }).promise(); + })); +} + +function returnMissing(a: CrossRegionExports, b: CrossRegionExports): string[] { + const missing: string[] = []; + for (const name of Object.keys(a)) { + if (!b.hasOwnProperty(name)) { + missing.push(name); + } + } + return missing; +} + +/** + * Get existing exports from SSM parameters + */ +async function getExistingParameters(ssm: SSM): Promise { + const existingExports: CrossRegionExports = {}; + function recordParameters(parameters: SSM.ParameterList) { + parameters.forEach(param => { + if (param.Name && param.Value) { + existingExports[param.Name] = param.Value; + } + }); + } + const res = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + }).promise(); + recordParameters(res.Parameters ?? []); + + while (res.NextToken) { + const nextRes = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + NextToken: res.NextToken, + }).promise(); + recordParameters(nextRes.Parameters ?? []); + res.NextToken = nextRes.NextToken; + } + return existingExports; +} + diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts index 58ee85efe02b6..681ae1bd3c2b3 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts @@ -1,7 +1,6 @@ -import * as crypto from 'crypto'; import * as path from 'path'; import { Construct } from 'constructs'; -import { CfnResource } from '../cfn-resource'; +import { CfnDynamicReference, CfnDynamicReferenceService } from '../cfn-dynamic-reference'; import { CustomResource } from '../custom-resource'; import { Lazy } from '../lazy'; import { Intrinsic } from '../private/intrinsic'; @@ -9,6 +8,9 @@ import { Reference } from '../reference'; import { Stack } from '../stack'; import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; +type CrossRegionExports = { [exportName: string]: string }; +export const SSM_EXPORT_PATH = 'cdk/exports/'; + /** * Properties for an ExportReader */ @@ -41,79 +43,57 @@ export interface ExportReaderProps { * * @internal - this is intentionally not exported from core */ -export class ExportReader extends Construct { - private readonly resource: CustomResource; - private readonly _references: Reference[] = []; +export class ExportWriter extends Construct { + private readonly _references: CrossRegionExports = {}; constructor(scope: Construct, id: string, props: ExportReaderProps) { super(scope, id); const stack = Stack.of(this); const region = props.region ?? stack.region; - const resourceType = 'Custom::CrossRegionExportReader'; + const resourceType = 'Custom::CrossRegionExportWriter'; const serviceToken = CustomResourceProvider.getOrCreate(this, resourceType, { - codeDirectory: path.join(__dirname, 'get-cfn-exports-handler'), + codeDirectory: path.join(__dirname, 'cross-region-ssm-writer-handler'), runtime: CustomResourceProviderRuntime.NODEJS_14_X, policyStatements: [{ Effect: 'Allow', - Resource: '*', - Action: ['cloudformation:ListExports'], + Resource: stack.formatArn({ + service: 'ssm', + resource: 'parameter', + region, + resourceName: `${SSM_EXPORT_PATH}*`, + }), + Action: [ + 'ssm:GetParametersByPath', + 'ssm:PutParameter', + 'ssm:DeleteParameters', + ], }], }); - this.resource = new CustomResource(this, 'Default', { + new CustomResource(this, 'Default', { resourceType: resourceType, serviceToken, properties: { Region: region, - // This is used to determine when custom resource should be executed again. - // - // We want to lookup the resources whenever any of the references - // change. The only reliable way to tell whether we need to perform another lookup - // is to check if _any_ property of the referenced resource changes - RefreshToken: Lazy.string({ - produce: () => { - const hash = crypto.createHash('md5'); - this._references.forEach(reference => { - const referenceStack = Stack.of(reference.target); - if (CfnResource.isCfnResource(reference.target)) { - const cfn = JSON.stringify(referenceStack.resolve(reference.target._toCloudFormation())); - hash.update(cfn); - } - }); - return hash.digest('hex'); - }, - }), + Exports: Lazy.any({ produce: () => this._references }), }, }); } /** - * Get a CloudFormation Stack export by name - * - * @example - * declare const app: App; - * const stack1 = new Stack(app, 'East1Stack', { env: { region: 'us-east-1' } }); - * new CfnOutput(stack1, 'Output', { value: 'someValue', exportName: 'someName' }); + * Register a reference with the writer and returns a CloudFormation Stack export by name * - * const stack2 = new Stack(app, 'East2Stack', { env: { region: 'us-east-2' } }); - * const exportReader = new ExportReader(stack2, 'ExportReader', { region: 'us-east-1' }); - * const anotherResource = new CfnResource(stack2, 'AnotherResource', { - * Parameters: { - * SomeParam: exportReader.importValue('someName'), - * }, - * }); - */ - public importValue(exportName: string): Intrinsic { - return this.resource.getAtt(exportName); - } - - /** - * Register a reference with the reader. + * The value will be "exported" via the ExportWriter. It will perform + * the export by creating an SSM parameter in the region that the consuming + * stack is created. * - * @internal + * @param exportName the unique name associated with the export + * @param reference the value that will be exported + * @returns a dynamic reference to an ssm parameter */ - public _registerExport(reference: Reference): void { - this._references.push(reference); + public exportValue(exportName: string, reference: Reference): Intrinsic { + this._references[exportName] = Stack.of(this).resolve(reference.toString()); + return new CfnDynamicReference(CfnDynamicReferenceService.SSM, `/${SSM_EXPORT_PATH}${exportName}`); } } diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index d3d754b919e68..ad149b9c7c965 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -3,11 +3,11 @@ // ---------------------------------------------------- import * as cxapi from '@aws-cdk/cx-api'; -import { IConstruct, Construct } from 'constructs'; +import { IConstruct } from 'constructs'; import { CfnElement } from '../cfn-element'; import { CfnOutput } from '../cfn-output'; import { CfnParameter } from '../cfn-parameter'; -import { ExportReader } from '../custom-resource-provider/export-reader-provider'; +import { ExportWriter } from '../custom-resource-provider/export-reader-provider'; import { FeatureFlags } from '../feature-flags'; import { Names } from '../names'; import { Reference } from '../reference'; @@ -203,31 +203,47 @@ function createImportValue(reference: Reference): Intrinsic { */ function createCrossRegionImportValue(reference: Reference, importStack: Stack): Intrinsic { const exportingStack = Stack.of(reference.target); - const exportName = generateExport(exportingStack, reference); - const constructName = makeUniqueId(['ExportsReader', exportingStack.region]); - const existing = importStack.node.tryFindChild(constructName); + // generate an export name + const exportable = getExportable(exportingStack, reference); + const id = JSON.stringify(exportingStack.resolve(exportable)); + const exportName = generateExportName(exportingStack, reference, id); + if (Token.isUnresolved(exportName)) { + throw new Error(`unresolved token in generated export name: ${JSON.stringify(exportingStack.resolve(exportName))}`); + } + + // get or create the export writer + const constructName = makeUniqueId(['ExportsWriter', importStack.region]); + const existing = exportingStack.node.tryFindChild(constructName); const exportReader = existing - ? existing as ExportReader - : new ExportReader(importStack, constructName, { - region: exportingStack.region, + ? existing as ExportWriter + : new ExportWriter(exportingStack, constructName, { + region: importStack.region, }); - exportReader._registerExport(reference); - return exportReader.importValue(exportName); + return exportReader.exportValue(exportName, reference); } -function getCreateExportsScope(stack: Stack) { - const exportsName = 'Exports'; - let stackExports = stack.node.tryFindChild(exportsName) as Construct; - if (stackExports === undefined) { - stackExports = new Construct(stack, exportsName); - } - - return stackExports; +/** + * Generate a unique physical name for the export + */ +function generateExportName(stack: Stack, reference: Reference, id: string): string { + const components = [ + ...reference.target.node.scopes + .slice(stack.node.scopes.length) + .map(c => c.node.id), + id, + ]; + const prefix = stack.stackName ? stack.stackName + '-' : ''; + const localPart = makeUniqueId(components); + // max name length for a system manager parameter is 1011 characters + // including the arn, i.e. + // arn:aws:ssm:us-east-2:111122223333:parameter/cdk/exports/${name} + const maxLength = 900; + return prefix + localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); } -export function generateExport(stack: Stack, reference: Reference): string { // if exportValue is being called manually (which is pre onPrepare) then the logicalId +export function getExportable(stack: Stack, reference: Reference): Reference { // could potentially be changed by a call to overrideLogicalId. This would cause our Export/Import // to have an incorrect id. For a better user experience, lock the logicalId and throw an error // if the user tries to override the id _after_ calling exportValue @@ -237,45 +253,7 @@ export function generateExport(stack: Stack, reference: Reference): string { // // "teleport" the value here, in case it comes from a nested stack. This will also // ensure the value is from our own scope. - const exportable = referenceNestedStackValueInParent(reference, stack); - - // Ensure a singleton "Exports" scoping Construct - // This mostly exists to trigger LogicalID munging, which would be - // disabled if we parented constructs directly under Stack. - // Also it nicely prevents likely construct name clashes - const exportScope = getCreateExportsScope(stack); - - // Ensure a singleton CfnOutput for this value - const resolved = stack.resolve(exportable); - const id = 'Output' + JSON.stringify(resolved); - const exportName = generateExportName(exportScope, id); - - if (Token.isUnresolved(exportName)) { - throw new Error(`unresolved token in generated export name: ${JSON.stringify(stack.resolve(exportName))}`); - } - - const output = exportScope.node.tryFindChild(id) as CfnOutput; - if (!output) { - new CfnOutput(exportScope, id, { value: Token.asString(exportable), exportName }); - } - - return exportName; -} - -function generateExportName(stackExports: Construct, id: string) { - const stackRelativeExports = FeatureFlags.of(stackExports).isEnabled(cxapi.STACK_RELATIVE_EXPORTS_CONTEXT); - const stack = Stack.of(stackExports); - - const components = [ - ...stackExports.node.scopes - .slice(stackRelativeExports ? stack.node.scopes.length : 2) - .map(c => c.node.id), - id, - ]; - const prefix = stack.stackName ? stack.stackName + ':' : ''; - const localPart = makeUniqueId(components); - const maxLength = 255; - return prefix + localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); + return referenceNestedStackValueInParent(reference, stack); } // ------------------------------------------------------------------------------------------------ diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 8227fd069fd30..9f917d78f70ea 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -907,7 +907,29 @@ export class Stack extends Construct implements ITaggable { throw new Error('exportValue: either supply \'name\' or make sure to export a resource attribute (like \'bucket.bucketName\')'); } - const exportName = generateExport(this, resolvable); + // "teleport" the value here, in case it comes from a nested stack. This will also + // ensure the value is from our own scope. + const exportable = getExportable(this, resolvable); + + // Ensure a singleton "Exports" scoping Construct + // This mostly exists to trigger LogicalID munging, which would be + // disabled if we parented constructs directly under Stack. + // Also it nicely prevents likely construct name clashes + const exportsScope = getCreateExportsScope(this); + + // Ensure a singleton CfnOutput for this value + const resolved = this.resolve(exportable); + const id = 'Output' + JSON.stringify(resolved); + const exportName = generateExportName(exportsScope, id); + + if (Token.isUnresolved(exportName)) { + throw new Error(`unresolved token in generated export name: ${JSON.stringify(this.resolve(exportName))}`); + } + + const output = exportsScope.node.tryFindChild(id) as CfnOutput; + if (!output) { + new CfnOutput(exportsScope, id, { value: Token.asString(exportable), exportName }); + } return Fn.importValue(exportName); } @@ -1293,6 +1315,32 @@ function makeStackName(components: string[]) { return makeUniqueResourceName(components, { maxLength: 128 }); } +function getCreateExportsScope(stack: Stack) { + const exportsName = 'Exports'; + let stackExports = stack.node.tryFindChild(exportsName) as Construct; + if (stackExports === undefined) { + stackExports = new Construct(stack, exportsName); + } + + return stackExports; +} + +function generateExportName(stackExports: Construct, id: string) { + const stackRelativeExports = FeatureFlags.of(stackExports).isEnabled(cxapi.STACK_RELATIVE_EXPORTS_CONTEXT); + const stack = Stack.of(stackExports); + + const components = [ + ...stackExports.node.scopes + .slice(stackRelativeExports ? stack.node.scopes.length : 2) + .map(c => c.node.id), + id, + ]; + const prefix = stack.stackName ? stack.stackName + ':' : ''; + const localPart = makeUniqueId(components); + const maxLength = 255; + return prefix + localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); +} + interface StackDependency { stack: Stack; reasons: string[]; @@ -1334,7 +1382,7 @@ import { DefaultStackSynthesizer, IStackSynthesizer, ISynthesisSession, LegacySt import { Stage } from './stage'; import { ITaggable, TagManager } from './tag-manager'; import { Token, Tokenization } from './token'; -import { generateExport } from './private/refs'; +import { getExportable } from './private/refs'; import { Fact, RegionInfo } from '@aws-cdk/region-info'; import { deployTimeLookup } from './private/region-lookup'; import { makeUniqueResourceName } from './private/unique-resource-name'; diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index 795e78eea4a04..cdbb4b4e4085f 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -215,43 +215,30 @@ describe('cross environment', () => { const template1 = assembly.getStackByName(stack1.stackName).template; const template2 = assembly.getStackByName(stack2.stackName).template; - expect(template1?.Outputs).toEqual({ - 'ExportsOutputRefMyResource6073B41F0296C218': { - 'Export': { - 'Name': 'Stack1:ExportsOutputRefMyResource6073B41F0296C218', - }, - 'Value': { - 'Ref': 'MyResource6073B41F', - }, - }, - }); - expect(template2?.Resources).toMatchObject({ - 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68': { - 'DependsOn': [ - 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', - ], - 'Type': 'AWS::Lambda::Function', - }, - 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD': { - 'Type': 'AWS::IAM::Role', - }, - 'ExportsReaderbermudatriangle1337E63A6E15': { + expect(template1?.Resources).toMatchObject({ + 'ExportsWriterbermudatriangle42E5959427': { 'DeletionPolicy': 'Delete', 'Properties': { - 'Region': 'bermuda-triangle-1337', + 'Exports': { + 'Stack1-MyResourceRefMyResource6073B41F992B761C': { + 'Ref': 'MyResource6073B41F', + }, + }, + 'Region': 'bermuda-triangle-42', + 'ServiceToken': { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', + ], + }, }, - 'Type': 'Custom::CrossRegionExportReader', + 'Type': 'Custom::CrossRegionExportWriter', 'UpdateReplacePolicy': 'Delete', }, }); expect(template2?.Outputs).toEqual({ 'Output': { - 'Value': { - 'Fn::GetAtt': [ - 'ExportsReaderbermudatriangle1337E63A6E15', - 'Stack1:ExportsOutputRefMyResource6073B41F0296C218', - ], - }, + 'Value': '{{resolve:ssm:/cdk/exports/Stack1-MyResourceRefMyResource6073B41F992B761C}}', }, }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts new file mode 100644 index 0000000000000..72c114fda75ad --- /dev/null +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -0,0 +1,238 @@ +import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-writer-handler'; +import { SSM_EXPORT_PATH } from '../../lib/custom-resource-provider/export-reader-provider'; + +let mockPutParameter: jest.Mock ; +let mockGetParametersByPath: jest.Mock; +let mockDeleteParameters: jest.Mock; +jest.mock('aws-sdk', () => { + return { + SSM: jest.fn(() => { + return { + putParameter: jest.fn((params) => { + return { + promise: () => mockPutParameter(params), + }; + }), + deleteParameters: jest.fn((params) => { + return { + promise: () => mockDeleteParameters(params), + }; + }), + getParametersByPath: jest.fn((params) => { + return { + promise: () => mockGetParametersByPath(params), + }; + }), + }; + }), + }; +}); +beforeEach(() => { + jest.spyOn(console, 'info').mockImplementation(() => {}); + jest.spyOn(console, 'error').mockImplementation(() => {}); + mockPutParameter = jest.fn(); + mockGetParametersByPath = jest.fn(); + mockDeleteParameters = jest.fn(); + mockPutParameter.mockImplementation(() => { + return {}; + }); +}); +afterEach(() => { + jest.restoreAllMocks(); +}); + +describe('cross-region-ssm-writer entrypoint', () => { + test('Create event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + Exports: { + MyExport: 'Value', + }, + }, + }); + + // WHEN + await handler(event); + + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH}MyExport`, + Value: 'Value', + }); + expect(mockPutParameter).toHaveBeenCalledTimes(1); + expect(mockDeleteParameters).toHaveBeenCalledTimes(0); + expect(mockGetParametersByPath).toHaveBeenCalledTimes(0); + }); + + test('Update event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + Exports: { + MyExport: 'Value', + }, + }, + }); + + // WHEN + mockGetParametersByPath.mockImplementation(() => { + return { + Parameters: [{ + Name: 'MyExport', + Value: 'Value', + }], + }; + }); + await handler(event); + + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH}MyExport`, + Value: 'Value', + }); + expect(mockPutParameter).toHaveBeenCalledTimes(1); + expect(mockDeleteParameters).toHaveBeenCalledTimes(0); + expect(mockGetParametersByPath).toHaveBeenCalledTimes(1); + }); + + test('Update event with nexttoken', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + Exports: { + MyExport: 'Value', + MyOtherExport: 'MyOtherValue', + }, + }, + }); + + // WHEN + mockGetParametersByPath.mockImplementationOnce(() => { + return { + NextToken: 'abc', + Parameters: [{ + Name: 'MyExport', + Value: 'Value', + }], + }; + }).mockImplementation(() => { + return { + Parameters: [{ + Name: 'MyOtherExport', + Value: 'MyOtherValue', + }], + }; + }); + await handler(event); + + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH}MyExport`, + Value: 'Value', + }); + expect(mockPutParameter).toHaveBeenCalledTimes(2); + expect(mockDeleteParameters).toHaveBeenCalledTimes(0); + expect(mockGetParametersByPath).toHaveBeenCalledTimes(2); + }); + + test('Update event with delete', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + Exports: { + MyExport: 'Value', + MyOtherExport: 'MyOtherValue', + }, + }, + }); + + // WHEN + mockGetParametersByPath.mockImplementation(() => { + return { + Parameters: [{ + Name: 'RemovedExport', + Value: 'RemovedValue', + }], + }; + }); + await handler(event); + + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH}MyExport`, + Value: 'Value', + }); + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH}MyOtherExport`, + Value: 'MyOtherValue', + }); + expect(mockDeleteParameters).toHaveBeenCalledWith({ + Names: ['RemovedExport'], + }); + expect(mockPutParameter).toHaveBeenCalledTimes(2); + expect(mockDeleteParameters).toHaveBeenCalledTimes(1); + expect(mockGetParametersByPath).toHaveBeenCalledTimes(1); + }); + test('Delete event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Delete', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + Exports: { + MyExport: 'Value', + MyOtherExport: 'MyOtherValue', + }, + }, + }); + + // WHEN + mockGetParametersByPath.mockImplementation(() => { + return { + Parameters: [{ + Name: 'RemovedExport', + Value: 'RemovedValue', + }], + }; + }); + await handler(event); + + // THEN + expect(mockDeleteParameters).toHaveBeenCalledWith({ + Names: ['RemovedExport'], + }); + expect(mockPutParameter).toHaveBeenCalledTimes(0); + expect(mockDeleteParameters).toHaveBeenCalledTimes(1); + expect(mockGetParametersByPath).toHaveBeenCalledTimes(1); + }); +}); + +function makeEvent(req: Partial): AWSLambda.CloudFormationCustomResourceEvent { + return { + LogicalResourceId: '', + RequestId: '', + ResourceType: '', + ResponseURL: '', + ServiceToken: '', + StackId: '', + ResourceProperties: { + ServiceToken: '', + ...req.ResourceProperties, + }, + ...req, + } as any; +} diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts index a6f7d9d2f0a1d..eae044bf634b6 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts @@ -1,5 +1,5 @@ -import { App, Stack, AssetStaging } from '../../lib'; -import { ExportReader } from '../../lib/custom-resource-provider/export-reader-provider'; +import { App, Stack, AssetStaging, CfnResource } from '../../lib'; +import { ExportWriter } from '../../lib/custom-resource-provider/export-reader-provider'; import { toCloudFormation } from '../util'; @@ -8,20 +8,28 @@ describe('export reader provider', () => { // GIVEN const app = new App(); const stack = new Stack(app); + const resource = new CfnResource(stack, 'MyResource', { + type: 'Custom::MyResource', + }); // WHEN - new ExportReader(stack, 'ExportReader', { + const exportWriter = new ExportWriter(stack, 'ExportWriter', { region: 'us-east-1', }); + const exportValue = exportWriter.exportValue('MyResourceName', resource.getAtt('arn')); // THEN const cfn = toCloudFormation(stack); - const staging = stack.node.tryFindChild('Custom::CrossRegionExportReaderCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; + const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; const assetHash = staging.assetHash; + expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/MyResourceName}}'); expect(cfn).toEqual({ Resources: { - CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD: { + MyResource: { + Type: 'Custom::MyResource', + }, + CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: { @@ -42,10 +50,27 @@ describe('export reader provider', () => { Statement: [ { Action: [ - 'cloudformation:ListExports', + 'ssm:GetParametersByPath', + 'ssm:PutParameter', + 'ssm:DeleteParameters', ], Effect: 'Allow', - Resource: '*', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':ssm:us-east-1:', + { + Ref: 'AWS::AccountId', + }, + ':parameter/cdk/exports/*', + ], + ], + }, }, ], Version: '2012-10-17', @@ -60,22 +85,29 @@ describe('export reader provider', () => { ], }, }, - ExportReader: { + ExportWriter: { DeletionPolicy: 'Delete', Properties: { - RefreshToken: expect.any(String), Region: 'us-east-1', ServiceToken: { 'Fn::GetAtt': [ - 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', 'Arn', ], }, + Exports: { + MyResourceName: { + 'Fn::GetAtt': [ + 'MyResource', + 'arn', + ], + }, + }, }, - Type: 'Custom::CrossRegionExportReader', + Type: 'Custom::CrossRegionExportWriter', UpdateReplacePolicy: 'Delete', }, - CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A: { Type: 'AWS::Lambda::Function', Properties: { Code: { @@ -89,14 +121,14 @@ describe('export reader provider', () => { Handler: '__entrypoint__.handler', Role: { 'Fn::GetAtt': [ - 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + 'CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1', 'Arn', ], }, Runtime: 'nodejs14.x', }, DependsOn: [ - 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + 'CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1', ], }, }, diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 5824ade52d8f1..8fa22c3110203 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -64,51 +64,46 @@ describe('nested-stack', () => { // THEN const assembly = app.synth(); - const template2 = JSON.parse(readFileSync(path.join(assembly.directory, `${nestedStack2.artifactId}.nested.template.json`), 'utf8')); - expect(template2).toMatchObject({ - Resources: { - CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { - DependsOn: [ - 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', - ], - Type: 'AWS::Lambda::Function', - }, - CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD: { - Type: 'AWS::IAM::Role', - }, - ExportsReaderbermudatriangle1337E63A6E15: { - DeletionPolicy: 'Delete', - Properties: { - Region: 'bermuda-triangle-1337', - }, - Type: 'Custom::CrossRegionExportReader', - UpdateReplacePolicy: 'Delete', - }, - }, + const nestedTemplate2 = JSON.parse(readFileSync(path.join(assembly.directory, `${nestedStack2.artifactId}.nested.template.json`), 'utf8')); + expect(nestedTemplate2).toMatchObject({ Outputs: { Output: { - Value: { - 'Fn::GetAtt': [ - 'ExportsReaderbermudatriangle1337E63A6E15', - 'Stack1:ExportsOutputFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref16CD9A2F', - ], - }, + Value: '{{resolve:ssm:/cdk/exports/Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D}}', }, }, }); const template1 = assembly.getStackByName(stack1.stackName).template; - - expect(template1?.Outputs).toEqual({ - ExportsOutputFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref16CD9A2F: { - Export: { - Name: 'Stack1:ExportsOutputFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref16CD9A2F', - }, + const nestedTemplate1 = JSON.parse(readFileSync(path.join(assembly.directory, `${nestedStack.artifactId}.nested.template.json`), 'utf8')); + expect(nestedTemplate1?.Outputs).toEqual({ + Stack1MyNestedStackMyResourceEDA18296Ref: { Value: { - 'Fn::GetAtt': [ - 'MyNestedStackNestedStackMyNestedStackNestedStackResource9C617903', - 'Outputs.Stack1MyNestedStackMyResourceEDA18296Ref', - ], + Ref: 'MyResource6073B41F', + }, + }, + }); + + expect(template1?.Resources).toMatchObject({ + ExportsWriterbermudatriangle42E5959427: { + DeletionPolicy: 'Delete', + Properties: { + Exports: { + 'Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D': { + 'Fn::GetAtt': [ + 'MyNestedStackNestedStackMyNestedStackNestedStackResource9C617903', + 'Outputs.Stack1MyNestedStackMyResourceEDA18296Ref', + ], + }, + }, + Region: 'bermuda-triangle-42', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', + ], + }, }, + Type: 'Custom::CrossRegionExportWriter', + UpdateReplacePolicy: 'Delete', }, }); }); diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index ca757f9c1ec2a..e6872ba562900 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -490,17 +490,25 @@ describe('stack', () => { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - }, - Outputs: { - ExportsOutputFnGetAttSomeResourceExportname33D556F7: { - Export: { - Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', - }, - Value: { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'name', - ], + ExportsWriteruseast2828FA26B: { + Type: 'Custom::CrossRegionExportWriter', + DeletionPolicy: 'Delete', + Properties: { + Exports: { + 'Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + }, + Region: 'us-east-2', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', + ], + }, }, }, }, @@ -511,20 +519,9 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: { - 'Fn::GetAtt': [ - 'ExportsReaderuseast1D746CBDB', - 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', - ], - }, + Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', }, }, - CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { - Type: 'AWS::Lambda::Function', - }, - ExportsReaderuseast1D746CBDB: { - Type: 'Custom::CrossRegionExportReader', - }, }, }); }); @@ -582,91 +579,78 @@ describe('stack', () => { const template1 = assembly.getStackByName(stack1.stackName).template; // THEN - const exportReaders = Object.entries(template2.Resources).filter((res: [string, any]) => res[1].Type === 'Custom::CrossRegionExportReader'); - expect(exportReaders.length).toEqual(1); - expect(template3).toMatchObject({ + expect(template2).toMatchObject({ Resources: { - SomeResourceExport: { + SomeResource: { Type: 'AWS::S3::Bucket', - }, - }, - Outputs: { - ExportsOutputFnGetAttSomeResourceExportother2AC0F424D: { - Export: { - Name: 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', - }, - Value: { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other2', - ], + Properties: { + Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', }, }, }, }); - expect(template1).toMatchObject({ + expect(template3).toMatchObject({ Resources: { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - }, - Outputs: { - ExportsOutputFnGetAttSomeResourceExportname33D556F7: { - Export: { - Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', - }, - Value: { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'name', - ], - }, - }, - ExportsOutputFnGetAttSomeResourceExportotherA189B4B9: { - Export: { - Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', - }, - Value: { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other', - ], + ExportsWriteruseast2828FA26B: { + Type: 'Custom::CrossRegionExportWriter', + DeletionPolicy: 'Delete', + Properties: { + Exports: { + 'Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other2', + ], + }, + }, + Region: 'us-east-2', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', + ], + }, }, }, }, }); - - expect(template2).toMatchObject({ + expect(template1).toMatchObject({ Resources: { - SomeResource: { + SomeResourceExport: { Type: 'AWS::S3::Bucket', + }, + ExportsWriteruseast2828FA26B: { + Type: 'Custom::CrossRegionExportWriter', + DeletionPolicy: 'Delete', Properties: { - Name: { - 'Fn::GetAtt': [ - 'ExportsReaderuseast1D746CBDB', - 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', - ], - }, - Other: { - 'Fn::GetAtt': [ - 'ExportsReaderuseast1D746CBDB', - 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', - ], + Exports: { + 'Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + 'Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other', + ], + }, }, - Other2: { + Region: 'us-east-2', + ServiceToken: { 'Fn::GetAtt': [ - 'ExportsReaderuseast1D746CBDB', - 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', ], }, }, }, - CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { - Type: 'AWS::Lambda::Function', - }, - ExportsReaderuseast1D746CBDB: { - Type: 'Custom::CrossRegionExportReader', - }, }, }); }); @@ -701,92 +685,83 @@ describe('stack', () => { const template1 = assembly.getStackByName(stack1.stackName).template; // THEN - const exportReaders = Object.entries(template2.Resources).filter((res: [string, any]) => res[1].Type === 'Custom::CrossRegionExportReader'); - expect(exportReaders.length).toEqual(2); expect(template3).toMatchObject({ Resources: { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, }, - Outputs: { - ExportsOutputFnGetAttSomeResourceExportother2AC0F424D: { - Export: { - Name: 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', - }, - Value: { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other2', - ], + }); + expect(template2).toMatchObject({ + Resources: { + SomeResource: { + Type: 'AWS::S3::Bucket', + Properties: { + Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', }, }, }, }); - expect(template1).toMatchObject({ + expect(template3).toMatchObject({ Resources: { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - }, - Outputs: { - ExportsOutputFnGetAttSomeResourceExportname33D556F7: { - Export: { - Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', - }, - Value: { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'name', - ], - }, - }, - ExportsOutputFnGetAttSomeResourceExportotherA189B4B9: { - Export: { - Name: 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', - }, - Value: { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other', - ], + ExportsWriteruseast2828FA26B: { + Type: 'Custom::CrossRegionExportWriter', + DeletionPolicy: 'Delete', + Properties: { + Exports: { + 'Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other2', + ], + }, + }, + Region: 'us-east-2', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', + ], + }, }, }, }, }); - - expect(template2).toMatchObject({ + expect(template1).toMatchObject({ Resources: { - SomeResource: { + SomeResourceExport: { Type: 'AWS::S3::Bucket', + }, + ExportsWriteruseast2828FA26B: { + Type: 'Custom::CrossRegionExportWriter', + DeletionPolicy: 'Delete', Properties: { - Name: { - 'Fn::GetAtt': [ - 'ExportsReaderuseast1D746CBDB', - 'Stack1:ExportsOutputFnGetAttSomeResourceExportname33D556F7', - ], + Exports: { + 'Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + 'Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other', + ], + }, }, - Other: { + Region: 'us-east-2', + ServiceToken: { 'Fn::GetAtt': [ - 'ExportsReaderuseast1D746CBDB', - 'Stack1:ExportsOutputFnGetAttSomeResourceExportotherA189B4B9', + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', ], }, - Other2: { - 'Fn::GetAtt': [ - 'ExportsReaderuswest1619FF90A', - 'Stack3:ExportsOutputFnGetAttSomeResourceExportother2AC0F424D', - ], - }, - }, - }, - CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { - Type: 'AWS::Lambda::Function', - }, - ExportsReaderuseast1D746CBDB: { - Type: 'Custom::CrossRegionExportReader', - Properties: { - Region: 'us-east-1', }, }, }, From 49944e6c7d8bda5ba996116cc627c649966da09b Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 23 Sep 2022 19:17:57 +0000 Subject: [PATCH 08/30] updating integration tests --- .../cross-region-consumer.assets.json | 16 +- .../cross-region-consumer.template.json | 6 +- .../cross-region-producer.assets.json | 22 +-- .../cross-region-producer.template.json | 23 ++- .../manifest.json | 20 +-- .../integ.core-cross-region-references.ts | 2 - .../test/integ.pipeline-with-replication.ts | 3 - .../__entrypoint__.js | 118 ++++++++++++++ .../index.d.ts | 1 + .../index.js | 97 +++++++++++ .../index.ts | 98 ++++++++++++ .../integ-pipeline-consumer-stack.assets.json | 30 +--- ...nteg-pipeline-consumer-stack.template.json | 151 +++++++----------- .../integ-pipeline-producer-stack.assets.json | 30 +++- ...nteg-pipeline-producer-stack.template.json | 131 ++++++++++++--- .../manifest.json | 52 +++--- 16 files changed, 586 insertions(+), 214 deletions(-) create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json index 43d35abeb77cd..0b1381df8b5ff 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -7,25 +7,25 @@ "packaging": "file" }, "destinations": { - "539334897376-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-2", + "current_account-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", "objectKey": "61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "3fd74b699f4b03ce328ef95703bb87b2b203a4ea68994f06947d0d1c46130a55": { + "073479a1920f44222959fc9231ad62d389ab3fe14dc0a6cdc14a3f1f54fb8dfc": { "source": { "path": "cross-region-consumer.template.json", "packaging": "file" }, "destinations": { - "539334897376-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-2", - "objectKey": "3fd74b699f4b03ce328ef95703bb87b2b203a4ea68994f06947d0d1c46130a55.json", + "current_account-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", + "objectKey": "073479a1920f44222959fc9231ad62d389ab3fe14dc0a6cdc14a3f1f54fb8dfc.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json index 7cd3f279d3da4..64beb86477802 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -11,7 +11,11 @@ { "Ref": "AWS::URLSuffix" }, - "/cdk-hnb659fds-assets-539334897376-us-east-2/61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404.json" + "/", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" + }, + "/61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404.json" ] ] } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json index a660cc46566e8..a00df32d70971 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -7,11 +7,11 @@ "packaging": "zip" }, "destinations": { - "539334897376-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-1", + "current_account-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", "objectKey": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, @@ -21,25 +21,25 @@ "packaging": "file" }, "destinations": { - "539334897376-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-1", + "current_account-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", "objectKey": "db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "53d01d6d16adb4ed7a288c6b057ad8862a46c20a65e8d446381b09ca57dc7570": { + "ba6acb4b66bba3f1449723bc0b9913b0c7f4d9b28bb6ecfd3f29014734e2734e": { "source": { "path": "cross-region-producer.template.json", "packaging": "file" }, "destinations": { - "539334897376-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-539334897376-us-east-1", - "objectKey": "53d01d6d16adb4ed7a288c6b057ad8862a46c20a65e8d446381b09ca57dc7570.json", + "current_account-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", + "objectKey": "ba6acb4b66bba3f1449723bc0b9913b0c7f4d9b28bb6ecfd3f29014734e2734e.json", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-file-publishing-role-539334897376-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json index 3ed613b7dd170..d000b381a2284 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -11,7 +11,11 @@ { "Ref": "AWS::URLSuffix" }, - "/cdk-hnb659fds-assets-539334897376-us-east-1/db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json" + "/", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" + }, + "/db4b89d277ac97fb3b94206516c7e60648f2e3a4d53793e2e8d073a607b04fdc.json" ] ] } @@ -80,7 +84,18 @@ "Statement": [ { "Effect": "Allow", - "Resource": "arn:aws:ssm:us-east-2:539334897376:parameter/cdk/exports/*", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:us-east-2:", + { + "Ref": "AWS::AccountId" + }, + ":parameter/cdk/exports/*" + ] + ] + }, "Action": [ "ssm:GetParametersByPath", "ssm:PutParameter", @@ -97,7 +112,9 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-539334897376-us-east-1", + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" + }, "S3Key": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip" }, "Timeout": 900, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index 5f91fae1486a7..132cdb6bc26b2 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -11,20 +11,20 @@ }, "cross-region-producer": { "type": "aws:cloudformation:stack", - "environment": "aws://539334897376/us-east-1", + "environment": "aws://unknown-account/us-east-1", "properties": { "templateFile": "cross-region-producer.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-deploy-role-539334897376-us-east-1", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-cfn-exec-role-539334897376-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-539334897376-us-east-1/53d01d6d16adb4ed7a288c6b057ad8862a46c20a65e8d446381b09ca57dc7570.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/ba6acb4b66bba3f1449723bc0b9913b0c7f4d9b28bb6ecfd3f29014734e2734e.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "cross-region-producer.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-lookup-role-539334897376-us-east-1", + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-us-east-1", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } @@ -100,20 +100,20 @@ }, "cross-region-consumer": { "type": "aws:cloudformation:stack", - "environment": "aws://539334897376/us-east-2", + "environment": "aws://unknown-account/us-east-2", "properties": { "templateFile": "cross-region-consumer.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-deploy-role-539334897376-us-east-2", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-cfn-exec-role-539334897376-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-539334897376-us-east-2/3fd74b699f4b03ce328ef95703bb87b2b203a4ea68994f06947d0d1c46130a55.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/073479a1920f44222959fc9231ad62d389ab3fe14dc0a6cdc14a3f1f54fb8dfc.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "cross-region-consumer.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::539334897376:role/cdk-hnb659fds-lookup-role-539334897376-us-east-2", + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-us-east-2", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts index 7df8d77b604e1..0b214aa4cd9a2 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts @@ -18,7 +18,6 @@ class ProducerStack extends Stack { super(scope, id, { env: { region: 'us-east-1', - account: process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, }, }); const nested = new NestedStack(this, 'IntegNested'); @@ -36,7 +35,6 @@ class ConsumerStack extends Stack { ...props, env: { region: 'us-east-2', - account: process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT, }, }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts index a49d0e3b15546..5d32b281a35b2 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts @@ -8,7 +8,6 @@ import { IntegTest } from '@aws-cdk/integ-tests'; import { S3SourceAction, CodeBuildAction } from '../lib'; -const account = process.env.CDK_INTEG_ACCOUNT || process.env.CDK_DEFAULT_ACCOUNT; const app = new App({ treeMetadata: false, }); @@ -16,13 +15,11 @@ app.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); const stack1 = new Stack(app, 'integ-pipeline-producer-stack', { env: { region: 'us-east-1', - account, }, }); const stack2 = new Stack(app, 'integ-pipeline-consumer-stack', { env: { region: 'us-east-2', - account, }, }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js new file mode 100644 index 0000000000000..9df94382cc74e --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + await exports.external.sendHttpRequest(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts new file mode 100644 index 0000000000000..3554dc94d4617 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts @@ -0,0 +1 @@ +export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js new file mode 100644 index 0000000000000..a45e08eb17a6d --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js @@ -0,0 +1,97 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +const SSM_EXPORT_PATH = '/cdk/exports/'; +async function handler(event) { + const props = event.ResourceProperties; + const exports = props.Exports; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Update': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existing = await getExistingParameters(ssm); + const paramsToDelete = returnMissing(existing, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (paramsToDelete.length > 0) { + await ssm.deleteParameters({ + Names: paramsToDelete, + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Delete': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existingParams = await getExistingParameters(ssm); + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(existingParams)), + }).promise(); + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: `${SSM_EXPORT_PATH}${name}`, + Value: value, + Type: 'String', + }).promise(); + })); +} +function returnMissing(a, b) { + const missing = []; + for (const name of Object.keys(a)) { + if (!b.hasOwnProperty(name)) { + missing.push(name); + } + } + return missing; +} +/** + * Get existing exports from SSM parameters + */ +async function getExistingParameters(ssm) { + const existingExports = {}; + function recordParameters(parameters) { + parameters.forEach(param => { + if (param.Name && param.Value) { + existingExports[param.Name] = param.Value; + } + }); + } + const res = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + }).promise(); + recordParameters(res.Parameters ?? []); + while (res.NextToken) { + const nextRes = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + NextToken: res.NextToken, + }).promise(); + recordParameters(nextRes.Parameters ?? []); + res.NextToken = nextRes.NextToken; + } + return existingExports; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUM5QixNQUFNLGVBQWUsR0FBRyxlQUFlLENBQUM7QUFHakMsS0FBSyxVQUFVLE9BQU8sQ0FBQyxLQUFrRDtJQUM5RSxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUM7SUFDdkMsTUFBTSxPQUFPLEdBQXVCLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFFbEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLG9EQUFvRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDakYsTUFBTSxRQUFRLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDbEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDeEQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUksY0FBYyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7b0JBQzdCLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO3dCQUN6QixLQUFLLEVBQUUsY0FBYztxQkFDdEIs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bUIsQ0FBQztZQUM1QyxJQUFJLEVBQUUsR0FBRyxlQUFlLEVBQUU7WUFDMUIsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTO1NBQ3pCLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUM7UUFDM0MsR0FBRyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDO0tBQ25DO0lBQ0QsT0FBTyxlQUFlLENBQUM7QUFDekIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xuY29uc3QgU1NNX0VYUE9SVF9QQVRIID0gJy9jZGsvZXhwb3J0cy8nO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBSZWFkaW5nIGV4aXN0aW5nIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbSk7XG4gICAgICAgIGNvbnN0IHBhcmFtc1RvRGVsZXRlID0gcmV0dXJuTWlzc2luZyhleGlzdGluZywgZXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGlmIChwYXJhbXNUb0RlbGV0ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgYXdhaXQgc3NtLmRlbGV0ZVBhcmFtZXRlcnMoe1xuICAgICAgICAgICAgTmFtZXM6IHBhcmFtc1RvRGVsZXRlLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgUmVhZGluZyBleGlzdGluZyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBjb25zdCBleGlzdGluZ1BhcmFtcyA9IGF3YWl0IGdldEV4aXN0aW5nUGFyYW1ldGVycyhzc20pO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIGFsbCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IEFycmF5LmZyb20oT2JqZWN0LmtleXMoZXhpc3RpbmdQYXJhbXMpKSxcbiAgICAgICAgfSkucHJvbWlzZSgpO1xuICAgICAgICByZXR1cm47XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm47XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc29sZS5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBldmVudDogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBDcmVhdGUgcGFyYW1ldGVycyBmb3IgZXhpc3RpbmcgZXhwb3J0c1xuICovXG5hc3luYyBmdW5jdGlvbiBwdXRQYXJhbWV0ZXJzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgUHJvbWlzZS5hbGwoQXJyYXkuZnJvbShPYmplY3QuZW50cmllcyhwYXJhbWV0ZXJzKSwgKFtuYW1lLCB2YWx1ZV0pID0+IHtcbiAgICByZXR1cm4gc3NtLnB1dFBhcmFtZXRlcih7XG4gICAgICBOYW1lOiBgJHtTU01fRVhQT1JUX1BBVEh9JHtuYW1lfWAsXG4gICAgICBWYWx1ZTogdmFsdWUsXG4gICAgICBUeXBlOiAnU3RyaW5nJyxcbiAgICB9KS5wcm9taXNlKCk7XG4gIH0pKTtcbn1cblxuZnVuY3Rpb24gcmV0dXJuTWlzc2luZyhhOiBDcm9zc1JlZ2lvbkV4cG9ydHMsIGI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IHN0cmluZ1tdIHtcbiAgY29uc3QgbWlzc2luZzogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChjb25zdCBuYW1lIG9mIE9iamVjdC5rZXlzKGEpKSB7XG4gICAgaWYgKCFiLmhhc093blByb3BlcnR5KG5hbWUpKSB7XG4gICAgICBtaXNzaW5nLnB1c2gobmFtZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBtaXNzaW5nO1xufVxuXG4vKipcbiAqIEdldCBleGlzdGluZyBleHBvcnRzIGZyb20gU1NNIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbTogU1NNKTogUHJvbWlzZTxDcm9zc1JlZ2lvbkV4cG9ydHM+IHtcbiAgY29uc3QgZXhpc3RpbmdFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7fTtcbiAgZnVuY3Rpb24gcmVjb3JkUGFyYW1ldGVycyhwYXJhbWV0ZXJzOiBTU00uUGFyYW1ldGVyTGlzdCkge1xuICAgIHBhcmFtZXRlcnMuZm9yRWFjaChwYXJhbSA9PiB7XG4gICAgICBpZiAocGFyYW0uTmFtZSAmJiBwYXJhbS5WYWx1ZSkge1xuICAgICAgICBleGlzdGluZ0V4cG9ydHNbcGFyYW0uTmFtZV0gPSBwYXJhbS5WYWx1ZTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBjb25zdCByZXMgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVyc0J5UGF0aCh7XG4gICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gIH0pLnByb21pc2UoKTtcbiAgcmVjb3JkUGFyYW1ldGVycyhyZXMuUGFyYW1ldGVycyA/PyBbXSk7XG5cbiAgd2hpbGUgKHJlcy5OZXh0VG9rZW4pIHtcbiAgICBjb25zdCBuZXh0UmVzID0gYXdhaXQgc3NtLmdldFBhcmFtZXRlcnNCeVBhdGgoe1xuICAgICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gICAgICBOZXh0VG9rZW46IHJlcy5OZXh0VG9rZW4sXG4gICAgfSkucHJvbWlzZSgpO1xuICAgIHJlY29yZFBhcmFtZXRlcnMobmV4dFJlcy5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICByZXMuTmV4dFRva2VuID0gbmV4dFJlcy5OZXh0VG9rZW47XG4gIH1cbiAgcmV0dXJuIGV4aXN0aW5nRXhwb3J0cztcbn1cblxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts new file mode 100644 index 0000000000000..40c505eb55657 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts @@ -0,0 +1,98 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; +const SSM_EXPORT_PATH = '/cdk/exports/'; +type CrossRegionExports = { [exportName: string]: string }; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const exports: CrossRegionExports = props.Exports; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Update': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existing = await getExistingParameters(ssm); + const paramsToDelete = returnMissing(existing, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (paramsToDelete.length > 0) { + await ssm.deleteParameters({ + Names: paramsToDelete, + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, exports); + return; + case 'Delete': + console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); + const existingParams = await getExistingParameters(ssm); + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(existingParams)), + }).promise(); + return; + default: + return; + } + } catch (e) { + console.error('Error processing event: ', e); + throw e; + } +}; + +/** + * Create parameters for existing exports + */ +async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: `${SSM_EXPORT_PATH}${name}`, + Value: value, + Type: 'String', + }).promise(); + })); +} + +function returnMissing(a: CrossRegionExports, b: CrossRegionExports): string[] { + const missing: string[] = []; + for (const name of Object.keys(a)) { + if (!b.hasOwnProperty(name)) { + missing.push(name); + } + } + return missing; +} + +/** + * Get existing exports from SSM parameters + */ +async function getExistingParameters(ssm: SSM): Promise { + const existingExports: CrossRegionExports = {}; + function recordParameters(parameters: SSM.ParameterList) { + parameters.forEach(param => { + if (param.Name && param.Value) { + existingExports[param.Name] = param.Value; + } + }); + } + const res = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + }).promise(); + recordParameters(res.Parameters ?? []); + + while (res.NextToken) { + const nextRes = await ssm.getParametersByPath({ + Path: `${SSM_EXPORT_PATH}`, + NextToken: res.NextToken, + }).promise(); + recordParameters(nextRes.Parameters ?? []); + res.NextToken = nextRes.NextToken; + } + return existingExports; +} + diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json index b1c6210254e04..f1c403c2ebdea 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json @@ -7,39 +7,25 @@ "packaging": "zip" }, "destinations": { - "12345678-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", + "current_account-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf": { - "source": { - "path": "asset.1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf", - "packaging": "zip" - }, - "destinations": { - "12345678-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip", - "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" - } - } - }, - "a0e7b5e05681c4b759e7ec2d86e5ffa92067757e6a136c7516438235b9ea2a4c": { + "e7cbbedc5cef94e341632c341639a71c30895ee9de9827d4fcb3450a70c7c85c": { "source": { "path": "integ-pipeline-consumer-stack.template.json", "packaging": "file" }, "destinations": { - "12345678-us-east-2": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "a0e7b5e05681c4b759e7ec2d86e5ffa92067757e6a136c7516438235b9ea2a4c.json", + "current_account-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", + "objectKey": "e7cbbedc5cef94e341632c341639a71c30895ee9de9827d4fcb3450a70c7c85c.json", "region": "us-east-2", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json index 21169dcc01b96..64c4d85591491 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json @@ -9,7 +9,18 @@ "Action": "kms:*", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::12345678:root" + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } }, "Resource": "*" } @@ -291,20 +302,10 @@ { "ArtifactStore": { "EncryptionKey": { - "Id": { - "Fn::GetAtt": [ - "ExportsReaderuseast1D746CBDB", - "integ-pipeline-producer-stack:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" - ] - }, + "Id": "{{resolve:ssm:/cdk/exports/integ-pipeline-producer-stack-ReplicationKeyFnGetAttReplicationKeyFCE40BF4ArnFBFE312D}}", "Type": "KMS" }, - "Location": { - "Fn::GetAtt": [ - "ExportsReaderuseast1D746CBDB", - "integ-pipeline-producer-stack:ExportsOutputRefReplicationBucket70D68737E331A47A" - ] - }, + "Location": "{{resolve:ssm:/cdk/exports/integ-pipeline-producer-stack-ReplicationBucketRefReplicationBucket70D6873707044AE1}}", "Type": "S3" }, "Region": "us-east-1" @@ -343,7 +344,18 @@ "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::12345678:root" + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } } } ], @@ -455,7 +467,18 @@ "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::12345678:root" + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } } } ], @@ -602,7 +625,9 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" + }, "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" }, "Timeout": 900, @@ -666,7 +691,11 @@ "Fn::Join": [ "", [ - "arn:aws:logs:us-east-2:12345678:log-group:/aws/codebuild/", + "arn:aws:logs:us-east-2:", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", { "Ref": "Build45A36621" }, @@ -678,7 +707,11 @@ "Fn::Join": [ "", [ - "arn:aws:logs:us-east-2:12345678:log-group:/aws/codebuild/", + "arn:aws:logs:us-east-2:", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", { "Ref": "Build45A36621" } @@ -700,7 +733,11 @@ "Fn::Join": [ "", [ - "arn:aws:codebuild:us-east-2:12345678:report-group/", + "arn:aws:codebuild:us-east-2:", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", { "Ref": "Build45A36621" }, @@ -798,82 +835,6 @@ ] } } - }, - "ExportsReaderuseast1D746CBDB": { - "Type": "Custom::CrossRegionExportReader", - "Properties": { - "ServiceToken": { - "Fn::GetAtt": [ - "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", - "Arn" - ] - }, - "Region": "us-east-1", - "RefreshToken": "98880eb177b602d9fd70c7d737108d84" - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ] - }, - "ManagedPolicyArns": [ - { - "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - } - ], - "Policies": [ - { - "PolicyName": "Inline", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Resource": "*", - "Action": [ - "cloudformation:ListExports" - ] - } - ] - } - } - ] - } - }, - "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", - "S3Key": "1ee31a833482538af5b3690ccbb84c41ac4c66f7892bcb8802b0695553bc4ccf.zip" - }, - "Timeout": 900, - "MemorySize": 128, - "Handler": "__entrypoint__.handler", - "Role": { - "Fn::GetAtt": [ - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", - "Arn" - ] - }, - "Runtime": "nodejs14.x" - }, - "DependsOn": [ - "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" - ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json index 5339f36592f7c..fdf64dc1b8dd8 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json @@ -7,25 +7,39 @@ "packaging": "zip" }, "destinations": { - "12345678-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "current_account-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "cd81916f97f443b595350f121a34cc07641fedfc3e39d80b64772fbf5e7a48be": { + "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989": { + "source": { + "path": "asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989", + "packaging": "zip" + }, + "destinations": { + "current_account-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", + "objectKey": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" + } + } + }, + "f6b8517d223bcb31f6544cef17261ee6be38b83b8bb0e212b76d81a03df462a5": { "source": { "path": "integ-pipeline-producer-stack.template.json", "packaging": "file" }, "destinations": { - "12345678-us-east-1": { - "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", - "objectKey": "cd81916f97f443b595350f121a34cc07641fedfc3e39d80b64772fbf5e7a48be.json", + "current_account-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", + "objectKey": "f6b8517d223bcb31f6544cef17261ee6be38b83b8bb0e212b76d81a03df462a5.json", "region": "us-east-1", - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json index 949be2203e697..6330564efa374 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json @@ -9,7 +9,18 @@ "Action": "kms:*", "Effect": "Allow", "Principal": { - "AWS": "arn:aws:iam::12345678:root" + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } }, "Resource": "*" } @@ -144,7 +155,9 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-1", + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" + }, "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" }, "Timeout": 900, @@ -173,27 +186,107 @@ "DependsOn": [ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" ] - } - }, - "Outputs": { - "ExportsOutputRefReplicationBucket70D68737E331A47A": { - "Value": { - "Ref": "ReplicationBucket70D68737" + }, + "ExportsWriteruseast2828FA26B": { + "Type": "Custom::CrossRegionExportWriter", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A", + "Arn" + ] + }, + "Region": "us-east-2", + "Exports": { + "integ-pipeline-producer-stack-ReplicationBucketRefReplicationBucket70D6873707044AE1": { + "Ref": "ReplicationBucket70D68737" + }, + "integ-pipeline-producer-stack-ReplicationKeyFnGetAttReplicationKeyFCE40BF4ArnFBFE312D": { + "Fn::GetAtt": [ + "ReplicationKeyFCE40BF4", + "Arn" + ] + } + } }, - "Export": { - "Name": "integ-pipeline-producer-stack:ExportsOutputRefReplicationBucket70D68737E331A47A" - } + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, - "ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6": { - "Value": { - "Fn::GetAtt": [ - "ReplicationKeyFCE40BF4", - "Arn" + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:us-east-2:", + { + "Ref": "AWS::AccountId" + }, + ":parameter/cdk/exports/*" + ] + ] + }, + "Action": [ + "ssm:GetParametersByPath", + "ssm:PutParameter", + "ssm:DeleteParameters" + ] + } + ] + } + } ] - }, - "Export": { - "Name": "integ-pipeline-producer-stack:ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" } + }, + "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" + }, + "S3Key": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json index 7f4e150473faf..6d8e7deb80d6d 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json @@ -11,20 +11,20 @@ }, "integ-pipeline-producer-stack": { "type": "aws:cloudformation:stack", - "environment": "aws://12345678/us-east-1", + "environment": "aws://unknown-account/us-east-1", "properties": { "templateFile": "integ-pipeline-producer-stack.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/cd81916f97f443b595350f121a34cc07641fedfc3e39d80b64772fbf5e7a48be.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/f6b8517d223bcb31f6544cef17261ee6be38b83b8bb0e212b76d81a03df462a5.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "integ-pipeline-producer-stack.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-1", + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-us-east-1", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } @@ -69,16 +69,22 @@ "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" } ], - "/integ-pipeline-producer-stack/Exports/Output{\"Ref\":\"ReplicationBucket70D68737\"}": [ + "/integ-pipeline-producer-stack/ExportsWriteruseast2828FA26B/Default/Default": [ { "type": "aws:cdk:logicalId", - "data": "ExportsOutputRefReplicationBucket70D68737E331A47A" + "data": "ExportsWriteruseast2828FA26B" } ], - "/integ-pipeline-producer-stack/Exports/Output{\"Fn::GetAtt\":[\"ReplicationKeyFCE40BF4\",\"Arn\"]}": [ + "/integ-pipeline-producer-stack/Custom::CrossRegionExportWriterCustomResourceProvider/Role": [ { "type": "aws:cdk:logicalId", - "data": "ExportsOutputFnGetAttReplicationKeyFCE40BF4Arn53D389D6" + "data": "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1" + } + ], + "/integ-pipeline-producer-stack/Custom::CrossRegionExportWriterCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A" } ], "/integ-pipeline-producer-stack/BootstrapVersion": [ @@ -106,20 +112,20 @@ }, "integ-pipeline-consumer-stack": { "type": "aws:cloudformation:stack", - "environment": "aws://12345678/us-east-2", + "environment": "aws://unknown-account/us-east-2", "properties": { "templateFile": "integ-pipeline-consumer-stack.template.json", "validateOnSynth": false, - "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", - "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/a0e7b5e05681c4b759e7ec2d86e5ffa92067757e6a136c7516438235b9ea2a4c.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/e7cbbedc5cef94e341632c341639a71c30895ee9de9827d4fcb3450a70c7c85c.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ "integ-pipeline-consumer-stack.assets" ], "lookupRole": { - "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-2", + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-us-east-2", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } @@ -243,24 +249,6 @@ "data": "Build45A36621" } ], - "/integ-pipeline-consumer-stack/ExportsReaderuseast1D746CBDB/Default/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "ExportsReaderuseast1D746CBDB" - } - ], - "/integ-pipeline-consumer-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" - } - ], - "/integ-pipeline-consumer-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ - { - "type": "aws:cdk:logicalId", - "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" - } - ], "/integ-pipeline-consumer-stack/BootstrapVersion": [ { "type": "aws:cdk:logicalId", From a4ac262d94fb52c1f3a6fbb8da17d093831385f9 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 23 Sep 2022 19:21:12 +0000 Subject: [PATCH 09/30] renaming --- ...-provider.ts => export-writer-provider.ts} | 0 .../get-cfn-exports-handler/index.ts | 26 --- packages/@aws-cdk/core/lib/private/refs.ts | 2 +- ...test.ts => export-writer-provider.test.ts} | 4 +- .../get-cfn-exports-handler.test.ts | 152 ------------------ 5 files changed, 3 insertions(+), 181 deletions(-) rename packages/@aws-cdk/core/lib/custom-resource-provider/{export-reader-provider.ts => export-writer-provider.ts} (100%) delete mode 100644 packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts rename packages/@aws-cdk/core/test/custom-resource-provider/{export-reader-provider.test.ts => export-writer-provider.test.ts} (98%) delete mode 100644 packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts similarity index 100% rename from packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts rename to packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts deleted file mode 100644 index bde33a8a1913b..0000000000000 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/get-cfn-exports-handler/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -import { CloudFormation } from 'aws-sdk'; - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - console.info(`Reading CFN Stack exports in region ${props.Region}`); - - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - const cfn = new CloudFormation({ region: props.Region }); - const exports = await cfn.listExports().promise(); - const values = exports.Exports?.reduce((prev: { [key: string]: string }, curr) => { - if (curr.Name && curr.Value) { - prev[curr.Name] = curr.Value; - } - return prev; - }, {}); - console.info('Response: %j', values); - return { - Data: { - ...values, - }, - }; - } - return; -}; diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index ad149b9c7c965..92df6179f382d 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -7,7 +7,7 @@ import { IConstruct } from 'constructs'; import { CfnElement } from '../cfn-element'; import { CfnOutput } from '../cfn-output'; import { CfnParameter } from '../cfn-parameter'; -import { ExportWriter } from '../custom-resource-provider/export-reader-provider'; +import { ExportWriter } from '../custom-resource-provider/export-writer-provider'; import { FeatureFlags } from '../feature-flags'; import { Names } from '../names'; import { Reference } from '../reference'; diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts similarity index 98% rename from packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts rename to packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index eae044bf634b6..4e6afdc2f0d45 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-reader-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -1,9 +1,9 @@ import { App, Stack, AssetStaging, CfnResource } from '../../lib'; -import { ExportWriter } from '../../lib/custom-resource-provider/export-reader-provider'; +import { ExportWriter } from '../../lib/custom-resource-provider/export-writer-provider'; import { toCloudFormation } from '../util'; -describe('export reader provider', () => { +describe('export writer provider', () => { test('basic configuration', () => { // GIVEN const app = new App(); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts deleted file mode 100644 index 0e8b6b9519358..0000000000000 --- a/packages/@aws-cdk/core/test/custom-resource-provider/get-cfn-exports-handler.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { handler } from '../../lib/custom-resource-provider/get-cfn-exports-handler'; - -const mockListExports = jest.fn(); -jest.mock('aws-sdk', () => { - return { - CloudFormation: jest.fn(() => { - return { - listExports: jest.fn(() => { - return { - promise: () => mockListExports(), - }; - }), - }; - }), - }; -}); - -describe('get-cfn-exports entrypoint', () => { - beforeEach(() => { - mockListExports.mockReset(); - jest.spyOn(console, 'info').mockImplementation(() => {}); - }); - afterEach(() => { - jest.restoreAllMocks(); - }); - test('Create event', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Create', - ResourceProperties: { - ServiceToken: '', - Region: 'us-east-1', - }, - }); - mockListExports.mockImplementation(() => { - return { - Exports: [{ - Name: 'ExportName', - Value: 'ExportValue', - }], - }; - }); - - // WHEN - const response = await handler(event); - - // THEN - expect(mockListExports).toHaveBeenCalledTimes(1); - expect(response).toEqual({ - Data: { - ExportName: 'ExportValue', - }, - }); - }); - - test('Update event', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Update', - ResourceProperties: { - ServiceToken: '', - Region: 'us-east-1', - }, - }); - mockListExports.mockImplementation(() => { - return { - Exports: [{ - Name: 'ExportName', - Value: 'ExportValue', - }], - }; - }); - - // WHEN - const response = await handler(event); - - // THEN - expect(mockListExports).toHaveBeenCalledTimes(1); - expect(response).toEqual({ - Data: { - ExportName: 'ExportValue', - }, - }); - }); - - test('Delete event', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Delete', - ResourceProperties: { - ServiceToken: '', - Region: 'us-east-1', - }, - }); - mockListExports.mockImplementation(() => { - return { - Exports: [{ - Name: 'ExportName', - Value: 'ExportValue', - }], - }; - }); - - // WHEN - const response = await handler(event); - - // THEN - expect(mockListExports).toHaveBeenCalledTimes(0); - expect(response).toBeUndefined; - }); - - test('no exports', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Create', - ResourceProperties: { - ServiceToken: '', - Region: 'us-east-1', - }, - }); - mockListExports.mockImplementationOnce(() => { - return { - Exports: [], - }; - }); - - // WHEN - const response = await handler(event); - - // THEN - expect(mockListExports).toHaveBeenCalledTimes(1); - expect(response).toEqual({ - Data: {}, - }); - }); -}); - -function makeEvent(req: Partial): AWSLambda.CloudFormationCustomResourceEvent { - return { - LogicalResourceId: '', - RequestId: '', - ResourceType: '', - ResponseURL: '', - ServiceToken: '', - StackId: '', - ResourceProperties: { - ServiceToken: '', - ...req.ResourceProperties, - }, - ...req, - } as any; -} From 3f99d97092bce5771024b00fd6648e528a902c4d Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 23 Sep 2022 19:27:09 +0000 Subject: [PATCH 10/30] updating docs --- packages/@aws-cdk/core/README.md | 9 +++++---- packages/@aws-cdk/cx-api/README.md | 2 +- packages/@aws-cdk/cx-api/lib/features.ts | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 22c31cd5efee4..cbb622ec25fed 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -187,10 +187,11 @@ new cloudfront.Distribution(stack2, 'Distribution', { ``` When the AWS CDK determines that the resource is in a different stack _and_ is in a different -region, it automatically synthesizes AWS -CloudFormation [Exports](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html) -in the producing stack. In order to "import" the exports into the consuming stack a CloudFormation -Custom Resource is created which "imports" the values from the cross region stack. +region, it will "export" the value by creating a custom resource in the producing stack which +creates SSM Parameters in the consuming region for each exported value. The parameters will be +created with the name '/cdk/exports/${export-name}'. +In order to "import" the exports into the consuming stack a [SSM Dynamic reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-ssm) +is used to reference the SSM parameter which was created. ### Removing automatic cross-stack references diff --git a/packages/@aws-cdk/cx-api/README.md b/packages/@aws-cdk/cx-api/README.md index bf28b153cf3a8..8ee777c3832b8 100644 --- a/packages/@aws-cdk/cx-api/README.md +++ b/packages/@aws-cdk/cx-api/README.md @@ -109,7 +109,7 @@ becomes: Enable this feature flag to allow native cross region stack references. This will use a CloudFormation -Custom Resource to perform the cross region lookup. +Custom Resource to perform the cross region export. This is disabled by default. diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index 3762900f72cfe..8c96dff076541 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -354,7 +354,7 @@ export const ENABLE_PARTITION_LITERALS = '@aws-cdk/core:enablePartitionLiterals' /** * Enable this feature flag to allow native cross region stack references. This will use a CloudFormation - * Custom Resource to perform the cross region lookup. + * Custom Resource to perform the cross region export. * * @default false */ From 13b831b8d0474fd05640fdac1ce6d35402491e63 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 23 Sep 2022 19:35:55 +0000 Subject: [PATCH 11/30] fixing build --- .../cross-region-ssm-writer-handler.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts index 72c114fda75ad..0573e4cbe305d 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -1,5 +1,5 @@ import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-writer-handler'; -import { SSM_EXPORT_PATH } from '../../lib/custom-resource-provider/export-reader-provider'; +import { SSM_EXPORT_PATH } from '../../lib/custom-resource-provider/export-writer-provider'; let mockPutParameter: jest.Mock ; let mockGetParametersByPath: jest.Mock; From 38c4226135efb23558c677e3f1fe39945998bf2b Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 23 Sep 2022 20:04:04 +0000 Subject: [PATCH 12/30] adding stack name to ssm path prefix --- .../cross-region-ssm-writer-handler/index.ts | 14 +++--- .../export-writer-provider.ts | 12 ++--- packages/@aws-cdk/core/lib/private/refs.ts | 6 +-- .../core/test/cross-environment-token.test.ts | 5 ++- .../cross-region-ssm-writer-handler.test.ts | 44 ++++++++++++------- .../export-writer-provider.test.ts | 7 +-- .../@aws-cdk/core/test/nested-stack.test.ts | 5 ++- packages/@aws-cdk/core/test/stack.test.ts | 33 ++++++++------ 8 files changed, 73 insertions(+), 53 deletions(-) diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts index 40c505eb55657..819ffb3fdd312 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts @@ -1,11 +1,11 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; -const SSM_EXPORT_PATH = '/cdk/exports/'; type CrossRegionExports = { [exportName: string]: string }; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { const props = event.ResourceProperties; +const ssmPathPrefix = `/cdk/exports/${props.StackName}/`; const exports: CrossRegionExports = props.Exports; const ssm = new SSM({ region: props.Region }); @@ -17,7 +17,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent return; case 'Update': console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existing = await getExistingParameters(ssm); + const existing = await getExistingParameters(ssm, ssmPathPrefix); const paramsToDelete = returnMissing(existing, exports); console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); if (paramsToDelete.length > 0) { @@ -30,7 +30,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent return; case 'Delete': console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existingParams = await getExistingParameters(ssm); + const existingParams = await getExistingParameters(ssm, ssmPathPrefix); console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); await ssm.deleteParameters({ Names: Array.from(Object.keys(existingParams)), @@ -51,7 +51,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { return ssm.putParameter({ - Name: `${SSM_EXPORT_PATH}${name}`, + Name: name, Value: value, Type: 'String', }).promise(); @@ -71,7 +71,7 @@ function returnMissing(a: CrossRegionExports, b: CrossRegionExports): string[] { /** * Get existing exports from SSM parameters */ -async function getExistingParameters(ssm: SSM): Promise { +async function getExistingParameters(ssm: SSM, ssmPathPrefix: string): Promise { const existingExports: CrossRegionExports = {}; function recordParameters(parameters: SSM.ParameterList) { parameters.forEach(param => { @@ -81,13 +81,13 @@ async function getExistingParameters(ssm: SSM): Promise { }); } const res = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, + Path: ssmPathPrefix, }).promise(); recordParameters(res.Parameters ?? []); while (res.NextToken) { const nextRes = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, + Path: ssmPathPrefix, NextToken: res.NextToken, }).promise(); recordParameters(nextRes.Parameters ?? []); diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts index 681ae1bd3c2b3..3d151b653a86b 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts @@ -9,7 +9,7 @@ import { Stack } from '../stack'; import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; type CrossRegionExports = { [exportName: string]: string }; -export const SSM_EXPORT_PATH = 'cdk/exports/'; +export const SSM_EXPORT_PATH_PREFIX = 'cdk/exports/'; /** * Properties for an ExportReader @@ -60,7 +60,7 @@ export class ExportWriter extends Construct { service: 'ssm', resource: 'parameter', region, - resourceName: `${SSM_EXPORT_PATH}*`, + resourceName: `${SSM_EXPORT_PATH_PREFIX}${Stack.of(this).stackName}/*`, }), Action: [ 'ssm:GetParametersByPath', @@ -75,10 +75,10 @@ export class ExportWriter extends Construct { serviceToken, properties: { Region: region, + StackName: stack.stackName, Exports: Lazy.any({ produce: () => this._references }), }, }); - } /** @@ -93,7 +93,9 @@ export class ExportWriter extends Construct { * @returns a dynamic reference to an ssm parameter */ public exportValue(exportName: string, reference: Reference): Intrinsic { - this._references[exportName] = Stack.of(this).resolve(reference.toString()); - return new CfnDynamicReference(CfnDynamicReferenceService.SSM, `/${SSM_EXPORT_PATH}${exportName}`); + const stack = Stack.of(this); + const parameterName = `/${SSM_EXPORT_PATH_PREFIX}${stack.stackName}/${exportName}`; + this._references[parameterName] = stack.resolve(reference.toString()); + return new CfnDynamicReference(CfnDynamicReferenceService.SSM, parameterName); } } diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index 92df6179f382d..b87c7b73e5bf6 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -234,13 +234,13 @@ function generateExportName(stack: Stack, reference: Reference, id: string): str .map(c => c.node.id), id, ]; - const prefix = stack.stackName ? stack.stackName + '-' : ''; + const prefix = stack.stackName; const localPart = makeUniqueId(components); // max name length for a system manager parameter is 1011 characters // including the arn, i.e. - // arn:aws:ssm:us-east-2:111122223333:parameter/cdk/exports/${name} + // arn:aws:ssm:us-east-2:111122223333:parameter/cdk/exports/${stackName}/${name} const maxLength = 900; - return prefix + localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); + return localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); } export function getExportable(stack: Stack, reference: Reference): Reference { diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index cdbb4b4e4085f..c0744570b6221 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -220,11 +220,12 @@ describe('cross environment', () => { 'DeletionPolicy': 'Delete', 'Properties': { 'Exports': { - 'Stack1-MyResourceRefMyResource6073B41F992B761C': { + '/cdk/exports/Stack1/MyResourceRefMyResource6073B41F992B761C': { 'Ref': 'MyResource6073B41F', }, }, 'Region': 'bermuda-triangle-42', + 'StackName': 'Stack1', 'ServiceToken': { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -238,7 +239,7 @@ describe('cross environment', () => { }); expect(template2?.Outputs).toEqual({ 'Output': { - 'Value': '{{resolve:ssm:/cdk/exports/Stack1-MyResourceRefMyResource6073B41F992B761C}}', + 'Value': '{{resolve:ssm:/cdk/exports/Stack1/MyResourceRefMyResource6073B41F992B761C}}', }, }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts index 0573e4cbe305d..3967570ac108e 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -1,5 +1,5 @@ import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-writer-handler'; -import { SSM_EXPORT_PATH } from '../../lib/custom-resource-provider/export-writer-provider'; +import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/export-writer-provider'; let mockPutParameter: jest.Mock ; let mockGetParametersByPath: jest.Mock; @@ -49,8 +49,9 @@ describe('cross-region-ssm-writer entrypoint', () => { ResourceProperties: { ServiceToken: '', Region: 'us-east-1', + StackName: 'MyStack', Exports: { - MyExport: 'Value', + '/cdk/exports/MyStack/MyExport': 'Value', }, }, }); @@ -60,8 +61,9 @@ describe('cross-region-ssm-writer entrypoint', () => { // THEN expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH}MyExport`, + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, Value: 'Value', + Type: 'String', }); expect(mockPutParameter).toHaveBeenCalledTimes(1); expect(mockDeleteParameters).toHaveBeenCalledTimes(0); @@ -75,8 +77,9 @@ describe('cross-region-ssm-writer entrypoint', () => { ResourceProperties: { ServiceToken: '', Region: 'us-east-1', + StackName: 'MyStack', Exports: { - MyExport: 'Value', + '/cdk/exports/MyStack/MyExport': 'Value', }, }, }); @@ -85,7 +88,7 @@ describe('cross-region-ssm-writer entrypoint', () => { mockGetParametersByPath.mockImplementation(() => { return { Parameters: [{ - Name: 'MyExport', + Name: '/cdk/exports/MyStack/MyExport', Value: 'Value', }], }; @@ -94,8 +97,9 @@ describe('cross-region-ssm-writer entrypoint', () => { // THEN expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH}MyExport`, + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, Value: 'Value', + Type: 'String', }); expect(mockPutParameter).toHaveBeenCalledTimes(1); expect(mockDeleteParameters).toHaveBeenCalledTimes(0); @@ -109,9 +113,10 @@ describe('cross-region-ssm-writer entrypoint', () => { ResourceProperties: { ServiceToken: '', Region: 'us-east-1', + StackName: 'MyStack', Exports: { - MyExport: 'Value', - MyOtherExport: 'MyOtherValue', + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/MyOtherExport': 'MyOtherValue', }, }, }); @@ -121,14 +126,14 @@ describe('cross-region-ssm-writer entrypoint', () => { return { NextToken: 'abc', Parameters: [{ - Name: 'MyExport', + Name: '/cdk/exports/MyStack/MyExport', Value: 'Value', }], }; }).mockImplementation(() => { return { Parameters: [{ - Name: 'MyOtherExport', + Name: '/cdk/exports/MyStack/MyOtherExport', Value: 'MyOtherValue', }], }; @@ -137,8 +142,9 @@ describe('cross-region-ssm-writer entrypoint', () => { // THEN expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH}MyExport`, + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, Value: 'Value', + Type: 'String', }); expect(mockPutParameter).toHaveBeenCalledTimes(2); expect(mockDeleteParameters).toHaveBeenCalledTimes(0); @@ -152,9 +158,10 @@ describe('cross-region-ssm-writer entrypoint', () => { ResourceProperties: { ServiceToken: '', Region: 'us-east-1', + StackName: 'MyStack', Exports: { - MyExport: 'Value', - MyOtherExport: 'MyOtherValue', + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/MyOtherExport': 'MyOtherValue', }, }, }); @@ -172,12 +179,14 @@ describe('cross-region-ssm-writer entrypoint', () => { // THEN expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH}MyExport`, + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, Value: 'Value', + Type: 'String', }); expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH}MyOtherExport`, + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyOtherExport`, Value: 'MyOtherValue', + Type: 'String', }); expect(mockDeleteParameters).toHaveBeenCalledWith({ Names: ['RemovedExport'], @@ -193,9 +202,10 @@ describe('cross-region-ssm-writer entrypoint', () => { ResourceProperties: { ServiceToken: '', Region: 'us-east-1', + StackName: 'MyStack', Exports: { - MyExport: 'Value', - MyOtherExport: 'MyOtherValue', + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/MyOtherExport': 'MyOtherValue', }, }, }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index 4e6afdc2f0d45..be1255298e535 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -23,7 +23,7 @@ describe('export writer provider', () => { const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; const assetHash = staging.assetHash; - expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/MyResourceName}}'); + expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/Default/MyResourceName}}'); expect(cfn).toEqual({ Resources: { MyResource: { @@ -67,7 +67,7 @@ describe('export writer provider', () => { { Ref: 'AWS::AccountId', }, - ':parameter/cdk/exports/*', + ':parameter/cdk/exports/Default/*', ], ], }, @@ -96,13 +96,14 @@ describe('export writer provider', () => { ], }, Exports: { - MyResourceName: { + '/cdk/exports/Default/MyResourceName': { 'Fn::GetAtt': [ 'MyResource', 'arn', ], }, }, + StackName: 'Default', }, Type: 'Custom::CrossRegionExportWriter', UpdateReplacePolicy: 'Delete', diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 8fa22c3110203..7df41b8e5cee9 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -68,7 +68,7 @@ describe('nested-stack', () => { expect(nestedTemplate2).toMatchObject({ Outputs: { Output: { - Value: '{{resolve:ssm:/cdk/exports/Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D}}', + Value: '{{resolve:ssm:/cdk/exports/Stack1/MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D}}', }, }, }); @@ -87,13 +87,14 @@ describe('nested-stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - 'Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D': { + '/cdk/exports/Stack1/MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D': { 'Fn::GetAtt': [ 'MyNestedStackNestedStackMyNestedStackNestedStackResource9C617903', 'Outputs.Stack1MyNestedStackMyResourceEDA18296Ref', ], }, }, + StackName: 'Stack1', Region: 'bermuda-triangle-42', ServiceToken: { 'Fn::GetAtt': [ diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index e6872ba562900..ff65e35891d98 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -495,13 +495,14 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - 'Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', ], }, }, + StackName: 'Stack1', Region: 'us-east-2', ServiceToken: { 'Fn::GetAtt': [ @@ -519,7 +520,7 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Name: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', }, }, }, @@ -584,9 +585,9 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', - Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', + Name: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Other: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', }, }, }, @@ -601,7 +602,7 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - 'Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + '/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other2', @@ -609,6 +610,7 @@ describe('stack', () => { }, }, Region: 'us-east-2', + StackName: 'Stack3', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -629,13 +631,13 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - 'Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', ], }, - 'Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other', @@ -643,6 +645,7 @@ describe('stack', () => { }, }, Region: 'us-east-2', + StackName: 'Stack1', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -697,9 +700,9 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', - Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', + Name: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Other: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', }, }, }, @@ -714,7 +717,7 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - 'Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + '/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other2', @@ -722,6 +725,7 @@ describe('stack', () => { }, }, Region: 'us-east-2', + StackName: 'Stack3', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -742,13 +746,13 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - 'Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', ], }, - 'Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other', @@ -756,6 +760,7 @@ describe('stack', () => { }, }, Region: 'us-east-2', + StackName: 'Stack1', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', From 56b4fe6f3aa7ea104098bdbd91ce0fc5062c68bd Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 26 Sep 2022 18:50:37 +0000 Subject: [PATCH 13/30] refactoring provider to use OldResourceProperties --- .../cross-region-ssm-writer-handler/index.ts | 73 ++++------ .../export-writer-provider.ts | 7 +- packages/@aws-cdk/core/lib/private/refs.ts | 4 +- .../core/test/cross-environment-token.test.ts | 4 +- .../cross-region-ssm-writer-handler.test.ts | 137 ++++++++++-------- .../export-writer-provider.test.ts | 9 +- .../@aws-cdk/core/test/nested-stack.test.ts | 4 +- packages/@aws-cdk/core/test/stack.test.ts | 28 ++-- 8 files changed, 133 insertions(+), 133 deletions(-) diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts index 819ffb3fdd312..650f970a48b9d 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts @@ -5,7 +5,6 @@ type CrossRegionExports = { [exportName: string]: string }; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { const props = event.ResourceProperties; -const ssmPathPrefix = `/cdk/exports/${props.StackName}/`; const exports: CrossRegionExports = props.Exports; const ssm = new SSM({ region: props.Region }); @@ -13,27 +12,28 @@ const ssmPathPrefix = `/cdk/exports/${props.StackName}/`; switch (event.RequestType) { case 'Create': console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyExistingParameters(ssm, exports); await putParameters(ssm, exports); return; case 'Update': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existing = await getExistingParameters(ssm, ssmPathPrefix); - const paramsToDelete = returnMissing(existing, exports); + const oldProps = event.OldResourceProperties; + const oldExports: CrossRegionExports = oldProps.Exports; + const newExports = filter(exports, oldExports); + await throwIfAnyExistingParameters(ssm, newExports); + const paramsToDelete = filter(oldExports, exports); console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (paramsToDelete.length > 0) { + if (Object.keys(paramsToDelete).length > 0) { await ssm.deleteParameters({ - Names: paramsToDelete, + Names: Object.keys(paramsToDelete), }).promise(); } console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); + await putParameters(ssm, newExports); return; case 'Delete': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existingParams = await getExistingParameters(ssm, ssmPathPrefix); console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); await ssm.deleteParameters({ - Names: Array.from(Object.keys(existingParams)), + Names: Array.from(Object.keys(exports)), }).promise(); return; default: @@ -58,41 +58,30 @@ async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise< })); } -function returnMissing(a: CrossRegionExports, b: CrossRegionExports): string[] { - const missing: string[] = []; - for (const name of Object.keys(a)) { - if (!b.hasOwnProperty(name)) { - missing.push(name); - } - } - return missing; -} - /** - * Get existing exports from SSM parameters + * Query for existing parameters */ -async function getExistingParameters(ssm: SSM, ssmPathPrefix: string): Promise { - const existingExports: CrossRegionExports = {}; - function recordParameters(parameters: SSM.ParameterList) { - parameters.forEach(param => { - if (param.Name && param.Value) { - existingExports[param.Name] = param.Value; - } - }); - } - const res = await ssm.getParametersByPath({ - Path: ssmPathPrefix, +async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + const result = await ssm.getParameters({ + Names: Object.keys(parameters), }).promise(); - recordParameters(res.Parameters ?? []); - - while (res.NextToken) { - const nextRes = await ssm.getParametersByPath({ - Path: ssmPathPrefix, - NextToken: res.NextToken, - }).promise(); - recordParameters(nextRes.Parameters ?? []); - res.NextToken = nextRes.NextToken; + if ((result.Parameters ?? []).length > 0) { + const existing = result.Parameters!.map(param => param.Name); + throw new Error(`Exports already exist: \n${existing.join('\n')}`); } - return existingExports; } +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filter(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { + return Object.keys(source) + .filter(key => !filter.hasOwnProperty(key)) + .reduce((acc: CrossRegionExports, curr: string) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts index 3d151b653a86b..d6ef0d36d36a0 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts @@ -60,10 +60,10 @@ export class ExportWriter extends Construct { service: 'ssm', resource: 'parameter', region, - resourceName: `${SSM_EXPORT_PATH_PREFIX}${Stack.of(this).stackName}/*`, + resourceName: `${SSM_EXPORT_PATH_PREFIX}*`, }), Action: [ - 'ssm:GetParametersByPath', + 'ssm:GetParameters', 'ssm:PutParameter', 'ssm:DeleteParameters', ], @@ -75,7 +75,6 @@ export class ExportWriter extends Construct { serviceToken, properties: { Region: region, - StackName: stack.stackName, Exports: Lazy.any({ produce: () => this._references }), }, }); @@ -94,7 +93,7 @@ export class ExportWriter extends Construct { */ public exportValue(exportName: string, reference: Reference): Intrinsic { const stack = Stack.of(this); - const parameterName = `/${SSM_EXPORT_PATH_PREFIX}${stack.stackName}/${exportName}`; + const parameterName = `/${SSM_EXPORT_PATH_PREFIX}${exportName}`; this._references[parameterName] = stack.resolve(reference.toString()); return new CfnDynamicReference(CfnDynamicReferenceService.SSM, parameterName); } diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index b87c7b73e5bf6..ce3c7e6275285 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -234,13 +234,13 @@ function generateExportName(stack: Stack, reference: Reference, id: string): str .map(c => c.node.id), id, ]; - const prefix = stack.stackName; + const prefix = stack.stackName + '-'; const localPart = makeUniqueId(components); // max name length for a system manager parameter is 1011 characters // including the arn, i.e. // arn:aws:ssm:us-east-2:111122223333:parameter/cdk/exports/${stackName}/${name} const maxLength = 900; - return localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); + return prefix + localPart.slice(Math.max(0, localPart.length - maxLength + prefix.length)); } export function getExportable(stack: Stack, reference: Reference): Reference { diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index c0744570b6221..56b406855f051 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -220,7 +220,7 @@ describe('cross environment', () => { 'DeletionPolicy': 'Delete', 'Properties': { 'Exports': { - '/cdk/exports/Stack1/MyResourceRefMyResource6073B41F992B761C': { + '/cdk/exports/Stack1-MyResourceRefMyResource6073B41F992B761C': { 'Ref': 'MyResource6073B41F', }, }, @@ -239,7 +239,7 @@ describe('cross environment', () => { }); expect(template2?.Outputs).toEqual({ 'Output': { - 'Value': '{{resolve:ssm:/cdk/exports/Stack1/MyResourceRefMyResource6073B41F992B761C}}', + 'Value': '{{resolve:ssm:/cdk/exports/Stack1-MyResourceRefMyResource6073B41F992B761C}}', }, }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts index 3967570ac108e..c805d71038b76 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -2,7 +2,7 @@ import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-wri import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/export-writer-provider'; let mockPutParameter: jest.Mock ; -let mockGetParametersByPath: jest.Mock; +let mockGetParameters: jest.Mock; let mockDeleteParameters: jest.Mock; jest.mock('aws-sdk', () => { return { @@ -18,9 +18,9 @@ jest.mock('aws-sdk', () => { promise: () => mockDeleteParameters(params), }; }), - getParametersByPath: jest.fn((params) => { + getParameters: jest.fn((params) => { return { - promise: () => mockGetParametersByPath(params), + promise: () => mockGetParameters(params), }; }), }; @@ -31,18 +31,21 @@ beforeEach(() => { jest.spyOn(console, 'info').mockImplementation(() => {}); jest.spyOn(console, 'error').mockImplementation(() => {}); mockPutParameter = jest.fn(); - mockGetParametersByPath = jest.fn(); + mockGetParameters = jest.fn(); mockDeleteParameters = jest.fn(); mockPutParameter.mockImplementation(() => { return {}; }); + mockGetParameters.mockImplementation(() => { + return {}; + }) }); afterEach(() => { jest.restoreAllMocks(); }); -describe('cross-region-ssm-writer entrypoint', () => { - test('Create event', async () => { +describe('cross-region-ssm-writer throws', () => { + test('create throws if params already exist', async () => { // GIVEN const event = makeEvent({ RequestType: 'Create', @@ -57,42 +60,71 @@ describe('cross-region-ssm-writer entrypoint', () => { }); // WHEN - await handler(event); + mockGetParameters.mockImplementation(() => { + return { + Parameters: [{ + Name: '/cdk/exports/MyStack/MyExport', + }], + }; + }); // THEN - expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, - Value: 'Value', - Type: 'String', - }); - expect(mockPutParameter).toHaveBeenCalledTimes(1); - expect(mockDeleteParameters).toHaveBeenCalledTimes(0); - expect(mockGetParametersByPath).toHaveBeenCalledTimes(0); + await expect(handler(event)).rejects.toThrow(/Exports already exist/); }); - test('Update event', async () => { + test('update throws if params already exist', async () => { // GIVEN const event = makeEvent({ RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, + }, ResourceProperties: { ServiceToken: '', Region: 'us-east-1', StackName: 'MyStack', Exports: { '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'Value', }, }, }); // WHEN - mockGetParametersByPath.mockImplementation(() => { + mockGetParameters.mockImplementation(() => { return { Parameters: [{ - Name: '/cdk/exports/MyStack/MyExport', - Value: 'Value', + Name: '/cdk/exports/MyStack/AlreadyExists', }], }; }); + + // THEN + await expect(handler(event)).rejects.toThrow(/Exports already exist/); + }); +}) + +describe('cross-region-ssm-writer entrypoint', () => { + test('Create event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, + }, + }); + + // WHEN await handler(event); // THEN @@ -103,41 +135,32 @@ describe('cross-region-ssm-writer entrypoint', () => { }); expect(mockPutParameter).toHaveBeenCalledTimes(1); expect(mockDeleteParameters).toHaveBeenCalledTimes(0); - expect(mockGetParametersByPath).toHaveBeenCalledTimes(1); + expect(mockGetParameters).toHaveBeenCalledTimes(1); }); - test('Update event with nexttoken', async () => { + test('Update event', async () => { // GIVEN const event = makeEvent({ RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + StackName: 'MyStack', + Exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + }, + }, ResourceProperties: { ServiceToken: '', Region: 'us-east-1', StackName: 'MyStack', Exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/MyOtherExport': 'MyOtherValue', }, }, }); // WHEN - mockGetParametersByPath.mockImplementationOnce(() => { - return { - NextToken: 'abc', - Parameters: [{ - Name: '/cdk/exports/MyStack/MyExport', - Value: 'Value', - }], - }; - }).mockImplementation(() => { - return { - Parameters: [{ - Name: '/cdk/exports/MyStack/MyOtherExport', - Value: 'MyOtherValue', - }], - }; - }); await handler(event); // THEN @@ -146,15 +169,22 @@ describe('cross-region-ssm-writer entrypoint', () => { Value: 'Value', Type: 'String', }); - expect(mockPutParameter).toHaveBeenCalledTimes(2); + expect(mockPutParameter).toHaveBeenCalledTimes(1); expect(mockDeleteParameters).toHaveBeenCalledTimes(0); - expect(mockGetParametersByPath).toHaveBeenCalledTimes(2); + expect(mockGetParameters).toHaveBeenCalledTimes(1); }); test('Update event with delete', async () => { // GIVEN const event = makeEvent({ RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + StackName: 'MyStack', + Exports: { + '/cdk/exports/MyStack/RemovedExport': 'MyRemovedValue', + }, + }, ResourceProperties: { ServiceToken: '', Region: 'us-east-1', @@ -167,14 +197,6 @@ describe('cross-region-ssm-writer entrypoint', () => { }); // WHEN - mockGetParametersByPath.mockImplementation(() => { - return { - Parameters: [{ - Name: 'RemovedExport', - Value: 'RemovedValue', - }], - }; - }); await handler(event); // THEN @@ -189,11 +211,11 @@ describe('cross-region-ssm-writer entrypoint', () => { Type: 'String', }); expect(mockDeleteParameters).toHaveBeenCalledWith({ - Names: ['RemovedExport'], + Names: ['/cdk/exports/MyStack/RemovedExport'], }); expect(mockPutParameter).toHaveBeenCalledTimes(2); expect(mockDeleteParameters).toHaveBeenCalledTimes(1); - expect(mockGetParametersByPath).toHaveBeenCalledTimes(1); + expect(mockGetParameters).toHaveBeenCalledTimes(1); }); test('Delete event', async () => { // GIVEN @@ -204,30 +226,21 @@ describe('cross-region-ssm-writer entrypoint', () => { Region: 'us-east-1', StackName: 'MyStack', Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/MyOtherExport': 'MyOtherValue', + '/cdk/exports/MyStack/RemovedExport': 'RemovedValue', }, }, }); // WHEN - mockGetParametersByPath.mockImplementation(() => { - return { - Parameters: [{ - Name: 'RemovedExport', - Value: 'RemovedValue', - }], - }; - }); await handler(event); // THEN expect(mockDeleteParameters).toHaveBeenCalledWith({ - Names: ['RemovedExport'], + Names: ['/cdk/exports/MyStack/RemovedExport'], }); expect(mockPutParameter).toHaveBeenCalledTimes(0); expect(mockDeleteParameters).toHaveBeenCalledTimes(1); - expect(mockGetParametersByPath).toHaveBeenCalledTimes(1); + expect(mockGetParameters).toHaveBeenCalledTimes(0); }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index be1255298e535..4a51b05cd3b72 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -7,7 +7,7 @@ describe('export writer provider', () => { test('basic configuration', () => { // GIVEN const app = new App(); - const stack = new Stack(app); + const stack = new Stack(app, 'Stack1'); const resource = new CfnResource(stack, 'MyResource', { type: 'Custom::MyResource', }); @@ -23,7 +23,7 @@ describe('export writer provider', () => { const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; const assetHash = staging.assetHash; - expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/Default/MyResourceName}}'); + expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/Stack1-MyResourceName}}'); expect(cfn).toEqual({ Resources: { MyResource: { @@ -67,7 +67,7 @@ describe('export writer provider', () => { { Ref: 'AWS::AccountId', }, - ':parameter/cdk/exports/Default/*', + ':parameter/cdk/exports/*', ], ], }, @@ -96,14 +96,13 @@ describe('export writer provider', () => { ], }, Exports: { - '/cdk/exports/Default/MyResourceName': { + '/cdk/exports/Default-MyResourceName': { 'Fn::GetAtt': [ 'MyResource', 'arn', ], }, }, - StackName: 'Default', }, Type: 'Custom::CrossRegionExportWriter', UpdateReplacePolicy: 'Delete', diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 7df41b8e5cee9..1b2809bb826e5 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -68,7 +68,7 @@ describe('nested-stack', () => { expect(nestedTemplate2).toMatchObject({ Outputs: { Output: { - Value: '{{resolve:ssm:/cdk/exports/Stack1/MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D}}', + Value: '{{resolve:ssm:/cdk/exports/Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D}}', }, }, }); @@ -87,7 +87,7 @@ describe('nested-stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1/MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D': { + '/cdk/exports/Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D': { 'Fn::GetAtt': [ 'MyNestedStackNestedStackMyNestedStackNestedStackResource9C617903', 'Outputs.Stack1MyNestedStackMyResourceEDA18296Ref', diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index ff65e35891d98..49d5f75c03b2a 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -495,7 +495,7 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', @@ -520,7 +520,7 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', }, }, }, @@ -585,9 +585,9 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', - Other: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', + Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', }, }, }, @@ -602,7 +602,7 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + '/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other2', @@ -631,13 +631,13 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', ], }, - '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other', @@ -700,9 +700,9 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', - Other: '{{resolve:ssm:/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', + Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', }, }, }, @@ -717,7 +717,7 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack3/SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + '/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other2', @@ -746,13 +746,13 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', ], }, - '/cdk/exports/Stack1/SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other', From 5160273d6a1438fd38d0a3d06af2fdc930a7933e Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Tue, 27 Sep 2022 11:47:52 +0000 Subject: [PATCH 14/30] rerunning integ tests --- .../__entrypoint__.js | 0 .../index.d.ts | 0 .../index.js | 88 +++++++++++++++++ .../index.ts | 87 ++++++++++++++++ .../index.js | 97 ------------------ .../index.ts | 98 ------------------- .../cross-region-producer.assets.json | 10 +- .../cross-region-producer.template.json | 8 +- .../manifest.json | 2 +- .../__entrypoint__.js | 0 .../index.d.ts | 0 .../index.js | 88 +++++++++++++++++ .../index.ts | 87 ++++++++++++++++ .../index.js | 97 ------------------ .../index.ts | 98 ------------------- .../integ-pipeline-producer-stack.assets.json | 10 +- ...nteg-pipeline-producer-stack.template.json | 8 +- .../manifest.json | 2 +- .../cross-region-ssm-writer-handler/index.ts | 8 +- .../export-writer-provider.test.ts | 2 +- 20 files changed, 375 insertions(+), 415 deletions(-) rename packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/{asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989 => asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4}/__entrypoint__.js (100%) rename packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/{asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989 => asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989 => asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4}/__entrypoint__.js (100%) rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989 => asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js new file mode 100644 index 0000000000000..9bc3257fb6397 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js @@ -0,0 +1,88 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties; + const exports = props.Exports; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyExistingParameters(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyExistingParameters(ssm, newExports); + const paramsToDelete = filterExports(oldExports, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (Object.keys(paramsToDelete).length > 0) { + await ssm.deleteParameters({ + Names: Object.keys(paramsToDelete), + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(exports)), + }).promise(); + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters + */ +async function throwIfAnyExistingParameters(ssm, parameters) { + const result = await ssm.getParameters({ + Names: Object.keys(parameters), + }).promise(); + if ((result.Parameters ?? []).length > 0) { + const existing = result.Parameters.map(param => param.Name); + throw new Error(`Exports already exist: \n${existing.join('\n')}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source, filter) { + return Object.keys(source) + .filter(key => !filter.hasOwnProperty(key)) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSw0QkFBNEIsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2pELE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMscUJBQXFCLENBQUM7Z0JBQzdDLE1BQU0sVUFBVSxHQUF1QixRQUFRLENBQUMsT0FBTyxDQUFDO2dCQUN4RCxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsT0FBTyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN0RCxNQUFNLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDcEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUks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IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgYXdhaXQgcHV0UGFyYW1ldGVycyhzc20sIGV4cG9ydHMpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogQ3Jvc3NSZWdpb25FeHBvcnRzID0gb2xkUHJvcHMuRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGZpbHRlckV4cG9ydHMoZXhwb3J0cywgb2xkRXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgY29uc3QgcGFyYW1zVG9EZWxldGUgPSBmaWx0ZXJFeHBvcnRzKG9sZEV4cG9ydHMsIGV4cG9ydHMpO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIHVudXNlZCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgICBOYW1lczogT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgYWxsIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHNzbS5kZWxldGVQYXJhbWV0ZXJzKHtcbiAgICAgICAgICBOYW1lczogQXJyYXkuZnJvbShPYmplY3Qua2V5cyhleHBvcnRzKSksXG4gICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByZXN1bHQgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLFxuICB9KS5wcm9taXNlKCk7XG4gIGlmICgocmVzdWx0LlBhcmFtZXRlcnMgPz8gW10pLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBleGlzdGluZyA9IHJlc3VsdC5QYXJhbWV0ZXJzIS5tYXAocGFyYW0gPT4gcGFyYW0uTmFtZSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBFeHBvcnRzIGFscmVhZHkgZXhpc3Q6IFxcbiR7ZXhpc3Rpbmcuam9pbignXFxuJyl9YCk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZXR1cm4gb25seSB0aGUgaXRlbXMgZnJvbSBzb3VyY2UgdGhhdCBkbyBub3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICpcbiAqIEBwYXJhbSBzb3VyY2UgdGhlIHNvdXJjZSBvYmplY3QgdG8gcGVyZm9ybSB0aGUgZmlsdGVyIG9uXG4gKiBAcGFyYW0gZmlsdGVyIGZpbHRlciBvdXQgaXRlbXMgdGhhdCBleGlzdCBpbiB0aGlzIG9iamVjdFxuICovXG5mdW5jdGlvbiBmaWx0ZXJFeHBvcnRzKHNvdXJjZTogQ3Jvc3NSZWdpb25FeHBvcnRzLCBmaWx0ZXI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IENyb3NzUmVnaW9uRXhwb3J0cyB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhzb3VyY2UpXG4gICAgLmZpbHRlcihrZXkgPT4gIWZpbHRlci5oYXNPd25Qcm9wZXJ0eShrZXkpKVxuICAgIC5yZWR1Y2UoKGFjYzogQ3Jvc3NSZWdpb25FeHBvcnRzLCBjdXJyOiBzdHJpbmcpID0+IHtcbiAgICAgIGFjY1tjdXJyXSA9IHNvdXJjZVtjdXJyXTtcbiAgICAgIHJldHVybiBhY2M7XG4gICAgfSwge30pO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts new file mode 100644 index 0000000000000..8a829ea1a2cb4 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts @@ -0,0 +1,87 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; +type CrossRegionExports = { [exportName: string]: string }; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const exports: CrossRegionExports = props.Exports; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyExistingParameters(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports: CrossRegionExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyExistingParameters(ssm, newExports); + const paramsToDelete = filterExports(oldExports, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (Object.keys(paramsToDelete).length > 0) { + await ssm.deleteParameters({ + Names: Object.keys(paramsToDelete), + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(exports)), + }).promise(); + return; + default: + return; + } + } catch (e) { + console.error('Error processing event: ', e); + throw e; + } +}; + +/** + * Create parameters for existing exports + */ +async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} + +/** + * Query for existing parameters + */ +async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + const result = await ssm.getParameters({ + Names: Object.keys(parameters), + }).promise(); + if ((result.Parameters ?? []).length > 0) { + const existing = result.Parameters!.map(param => param.Name); + throw new Error(`Exports already exist: \n${existing.join('\n')}`); + } +} + +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { + return Object.keys(source) + .filter(key => !filter.hasOwnProperty(key)) + .reduce((acc: CrossRegionExports, curr: string) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js deleted file mode 100644 index a45e08eb17a6d..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js +++ /dev/null @@ -1,97 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -const SSM_EXPORT_PATH = '/cdk/exports/'; -async function handler(event) { - const props = event.ResourceProperties; - const exports = props.Exports; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Update': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existing = await getExistingParameters(ssm); - const paramsToDelete = returnMissing(existing, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (paramsToDelete.length > 0) { - await ssm.deleteParameters({ - Names: paramsToDelete, - }).promise(); - } - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Delete': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existingParams = await getExistingParameters(ssm); - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(existingParams)), - }).promise(); - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: `${SSM_EXPORT_PATH}${name}`, - Value: value, - Type: 'String', - }).promise(); - })); -} -function returnMissing(a, b) { - const missing = []; - for (const name of Object.keys(a)) { - if (!b.hasOwnProperty(name)) { - missing.push(name); - } - } - return missing; -} -/** - * Get existing exports from SSM parameters - */ -async function getExistingParameters(ssm) { - const existingExports = {}; - function recordParameters(parameters) { - parameters.forEach(param => { - if (param.Name && param.Value) { - existingExports[param.Name] = param.Value; - } - }); - } - const res = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - }).promise(); - recordParameters(res.Parameters ?? []); - while (res.NextToken) { - const nextRes = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - NextToken: res.NextToken, - }).promise(); - recordParameters(nextRes.Parameters ?? []); - res.NextToken = nextRes.NextToken; - } - return existingExports; -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUM5QixNQUFNLGVBQWUsR0FBRyxlQUFlLENBQUM7QUFHakMsS0FBSyxVQUFVLE9BQU8sQ0FBQyxLQUFrRDtJQUM5RSxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUM7SUFDdkMsTUFBTSxPQUFPLEdBQXVCLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFFbEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLG9EQUFvRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDakYsTUFBTSxRQUFRLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDbEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDeEQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUksY0FBYyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7b0JBQzdCLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO3dCQUN6QixLQUFLLEVBQUUsY0FBYztxQkFDdEIs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bUIsQ0FBQztZQUM1QyxJQUFJLEVBQUUsR0FBRyxlQUFlLEVBQUU7WUFDMUIsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTO1NBQ3pCLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUM7UUFDM0MsR0FBRyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDO0tBQ25DO0lBQ0QsT0FBTyxlQUFlLENBQUM7QUFDekIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xuY29uc3QgU1NNX0VYUE9SVF9QQVRIID0gJy9jZGsvZXhwb3J0cy8nO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBSZWFkaW5nIGV4aXN0aW5nIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbSk7XG4gICAgICAgIGNvbnN0IHBhcmFtc1RvRGVsZXRlID0gcmV0dXJuTWlzc2luZyhleGlzdGluZywgZXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGlmIChwYXJhbXNUb0RlbGV0ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgYXdhaXQgc3NtLmRlbGV0ZVBhcmFtZXRlcnMoe1xuICAgICAgICAgICAgTmFtZXM6IHBhcmFtc1RvRGVsZXRlLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgUmVhZGluZyBleGlzdGluZyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBjb25zdCBleGlzdGluZ1BhcmFtcyA9IGF3YWl0IGdldEV4aXN0aW5nUGFyYW1ldGVycyhzc20pO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIGFsbCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IEFycmF5LmZyb20oT2JqZWN0LmtleXMoZXhpc3RpbmdQYXJhbXMpKSxcbiAgICAgICAgfSkucHJvbWlzZSgpO1xuICAgICAgICByZXR1cm47XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm47XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc29sZS5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBldmVudDogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBDcmVhdGUgcGFyYW1ldGVycyBmb3IgZXhpc3RpbmcgZXhwb3J0c1xuICovXG5hc3luYyBmdW5jdGlvbiBwdXRQYXJhbWV0ZXJzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgUHJvbWlzZS5hbGwoQXJyYXkuZnJvbShPYmplY3QuZW50cmllcyhwYXJhbWV0ZXJzKSwgKFtuYW1lLCB2YWx1ZV0pID0+IHtcbiAgICByZXR1cm4gc3NtLnB1dFBhcmFtZXRlcih7XG4gICAgICBOYW1lOiBgJHtTU01fRVhQT1JUX1BBVEh9JHtuYW1lfWAsXG4gICAgICBWYWx1ZTogdmFsdWUsXG4gICAgICBUeXBlOiAnU3RyaW5nJyxcbiAgICB9KS5wcm9taXNlKCk7XG4gIH0pKTtcbn1cblxuZnVuY3Rpb24gcmV0dXJuTWlzc2luZyhhOiBDcm9zc1JlZ2lvbkV4cG9ydHMsIGI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IHN0cmluZ1tdIHtcbiAgY29uc3QgbWlzc2luZzogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChjb25zdCBuYW1lIG9mIE9iamVjdC5rZXlzKGEpKSB7XG4gICAgaWYgKCFiLmhhc093blByb3BlcnR5KG5hbWUpKSB7XG4gICAgICBtaXNzaW5nLnB1c2gobmFtZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBtaXNzaW5nO1xufVxuXG4vKipcbiAqIEdldCBleGlzdGluZyBleHBvcnRzIGZyb20gU1NNIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbTogU1NNKTogUHJvbWlzZTxDcm9zc1JlZ2lvbkV4cG9ydHM+IHtcbiAgY29uc3QgZXhpc3RpbmdFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7fTtcbiAgZnVuY3Rpb24gcmVjb3JkUGFyYW1ldGVycyhwYXJhbWV0ZXJzOiBTU00uUGFyYW1ldGVyTGlzdCkge1xuICAgIHBhcmFtZXRlcnMuZm9yRWFjaChwYXJhbSA9PiB7XG4gICAgICBpZiAocGFyYW0uTmFtZSAmJiBwYXJhbS5WYWx1ZSkge1xuICAgICAgICBleGlzdGluZ0V4cG9ydHNbcGFyYW0uTmFtZV0gPSBwYXJhbS5WYWx1ZTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBjb25zdCByZXMgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVyc0J5UGF0aCh7XG4gICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gIH0pLnByb21pc2UoKTtcbiAgcmVjb3JkUGFyYW1ldGVycyhyZXMuUGFyYW1ldGVycyA/PyBbXSk7XG5cbiAgd2hpbGUgKHJlcy5OZXh0VG9rZW4pIHtcbiAgICBjb25zdCBuZXh0UmVzID0gYXdhaXQgc3NtLmdldFBhcmFtZXRlcnNCeVBhdGgoe1xuICAgICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gICAgICBOZXh0VG9rZW46IHJlcy5OZXh0VG9rZW4sXG4gICAgfSkucHJvbWlzZSgpO1xuICAgIHJlY29yZFBhcmFtZXRlcnMobmV4dFJlcy5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICByZXMuTmV4dFRva2VuID0gbmV4dFJlcy5OZXh0VG9rZW47XG4gIH1cbiAgcmV0dXJuIGV4aXN0aW5nRXhwb3J0cztcbn1cblxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts deleted file mode 100644 index 40c505eb55657..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts +++ /dev/null @@ -1,98 +0,0 @@ -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -import { SSM } from 'aws-sdk'; -const SSM_EXPORT_PATH = '/cdk/exports/'; -type CrossRegionExports = { [exportName: string]: string }; - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const exports: CrossRegionExports = props.Exports; - - const ssm = new SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Update': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existing = await getExistingParameters(ssm); - const paramsToDelete = returnMissing(existing, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (paramsToDelete.length > 0) { - await ssm.deleteParameters({ - Names: paramsToDelete, - }).promise(); - } - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Delete': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existingParams = await getExistingParameters(ssm); - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(existingParams)), - }).promise(); - return; - default: - return; - } - } catch (e) { - console.error('Error processing event: ', e); - throw e; - } -}; - -/** - * Create parameters for existing exports - */ -async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: `${SSM_EXPORT_PATH}${name}`, - Value: value, - Type: 'String', - }).promise(); - })); -} - -function returnMissing(a: CrossRegionExports, b: CrossRegionExports): string[] { - const missing: string[] = []; - for (const name of Object.keys(a)) { - if (!b.hasOwnProperty(name)) { - missing.push(name); - } - } - return missing; -} - -/** - * Get existing exports from SSM parameters - */ -async function getExistingParameters(ssm: SSM): Promise { - const existingExports: CrossRegionExports = {}; - function recordParameters(parameters: SSM.ParameterList) { - parameters.forEach(param => { - if (param.Name && param.Value) { - existingExports[param.Name] = param.Value; - } - }); - } - const res = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - }).promise(); - recordParameters(res.Parameters ?? []); - - while (res.NextToken) { - const nextRes = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - NextToken: res.NextToken, - }).promise(); - recordParameters(nextRes.Parameters ?? []); - res.NextToken = nextRes.NextToken; - } - return existingExports; -} - diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json index a00df32d70971..cc754585b35fd 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -1,15 +1,15 @@ { "version": "21.0.0", "files": { - "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989": { + "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4": { "source": { - "path": "asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989", + "path": "asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip", + "objectKey": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } @@ -29,7 +29,7 @@ } } }, - "ba6acb4b66bba3f1449723bc0b9913b0c7f4d9b28bb6ecfd3f29014734e2734e": { + "7a3fc43a5189fb6d6a3bfc72db7aa7ac212db1e56232e932ed0fef91147422ba": { "source": { "path": "cross-region-producer.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "ba6acb4b66bba3f1449723bc0b9913b0c7f4d9b28bb6ecfd3f29014734e2734e.json", + "objectKey": "7a3fc43a5189fb6d6a3bfc72db7aa7ac212db1e56232e932ed0fef91147422ba.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json index d000b381a2284..9cb0a09c980e8 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -39,13 +39,13 @@ }, "Region": "us-east-2", "Exports": { - "cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429": { + "/cdk/exports/cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429": { "Fn::GetAtt": [ "IntegQueue3A18718A", "QueueName" ] }, - "cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698": { + "/cdk/exports/cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698": { "Fn::GetAtt": [ "IntegNestedNestedStackIntegNestedNestedStackResource168C5881", "Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" @@ -97,7 +97,7 @@ ] }, "Action": [ - "ssm:GetParametersByPath", + "ssm:GetParameters", "ssm:PutParameter", "ssm:DeleteParameters" ] @@ -115,7 +115,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip" + "S3Key": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index 132cdb6bc26b2..eda8d9769ed06 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/ba6acb4b66bba3f1449723bc0b9913b0c7f4d9b28bb6ecfd3f29014734e2734e.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/7a3fc43a5189fb6d6a3bfc72db7aa7ac212db1e56232e932ed0fef91147422ba.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/__entrypoint__.js rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.d.ts rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js new file mode 100644 index 0000000000000..9bc3257fb6397 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js @@ -0,0 +1,88 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties; + const exports = props.Exports; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyExistingParameters(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyExistingParameters(ssm, newExports); + const paramsToDelete = filterExports(oldExports, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (Object.keys(paramsToDelete).length > 0) { + await ssm.deleteParameters({ + Names: Object.keys(paramsToDelete), + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(exports)), + }).promise(); + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters + */ +async function throwIfAnyExistingParameters(ssm, parameters) { + const result = await ssm.getParameters({ + Names: Object.keys(parameters), + }).promise(); + if ((result.Parameters ?? []).length > 0) { + const existing = result.Parameters.map(param => param.Name); + throw new Error(`Exports already exist: \n${existing.join('\n')}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source, filter) { + return Object.keys(source) + .filter(key => !filter.hasOwnProperty(key)) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSw0QkFBNEIsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2pELE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMscUJBQXFCLENBQUM7Z0JBQzdDLE1BQU0sVUFBVSxHQUF1QixRQUFRLENBQUMsT0FBTyxDQUFDO2dCQUN4RCxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsT0FBTyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN0RCxNQUFNLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDcEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUks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IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgYXdhaXQgcHV0UGFyYW1ldGVycyhzc20sIGV4cG9ydHMpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogQ3Jvc3NSZWdpb25FeHBvcnRzID0gb2xkUHJvcHMuRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGZpbHRlckV4cG9ydHMoZXhwb3J0cywgb2xkRXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgY29uc3QgcGFyYW1zVG9EZWxldGUgPSBmaWx0ZXJFeHBvcnRzKG9sZEV4cG9ydHMsIGV4cG9ydHMpO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIHVudXNlZCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgICBOYW1lczogT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgYWxsIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHNzbS5kZWxldGVQYXJhbWV0ZXJzKHtcbiAgICAgICAgICBOYW1lczogQXJyYXkuZnJvbShPYmplY3Qua2V5cyhleHBvcnRzKSksXG4gICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByZXN1bHQgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLFxuICB9KS5wcm9taXNlKCk7XG4gIGlmICgocmVzdWx0LlBhcmFtZXRlcnMgPz8gW10pLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBleGlzdGluZyA9IHJlc3VsdC5QYXJhbWV0ZXJzIS5tYXAocGFyYW0gPT4gcGFyYW0uTmFtZSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBFeHBvcnRzIGFscmVhZHkgZXhpc3Q6IFxcbiR7ZXhpc3Rpbmcuam9pbignXFxuJyl9YCk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZXR1cm4gb25seSB0aGUgaXRlbXMgZnJvbSBzb3VyY2UgdGhhdCBkbyBub3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICpcbiAqIEBwYXJhbSBzb3VyY2UgdGhlIHNvdXJjZSBvYmplY3QgdG8gcGVyZm9ybSB0aGUgZmlsdGVyIG9uXG4gKiBAcGFyYW0gZmlsdGVyIGZpbHRlciBvdXQgaXRlbXMgdGhhdCBleGlzdCBpbiB0aGlzIG9iamVjdFxuICovXG5mdW5jdGlvbiBmaWx0ZXJFeHBvcnRzKHNvdXJjZTogQ3Jvc3NSZWdpb25FeHBvcnRzLCBmaWx0ZXI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IENyb3NzUmVnaW9uRXhwb3J0cyB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhzb3VyY2UpXG4gICAgLmZpbHRlcihrZXkgPT4gIWZpbHRlci5oYXNPd25Qcm9wZXJ0eShrZXkpKVxuICAgIC5yZWR1Y2UoKGFjYzogQ3Jvc3NSZWdpb25FeHBvcnRzLCBjdXJyOiBzdHJpbmcpID0+IHtcbiAgICAgIGFjY1tjdXJyXSA9IHNvdXJjZVtjdXJyXTtcbiAgICAgIHJldHVybiBhY2M7XG4gICAgfSwge30pO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts new file mode 100644 index 0000000000000..8a829ea1a2cb4 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts @@ -0,0 +1,87 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; +type CrossRegionExports = { [exportName: string]: string }; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const exports: CrossRegionExports = props.Exports; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyExistingParameters(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports: CrossRegionExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyExistingParameters(ssm, newExports); + const paramsToDelete = filterExports(oldExports, exports); + console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); + if (Object.keys(paramsToDelete).length > 0) { + await ssm.deleteParameters({ + Names: Object.keys(paramsToDelete), + }).promise(); + } + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); + await ssm.deleteParameters({ + Names: Array.from(Object.keys(exports)), + }).promise(); + return; + default: + return; + } + } catch (e) { + console.error('Error processing event: ', e); + throw e; + } +}; + +/** + * Create parameters for existing exports + */ +async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} + +/** + * Query for existing parameters + */ +async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + const result = await ssm.getParameters({ + Names: Object.keys(parameters), + }).promise(); + if ((result.Parameters ?? []).length > 0) { + const existing = result.Parameters!.map(param => param.Name); + throw new Error(`Exports already exist: \n${existing.join('\n')}`); + } +} + +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { + return Object.keys(source) + .filter(key => !filter.hasOwnProperty(key)) + .reduce((acc: CrossRegionExports, curr: string) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js deleted file mode 100644 index a45e08eb17a6d..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.js +++ /dev/null @@ -1,97 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -const SSM_EXPORT_PATH = '/cdk/exports/'; -async function handler(event) { - const props = event.ResourceProperties; - const exports = props.Exports; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Update': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existing = await getExistingParameters(ssm); - const paramsToDelete = returnMissing(existing, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (paramsToDelete.length > 0) { - await ssm.deleteParameters({ - Names: paramsToDelete, - }).promise(); - } - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Delete': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existingParams = await getExistingParameters(ssm); - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(existingParams)), - }).promise(); - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: `${SSM_EXPORT_PATH}${name}`, - Value: value, - Type: 'String', - }).promise(); - })); -} -function returnMissing(a, b) { - const missing = []; - for (const name of Object.keys(a)) { - if (!b.hasOwnProperty(name)) { - missing.push(name); - } - } - return missing; -} -/** - * Get existing exports from SSM parameters - */ -async function getExistingParameters(ssm) { - const existingExports = {}; - function recordParameters(parameters) { - parameters.forEach(param => { - if (param.Name && param.Value) { - existingExports[param.Name] = param.Value; - } - }); - } - const res = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - }).promise(); - recordParameters(res.Parameters ?? []); - while (res.NextToken) { - const nextRes = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - NextToken: res.NextToken, - }).promise(); - recordParameters(nextRes.Parameters ?? []); - res.NextToken = nextRes.NextToken; - } - return existingExports; -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUM5QixNQUFNLGVBQWUsR0FBRyxlQUFlLENBQUM7QUFHakMsS0FBSyxVQUFVLE9BQU8sQ0FBQyxLQUFrRDtJQUM5RSxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUM7SUFDdkMsTUFBTSxPQUFPLEdBQXVCLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFFbEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLG9EQUFvRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDakYsTUFBTSxRQUFRLEdBQUcsTUFBTSxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDbEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDeEQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUksY0FBYyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7b0JBQzdCLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO3dCQUN6QixLQUFLLEVBQUUsY0FBYztxQkFDdEIs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bUIsQ0FBQztZQUM1QyxJQUFJLEVBQUUsR0FBRyxlQUFlLEVBQUU7WUFDMUIsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTO1NBQ3pCLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUM7UUFDM0MsR0FBRyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDO0tBQ25DO0lBQ0QsT0FBTyxlQUFlLENBQUM7QUFDekIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xuY29uc3QgU1NNX0VYUE9SVF9QQVRIID0gJy9jZGsvZXhwb3J0cy8nO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBSZWFkaW5nIGV4aXN0aW5nIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbSk7XG4gICAgICAgIGNvbnN0IHBhcmFtc1RvRGVsZXRlID0gcmV0dXJuTWlzc2luZyhleGlzdGluZywgZXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGlmIChwYXJhbXNUb0RlbGV0ZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgYXdhaXQgc3NtLmRlbGV0ZVBhcmFtZXRlcnMoe1xuICAgICAgICAgICAgTmFtZXM6IHBhcmFtc1RvRGVsZXRlLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgUmVhZGluZyBleGlzdGluZyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBjb25zdCBleGlzdGluZ1BhcmFtcyA9IGF3YWl0IGdldEV4aXN0aW5nUGFyYW1ldGVycyhzc20pO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIGFsbCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IEFycmF5LmZyb20oT2JqZWN0LmtleXMoZXhpc3RpbmdQYXJhbXMpKSxcbiAgICAgICAgfSkucHJvbWlzZSgpO1xuICAgICAgICByZXR1cm47XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm47XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc29sZS5lcnJvcignRXJyb3IgcHJvY2Vzc2luZyBldmVudDogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBDcmVhdGUgcGFyYW1ldGVycyBmb3IgZXhpc3RpbmcgZXhwb3J0c1xuICovXG5hc3luYyBmdW5jdGlvbiBwdXRQYXJhbWV0ZXJzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgUHJvbWlzZS5hbGwoQXJyYXkuZnJvbShPYmplY3QuZW50cmllcyhwYXJhbWV0ZXJzKSwgKFtuYW1lLCB2YWx1ZV0pID0+IHtcbiAgICByZXR1cm4gc3NtLnB1dFBhcmFtZXRlcih7XG4gICAgICBOYW1lOiBgJHtTU01fRVhQT1JUX1BBVEh9JHtuYW1lfWAsXG4gICAgICBWYWx1ZTogdmFsdWUsXG4gICAgICBUeXBlOiAnU3RyaW5nJyxcbiAgICB9KS5wcm9taXNlKCk7XG4gIH0pKTtcbn1cblxuZnVuY3Rpb24gcmV0dXJuTWlzc2luZyhhOiBDcm9zc1JlZ2lvbkV4cG9ydHMsIGI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IHN0cmluZ1tdIHtcbiAgY29uc3QgbWlzc2luZzogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChjb25zdCBuYW1lIG9mIE9iamVjdC5rZXlzKGEpKSB7XG4gICAgaWYgKCFiLmhhc093blByb3BlcnR5KG5hbWUpKSB7XG4gICAgICBtaXNzaW5nLnB1c2gobmFtZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBtaXNzaW5nO1xufVxuXG4vKipcbiAqIEdldCBleGlzdGluZyBleHBvcnRzIGZyb20gU1NNIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0RXhpc3RpbmdQYXJhbWV0ZXJzKHNzbTogU1NNKTogUHJvbWlzZTxDcm9zc1JlZ2lvbkV4cG9ydHM+IHtcbiAgY29uc3QgZXhpc3RpbmdFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7fTtcbiAgZnVuY3Rpb24gcmVjb3JkUGFyYW1ldGVycyhwYXJhbWV0ZXJzOiBTU00uUGFyYW1ldGVyTGlzdCkge1xuICAgIHBhcmFtZXRlcnMuZm9yRWFjaChwYXJhbSA9PiB7XG4gICAgICBpZiAocGFyYW0uTmFtZSAmJiBwYXJhbS5WYWx1ZSkge1xuICAgICAgICBleGlzdGluZ0V4cG9ydHNbcGFyYW0uTmFtZV0gPSBwYXJhbS5WYWx1ZTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBjb25zdCByZXMgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVyc0J5UGF0aCh7XG4gICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gIH0pLnByb21pc2UoKTtcbiAgcmVjb3JkUGFyYW1ldGVycyhyZXMuUGFyYW1ldGVycyA/PyBbXSk7XG5cbiAgd2hpbGUgKHJlcy5OZXh0VG9rZW4pIHtcbiAgICBjb25zdCBuZXh0UmVzID0gYXdhaXQgc3NtLmdldFBhcmFtZXRlcnNCeVBhdGgoe1xuICAgICAgUGF0aDogYCR7U1NNX0VYUE9SVF9QQVRIfWAsXG4gICAgICBOZXh0VG9rZW46IHJlcy5OZXh0VG9rZW4sXG4gICAgfSkucHJvbWlzZSgpO1xuICAgIHJlY29yZFBhcmFtZXRlcnMobmV4dFJlcy5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICByZXMuTmV4dFRva2VuID0gbmV4dFJlcy5OZXh0VG9rZW47XG4gIH1cbiAgcmV0dXJuIGV4aXN0aW5nRXhwb3J0cztcbn1cblxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts deleted file mode 100644 index 40c505eb55657..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989/index.ts +++ /dev/null @@ -1,98 +0,0 @@ -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -import { SSM } from 'aws-sdk'; -const SSM_EXPORT_PATH = '/cdk/exports/'; -type CrossRegionExports = { [exportName: string]: string }; - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const exports: CrossRegionExports = props.Exports; - - const ssm = new SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Update': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existing = await getExistingParameters(ssm); - const paramsToDelete = returnMissing(existing, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (paramsToDelete.length > 0) { - await ssm.deleteParameters({ - Names: paramsToDelete, - }).promise(); - } - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, exports); - return; - case 'Delete': - console.info(`Reading existing SSM Parameter exports in region ${props.Region}`); - const existingParams = await getExistingParameters(ssm); - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(existingParams)), - }).promise(); - return; - default: - return; - } - } catch (e) { - console.error('Error processing event: ', e); - throw e; - } -}; - -/** - * Create parameters for existing exports - */ -async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: `${SSM_EXPORT_PATH}${name}`, - Value: value, - Type: 'String', - }).promise(); - })); -} - -function returnMissing(a: CrossRegionExports, b: CrossRegionExports): string[] { - const missing: string[] = []; - for (const name of Object.keys(a)) { - if (!b.hasOwnProperty(name)) { - missing.push(name); - } - } - return missing; -} - -/** - * Get existing exports from SSM parameters - */ -async function getExistingParameters(ssm: SSM): Promise { - const existingExports: CrossRegionExports = {}; - function recordParameters(parameters: SSM.ParameterList) { - parameters.forEach(param => { - if (param.Name && param.Value) { - existingExports[param.Name] = param.Value; - } - }); - } - const res = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - }).promise(); - recordParameters(res.Parameters ?? []); - - while (res.NextToken) { - const nextRes = await ssm.getParametersByPath({ - Path: `${SSM_EXPORT_PATH}`, - NextToken: res.NextToken, - }).promise(); - recordParameters(nextRes.Parameters ?? []); - res.NextToken = nextRes.NextToken; - } - return existingExports; -} - diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json index fdf64dc1b8dd8..8fc006d2da670 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json @@ -15,21 +15,21 @@ } } }, - "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989": { + "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4": { "source": { - "path": "asset.81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989", + "path": "asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip", + "objectKey": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "f6b8517d223bcb31f6544cef17261ee6be38b83b8bb0e212b76d81a03df462a5": { + "b999b67fa19cd155ebe11ceceae9a49374d11b1a245cdc0fe354761ab831646b": { "source": { "path": "integ-pipeline-producer-stack.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "f6b8517d223bcb31f6544cef17261ee6be38b83b8bb0e212b76d81a03df462a5.json", + "objectKey": "b999b67fa19cd155ebe11ceceae9a49374d11b1a245cdc0fe354761ab831646b.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json index 6330564efa374..fa079b36f4f9d 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json @@ -198,10 +198,10 @@ }, "Region": "us-east-2", "Exports": { - "integ-pipeline-producer-stack-ReplicationBucketRefReplicationBucket70D6873707044AE1": { + "/cdk/exports/integ-pipeline-producer-stack-ReplicationBucketRefReplicationBucket70D6873707044AE1": { "Ref": "ReplicationBucket70D68737" }, - "integ-pipeline-producer-stack-ReplicationKeyFnGetAttReplicationKeyFCE40BF4ArnFBFE312D": { + "/cdk/exports/integ-pipeline-producer-stack-ReplicationKeyFnGetAttReplicationKeyFCE40BF4ArnFBFE312D": { "Fn::GetAtt": [ "ReplicationKeyFCE40BF4", "Arn" @@ -253,7 +253,7 @@ ] }, "Action": [ - "ssm:GetParametersByPath", + "ssm:GetParameters", "ssm:PutParameter", "ssm:DeleteParameters" ] @@ -271,7 +271,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "81ce50203f1ecfd42920cc23099e4751011d8596f9eb0671f9b703f703b1c989.zip" + "S3Key": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json index 6d8e7deb80d6d..d95e5eb95f850 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/f6b8517d223bcb31f6544cef17261ee6be38b83b8bb0e212b76d81a03df462a5.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/b999b67fa19cd155ebe11ceceae9a49374d11b1a245cdc0fe354761ab831646b.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts index 650f970a48b9d..8a829ea1a2cb4 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts @@ -18,9 +18,9 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent case 'Update': const oldProps = event.OldResourceProperties; const oldExports: CrossRegionExports = oldProps.Exports; - const newExports = filter(exports, oldExports); + const newExports = filterExports(exports, oldExports); await throwIfAnyExistingParameters(ssm, newExports); - const paramsToDelete = filter(oldExports, exports); + const paramsToDelete = filterExports(oldExports, exports); console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); if (Object.keys(paramsToDelete).length > 0) { await ssm.deleteParameters({ @@ -73,11 +73,11 @@ async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExp /** * Return only the items from source that do not exist in the filter - * + * * @param source the source object to perform the filter on * @param filter filter out items that exist in this object */ -function filter(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { +function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) .filter(key => !filter.hasOwnProperty(key)) .reduce((acc: CrossRegionExports, curr: string) => { diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index 4a51b05cd3b72..648a43c46e551 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -96,7 +96,7 @@ describe('export writer provider', () => { ], }, Exports: { - '/cdk/exports/Default-MyResourceName': { + '/cdk/exports/Stack1-MyResourceName': { 'Fn::GetAtt': [ 'MyResource', 'arn', From 1933290ec4135ec1a756c932ae1b63b4c5ea4857 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Tue, 27 Sep 2022 12:04:21 +0000 Subject: [PATCH 15/30] fixing formatting --- .../cross-region-ssm-writer-handler.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts index c805d71038b76..6227b4028bd51 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -38,7 +38,7 @@ beforeEach(() => { }); mockGetParameters.mockImplementation(() => { return {}; - }) + }); }); afterEach(() => { jest.restoreAllMocks(); @@ -107,7 +107,7 @@ describe('cross-region-ssm-writer throws', () => { // THEN await expect(handler(event)).rejects.toThrow(/Exports already exist/); }); -}) +}); describe('cross-region-ssm-writer entrypoint', () => { test('Create event', async () => { From 472c33765cb68f0413a4d1f6c95ce3936952fd99 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Tue, 27 Sep 2022 12:53:35 +0000 Subject: [PATCH 16/30] fixing tests --- packages/@aws-cdk/core/test/cross-environment-token.test.ts | 1 - .../custom-resource-provider/export-writer-provider.test.ts | 6 +++--- packages/@aws-cdk/core/test/nested-stack.test.ts | 1 - packages/@aws-cdk/core/test/stack.test.ts | 5 ----- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index 56b406855f051..48863717741db 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -225,7 +225,6 @@ describe('cross environment', () => { }, }, 'Region': 'bermuda-triangle-42', - 'StackName': 'Stack1', 'ServiceToken': { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index 648a43c46e551..c1dd08af8778f 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -23,7 +23,7 @@ describe('export writer provider', () => { const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; const assetHash = staging.assetHash; - expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/Stack1-MyResourceName}}'); + expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/MyResourceName}}'); expect(cfn).toEqual({ Resources: { MyResource: { @@ -50,7 +50,7 @@ describe('export writer provider', () => { Statement: [ { Action: [ - 'ssm:GetParametersByPath', + 'ssm:GetParameters', 'ssm:PutParameter', 'ssm:DeleteParameters', ], @@ -96,7 +96,7 @@ describe('export writer provider', () => { ], }, Exports: { - '/cdk/exports/Stack1-MyResourceName': { + '/cdk/exports/MyResourceName': { 'Fn::GetAtt': [ 'MyResource', 'arn', diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 1b2809bb826e5..8228f5f7f987f 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -94,7 +94,6 @@ describe('nested-stack', () => { ], }, }, - StackName: 'Stack1', Region: 'bermuda-triangle-42', ServiceToken: { 'Fn::GetAtt': [ diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 49d5f75c03b2a..9a40c71bc16f3 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -502,7 +502,6 @@ describe('stack', () => { ], }, }, - StackName: 'Stack1', Region: 'us-east-2', ServiceToken: { 'Fn::GetAtt': [ @@ -610,7 +609,6 @@ describe('stack', () => { }, }, Region: 'us-east-2', - StackName: 'Stack3', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -645,7 +643,6 @@ describe('stack', () => { }, }, Region: 'us-east-2', - StackName: 'Stack1', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -725,7 +722,6 @@ describe('stack', () => { }, }, Region: 'us-east-2', - StackName: 'Stack3', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -760,7 +756,6 @@ describe('stack', () => { }, }, Region: 'us-east-2', - StackName: 'Stack1', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', From 308cb1ba442efb1260490db24e9253babe6c7e49 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 30 Sep 2022 18:40:55 +0000 Subject: [PATCH 17/30] Adding export reader in consuming stack --- .../cross-region-ssm-reader-handler/index.ts | 125 +++++++++++ .../cross-region-ssm-writer-handler/index.ts | 60 ++++-- .../export-reader-provider.ts | 91 ++++++++ .../export-writer-provider.ts | 41 +++- packages/@aws-cdk/core/lib/private/refs.ts | 36 ++-- .../core/test/cross-environment-token.test.ts | 6 +- .../cross-region-ssm-reader-handler.test.ts | 204 ++++++++++++++++++ .../cross-region-ssm-writer-handler.test.ts | 125 ++++++----- .../export-writer-provider.test.ts | 119 +++++++++- .../@aws-cdk/core/test/nested-stack.test.ts | 58 +++-- packages/@aws-cdk/core/test/stack.test.ts | 99 +++++++-- 11 files changed, 828 insertions(+), 136 deletions(-) create mode 100644 packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts create mode 100644 packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts create mode 100644 packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts new file mode 100644 index 0000000000000..23ec4ad44a7f8 --- /dev/null +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts @@ -0,0 +1,125 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const imports: string[] = props.Imports; + const keyName: string = `cdk-strong-ref:${props.StackName}`; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info('Tagging SSM Parameter imports'); + await addTags(ssm, imports, keyName); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports: string[] = oldProps.Imports; + const newExports = filterExports(imports, oldExports); + const paramsToDelete = filterExports(oldExports, imports); + console.info('Releasing unused SSM Parameter imports'); + if (Object.keys(paramsToDelete).length > 0) { + await removeTags(ssm, paramsToDelete, keyName); + } + console.info('Tagging new SSM Parameter imports'); + await addTags(ssm, newExports, keyName); + return; + case 'Delete': + console.info('Deleting all SSM Parameter exports'); + await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); + return; + default: + return; + } + } catch (e) { + console.error('Error importing cross region stack exports: ', e); + throw e; + } +}; + +/** + * Add tag to parameters for existing exports + */ +async function addTags(ssm: SSM, parameters: string[], keyName: string): Promise { + await Promise.all(parameters.map(async name => { + try { + return ssm.addTagsToResource({ + ResourceId: name, + ResourceType: 'Parameter', + Tags: [{ + Key: keyName, + Value: 'true', + }], + }).promise(); + } catch (e) { + throw new Error(`Error importing ${name}: ${e}`); + } + })); +} + +/** + * Remove tags from parameters + */ +async function removeTags(ssm: SSM, parameters: string[], keyName: string): Promise { + await Promise.all(parameters.map(async name => { + try { + return ssm.removeTagsFromResource({ + TagKeys: [keyName], + ResourceType: 'Parameter', + ResourceId: name, + }).promise(); + } catch (e) { + switch (e.code) { + // if the parameter doesn't exist then there is nothing to release + case 'InvalidResourceId': + return; + default: + throw new Error(`Error releasing import ${name}: ${e}`); + } + } + })); +} + +/** + * Get all parameters in a given path + * + * If the request fails for any reason it will fail the custom resource event. + * Since this is only run when the resource is deleted that is probably the behavior + * that is desired. + */ +async function getParametersByPath(ssm: SSM, path: string, nextToken?: string): Promise { + const parameters: SSM.Parameter[] = []; + return ssm.getParametersByPath({ + Path: path, + NextToken: nextToken, + }).promise().then(async getParametersByPathResult => { + parameters.push(...getParametersByPathResult.Parameters ?? []); + if (getParametersByPathResult.NextToken) { + parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); + } + return parameters; + }); +} + +/** + * Delete all parameters in a give path + */ +async function deleteParametersByPath(ssm: SSM, path: string): Promise { + const allParams = await getParametersByPath(ssm, path); + const names = allParams.map(param => param.Name).filter(x => !!x) as string[]; + await ssm.deleteParameters({ + Names: names, + }).promise(); +} + +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source: string[], filter: string[]): string[] { + return source.filter(key => !filter.includes(key)); +} diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts index 8a829ea1a2cb4..69866b354da99 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts @@ -12,29 +12,19 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent switch (event.RequestType) { case 'Create': console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyExistingParameters(ssm, exports); + await throwIfAnyInUse(ssm, exports); await putParameters(ssm, exports); return; case 'Update': const oldProps = event.OldResourceProperties; const oldExports: CrossRegionExports = oldProps.Exports; const newExports = filterExports(exports, oldExports); - await throwIfAnyExistingParameters(ssm, newExports); - const paramsToDelete = filterExports(oldExports, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (Object.keys(paramsToDelete).length > 0) { - await ssm.deleteParameters({ - Names: Object.keys(paramsToDelete), - }).promise(); - } + await throwIfAnyInUse(ssm, newExports); console.info(`Creating new SSM Parameter exports in region ${props.Region}`); await putParameters(ssm, newExports); return; case 'Delete': - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(exports)), - }).promise(); + // consuming stack will delete parameters return; default: return; @@ -59,15 +49,41 @@ async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise< } /** - * Query for existing parameters + * Query for existing parameters that are in use */ -async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - const result = await ssm.getParameters({ - Names: Object.keys(parameters), - }).promise(); - if ((result.Parameters ?? []).length > 0) { - const existing = result.Parameters!.map(param => param.Name); - throw new Error(`Exports already exist: \n${existing.join('\n')}`); +async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { + const tagResults: Map> = new Map(); + await Promise.all(Object.keys(parameters).map(async (name: string) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'cdk-strong-ref') { + tagResults.has(name) + ? tagResults.get(name)!.add(tagParts[1]) + : tagResults.set(name, new Set([tagParts[1]])); + } + }); + + } catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + + })); + + if (tagResults.size > 0) { + const message: string = Object.entries(tagResults) + .map((result: [string, string[]]) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); } } @@ -79,7 +95,7 @@ async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExp */ function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) - .filter(key => !filter.hasOwnProperty(key)) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) .reduce((acc: CrossRegionExports, curr: string) => { acc[curr] = source[curr]; return acc; diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts new file mode 100644 index 0000000000000..546f52fc2c24e --- /dev/null +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts @@ -0,0 +1,91 @@ +import * as path from 'path'; +import { Construct } from 'constructs'; +import { CustomResource } from '../custom-resource'; +import { Lazy } from '../lazy'; +import { Stack } from '../stack'; +import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; +import { CfnResource } from '../cfn-resource'; + +export const SSM_EXPORT_PATH_PREFIX = 'cdk/exports/'; + +/** + * Properties for an ExportReader + */ +export interface ExportReaderProps {} + +/** + * Creates a custom resource that will return a list of stack imports from a given + * The export can then be referenced by the export name. + * + * @internal - this is intentionally not exported from core + */ +export class ExportReader extends Construct { + public static getOrCreate(scope: Construct, uniqueId: string, _props: ExportReaderProps = {}): ExportReader { + const stack = Stack.of(scope); + const existing = stack.node.tryFindChild(uniqueId); + return existing + ? existing as ExportReader + : new ExportReader(stack, uniqueId); + } + + private readonly importParametersNames: string[] = []; + private readonly customResource: CustomResource; + constructor(scope: Construct, id: string, _props: ExportReaderProps = {}) { + super(scope, id); + const stack = Stack.of(this); + + const resourceType = 'Custom::CrossRegionExportReader'; + const serviceToken = CustomResourceProvider.getOrCreate(this, resourceType, { + codeDirectory: path.join(__dirname, 'cross-region-ssm-reader-handler'), + runtime: CustomResourceProviderRuntime.NODEJS_14_X, + policyStatements: [{ + Effect: 'Allow', + Resource: stack.formatArn({ + service: 'ssm', + resource: 'parameter', + resourceName: `${SSM_EXPORT_PATH_PREFIX}${stack.stackName}/*`, + }), + Action: [ + 'ssm:DeleteParameters', + 'ssm:AddTagsToResource', + 'ssm:RemoveTagsFromResource', + 'ssm:GetParametersByPath', + 'ssm:GetParameters', + ], + }], + }); + + this.customResource = new CustomResource(this, 'Resource', { + resourceType: resourceType, + serviceToken, + properties: { + Region: stack.region, + StackName: stack.stackName, + Imports: Lazy.list({ produce: () => this.importParametersNames }), + }, + }); + } + + /** + * This is the only way to add a dependency on a custom resource currently + */ + public addDependency(resource: CfnResource): void { + const customResource = this.customResource.node.tryFindChild('Default'); + if (customResource && CfnResource.isCfnResource(customResource)) { + customResource.addDependsOn(resource); + } + } + + /** + * Register a reference with the writer and returns a CloudFormation Stack export by name + * + * The value will be "exported" via the ExportWriter. It will perform + * the export by creating an SSM parameter in the region that the consuming + * stack is created. + * + * @param exportName the unique name associated with the export + */ + public importValue(exportName: string): void { + this.importParametersNames.push(exportName); + } +} diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts index d6ef0d36d36a0..d6281a66c3557 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts @@ -4,9 +4,11 @@ import { CfnDynamicReference, CfnDynamicReferenceService } from '../cfn-dynamic- import { CustomResource } from '../custom-resource'; import { Lazy } from '../lazy'; import { Intrinsic } from '../private/intrinsic'; +import { makeUniqueId } from '../private/uniqueid'; import { Reference } from '../reference'; import { Stack } from '../stack'; import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; +import { ExportReader } from './export-reader-provider'; type CrossRegionExports = { [exportName: string]: string }; export const SSM_EXPORT_PATH_PREFIX = 'cdk/exports/'; @@ -14,7 +16,7 @@ export const SSM_EXPORT_PATH_PREFIX = 'cdk/exports/'; /** * Properties for an ExportReader */ -export interface ExportReaderProps { +export interface ExportWriterProps { /** * The AWS region to read Stack exports from * @@ -44,8 +46,17 @@ export interface ExportReaderProps { * @internal - this is intentionally not exported from core */ export class ExportWriter extends Construct { + public static getOrCreate(scope: Construct, uniqueId: string, props: ExportWriterProps): ExportWriter { + const stack = Stack.of(scope); + const existing = stack.node.tryFindChild(uniqueId); + return existing + ? existing as ExportWriter + : new ExportWriter(stack, uniqueId, { + region: props.region, + }); + } private readonly _references: CrossRegionExports = {}; - constructor(scope: Construct, id: string, props: ExportReaderProps) { + constructor(scope: Construct, id: string, props: ExportWriterProps) { super(scope, id); const stack = Stack.of(this); const region = props.region ?? stack.region; @@ -63,14 +74,14 @@ export class ExportWriter extends Construct { resourceName: `${SSM_EXPORT_PATH_PREFIX}*`, }), Action: [ + 'ssm:ListTagsForResource', 'ssm:GetParameters', 'ssm:PutParameter', - 'ssm:DeleteParameters', ], }], }); - new CustomResource(this, 'Default', { + new CustomResource(this, 'Resource', { resourceType: resourceType, serviceToken, properties: { @@ -91,10 +102,30 @@ export class ExportWriter extends Construct { * @param reference the value that will be exported * @returns a dynamic reference to an ssm parameter */ - public exportValue(exportName: string, reference: Reference): Intrinsic { + public exportValue(exportName: string, reference: Reference, importStack: Stack): Intrinsic { const stack = Stack.of(this); const parameterName = `/${SSM_EXPORT_PATH_PREFIX}${exportName}`; + + this.addToExportReader(parameterName, importStack); + this._references[parameterName] = stack.resolve(reference.toString()); return new CfnDynamicReference(CfnDynamicReferenceService.SSM, parameterName); } + + /** + * Add the export to the export reader which is created in the importing stack + */ + private addToExportReader(exportName: string, importStack: Stack): void { + const readerConstructName = makeUniqueId(['ExportsReader']); + const exportReader = ExportReader.getOrCreate(importStack.nestedStackParent ?? importStack, readerConstructName); + // if the reference is being imported into a nested stack we create the export reader + // in the parent stack and then add a dependency on the nested stack + // this ensures that the nested stack deploys and consumes the reference before + // the ExportReader is executed + if (importStack.nestedStackResource) { + exportReader.addDependency(importStack.nestedStackResource); + } + + exportReader.importValue(exportName); + } } diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index ce3c7e6275285..ed8006a978c47 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -111,6 +111,11 @@ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { // Stacks are in the same account, but different regions if (producerRegion !== consumerRegion && FeatureFlags.of(consumer).isEnabled(cxapi.ENABLE_CROSS_REGION_REFERENCES)) { + if (producerRegion === cxapi.UNKNOWN_REGION || consumerRegion === cxapi.UNKNOWN_REGION) { + throw new Error( + `Stack "${consumer.node.path}" cannot consume a cross reference from stack "${producer.node.path}". ` + + 'Cross stack/region references are only supported for stacks with an explicit region defined. '); + } consumer.addDependency(producer, `${consumer.node.path} -> ${reference.target.node.path}.${reference.displayName}`); return createCrossRegionImportValue(reference, consumer); @@ -202,39 +207,38 @@ function createImportValue(reference: Reference): Intrinsic { * Returns a reference to the ExportsReader attribute which contains the exported value */ function createCrossRegionImportValue(reference: Reference, importStack: Stack): Intrinsic { - const exportingStack = Stack.of(reference.target); + const referenceStack = Stack.of(reference.target); + const exportingStack = referenceStack.nestedStackParent ?? referenceStack; // generate an export name const exportable = getExportable(exportingStack, reference); const id = JSON.stringify(exportingStack.resolve(exportable)); - const exportName = generateExportName(exportingStack, reference, id); + const exportName = generateExportName(importStack, reference, id); if (Token.isUnresolved(exportName)) { throw new Error(`unresolved token in generated export name: ${JSON.stringify(exportingStack.resolve(exportName))}`); } // get or create the export writer - const constructName = makeUniqueId(['ExportsWriter', importStack.region]); - const existing = exportingStack.node.tryFindChild(constructName); - const exportReader = existing - ? existing as ExportWriter - : new ExportWriter(exportingStack, constructName, { - region: importStack.region, - }); - - return exportReader.exportValue(exportName, reference); + const writerConstructName = makeUniqueId(['ExportsWriter', importStack.region]); + const exportReader = ExportWriter.getOrCreate(exportingStack, writerConstructName, { + region: importStack.region, + }); + + return exportReader.exportValue(exportName, reference, importStack); } /** * Generate a unique physical name for the export */ -function generateExportName(stack: Stack, reference: Reference, id: string): string { +function generateExportName(importStack: Stack, reference: Reference, id: string): string { + const referenceStack = Stack.of(reference.target); + const components = [ - ...reference.target.node.scopes - .slice(stack.node.scopes.length) - .map(c => c.node.id), + referenceStack.stackName ?? '', + referenceStack.region, id, ]; - const prefix = stack.stackName + '-'; + const prefix = `${importStack.nestedStackParent?.stackName ?? importStack.stackName}/`; const localPart = makeUniqueId(components); // max name length for a system manager parameter is 1011 characters // including the arn, i.e. diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index 48863717741db..b8bd59b032b8d 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -216,11 +216,11 @@ describe('cross environment', () => { const template2 = assembly.getStackByName(stack2.stackName).template; expect(template1?.Resources).toMatchObject({ - 'ExportsWriterbermudatriangle42E5959427': { + 'ExportsWriterbermudatriangle42E59594276156AC73': { 'DeletionPolicy': 'Delete', 'Properties': { 'Exports': { - '/cdk/exports/Stack1-MyResourceRefMyResource6073B41F992B761C': { + '/cdk/exports/Stack2/Stack1bermudatriangle1337RefMyResource6073B41F66B72887': { 'Ref': 'MyResource6073B41F', }, }, @@ -238,7 +238,7 @@ describe('cross environment', () => { }); expect(template2?.Outputs).toEqual({ 'Output': { - 'Value': '{{resolve:ssm:/cdk/exports/Stack1-MyResourceRefMyResource6073B41F992B761C}}', + 'Value': '{{resolve:ssm:/cdk/exports/Stack2/Stack1bermudatriangle1337RefMyResource6073B41F66B72887}}', }, }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts new file mode 100644 index 0000000000000..d026bfbb184a6 --- /dev/null +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts @@ -0,0 +1,204 @@ +import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-reader-handler'; +import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/export-reader-provider'; + +let mockDeleteParameters: jest.Mock ; +let mockAddTagsToResource: jest.Mock; +let mockGetParametersByPath: jest.Mock; +let mockRemoveTagsFromResource: jest.Mock; +jest.mock('aws-sdk', () => { + return { + SSM: jest.fn(() => { + return { + deleteParameters: jest.fn((params) => { + return { + promise: () => mockDeleteParameters(params), + }; + }), + addTagsToResource: jest.fn((params) => { + return { + promise: () => mockAddTagsToResource(params), + }; + }), + removeTagsFromResource: jest.fn((params) => { + return { + promise: () => mockRemoveTagsFromResource(params), + }; + }), + getParametersByPath: jest.fn((params) => { + return { + promise: () => mockGetParametersByPath(params), + }; + }), + }; + }), + }; +}); +beforeEach(() => { + jest.spyOn(console, 'info').mockImplementation(() => {}); + jest.spyOn(console, 'error').mockImplementation(() => {}); + mockDeleteParameters = jest.fn(); + mockGetParametersByPath = jest.fn(); + mockRemoveTagsFromResource = jest.fn().mockImplementation(() => { return {}; }); + mockAddTagsToResource = jest.fn().mockImplementation(() => { + return {}; + }); +}); +afterEach(() => { + jest.restoreAllMocks(); +}); + +describe('cross-region-ssm-reader entrypoint', () => { + test('Create event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Imports: ['/cdk/exports/MyStack/MyExport'], + }, + }); + + // WHEN + await handler(event); + + // THEN + expect(mockAddTagsToResource).toHaveBeenCalledWith({ + ResourceId: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, + ResourceType: 'Parameter', + Tags: [{ + Key: 'cdk-strong-ref:MyStack', + Value: 'true', + }], + }); + expect(mockDeleteParameters).toHaveBeenCalledTimes(0); + }); + + test('Update event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + StackName: 'MyStack', + Imports: [ + '/cdk/exports/MyStack/ExistingExport', + ], + }, + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Imports: [ + '/cdk/exports/MyStack/ExistingExport', + '/cdk/exports/MyStack/MyExport', + ], + }, + }); + + // WHEN + await handler(event); + + // THEN + expect(mockAddTagsToResource).toHaveBeenCalledWith({ + ResourceId: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, + ResourceType: 'Parameter', + Tags: [{ + Key: 'cdk-strong-ref:MyStack', + Value: 'true', + }], + }); + expect(mockDeleteParameters).toHaveBeenCalledTimes(0); + expect(mockRemoveTagsFromResource).toHaveBeenCalledTimes(0); + expect(mockGetParametersByPath).toHaveBeenCalledTimes(0); + }); + + test('Update event with export removal', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + StackName: 'MyStack', + Imports: [ + '/cdk/exports/MyStack/RemovedExport', + ], + }, + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Imports: [ + '/cdk/exports/MyStack/MyExport', + ], + }, + }); + + // WHEN + await handler(event); + + // THEN + expect(mockAddTagsToResource).toHaveBeenCalledWith({ + ResourceId: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, + ResourceType: 'Parameter', + Tags: [{ + Key: 'cdk-strong-ref:MyStack', + Value: 'true', + }], + }); + expect(mockRemoveTagsFromResource).toHaveBeenCalledWith({ + ResourceId: '/cdk/exports/MyStack/RemovedExport', + ResourceType: 'Parameter', + TagKeys: ['cdk-strong-ref:MyStack'], + }); + expect(mockDeleteParameters).toHaveBeenCalledTimes(0); + }); + + test('Delete event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Delete', + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Imports: [ + '/cdk/exports/MyStack/RemovedExport', + ], + }, + }); + + // WHEN + mockGetParametersByPath.mockImplementationOnce(() => { + return Promise.resolve({ + Parameters: [{ + Name: '/cdk/exports/MyStack/OtherExport', + }], + }); + }); + await handler(event); + + // THEN + expect(mockDeleteParameters).toHaveBeenCalledTimes(1); + expect(mockDeleteParameters).toHaveBeenCalledWith({ + Names: ['/cdk/exports/MyStack/OtherExport'], + }); + }); +}); + +function makeEvent(req: Partial): AWSLambda.CloudFormationCustomResourceEvent { + return { + LogicalResourceId: '', + RequestId: '', + ResourceType: '', + ResponseURL: '', + ServiceToken: '', + StackId: '', + ResourceProperties: { + ServiceToken: '', + ...req.ResourceProperties, + }, + ...req, + } as any; +} diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts index 6227b4028bd51..18ac8bbe33bd9 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -2,8 +2,7 @@ import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-wri import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/export-writer-provider'; let mockPutParameter: jest.Mock ; -let mockGetParameters: jest.Mock; -let mockDeleteParameters: jest.Mock; +let mocklistTagsForResource: jest.Mock; jest.mock('aws-sdk', () => { return { SSM: jest.fn(() => { @@ -13,14 +12,9 @@ jest.mock('aws-sdk', () => { promise: () => mockPutParameter(params), }; }), - deleteParameters: jest.fn((params) => { + listTagsForResource: jest.fn((params) => { return { - promise: () => mockDeleteParameters(params), - }; - }), - getParameters: jest.fn((params) => { - return { - promise: () => mockGetParameters(params), + promise: () => mocklistTagsForResource(params), }; }), }; @@ -31,12 +25,10 @@ beforeEach(() => { jest.spyOn(console, 'info').mockImplementation(() => {}); jest.spyOn(console, 'error').mockImplementation(() => {}); mockPutParameter = jest.fn(); - mockGetParameters = jest.fn(); - mockDeleteParameters = jest.fn(); - mockPutParameter.mockImplementation(() => { + mocklistTagsForResource = jest.fn().mockImplementation(() => { return {}; }); - mockGetParameters.mockImplementation(() => { + mockPutParameter.mockImplementation(() => { return {}; }); }); @@ -60,16 +52,17 @@ describe('cross-region-ssm-writer throws', () => { }); // WHEN - mockGetParameters.mockImplementation(() => { + mocklistTagsForResource.mockImplementation(() => { return { - Parameters: [{ - Name: '/cdk/exports/MyStack/MyExport', + TagList: [{ + Key: 'cdk-strong-ref:MyStack', + Value: 'true', }], }; }); // THEN - await expect(handler(event)).rejects.toThrow(/Exports already exist/); + await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); }); test('update throws if params already exist', async () => { @@ -96,16 +89,59 @@ describe('cross-region-ssm-writer throws', () => { }); // WHEN - mockGetParameters.mockImplementation(() => { + mocklistTagsForResource.mockImplementation(() => { return { - Parameters: [{ - Name: '/cdk/exports/MyStack/AlreadyExists', + TagList: [{ + Key: 'cdk-strong-ref:MyStack', + Value: 'true', }], }; }); // THEN - await expect(handler(event)).rejects.toThrow(/Exports already exist/); + await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); + }); + + test('update throws if value changes for existing parameter', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'Original', + }, + }, + ResourceProperties: { + ServiceToken: '', + Region: 'us-east-1', + StackName: 'MyStack', + Exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'NewValue', + }, + }, + }); + + // WHEN + mocklistTagsForResource.mockImplementation((params) => { + expect(params).toEqual({ + ResourceId: '/cdk/exports/MyStack/AlreadyExists', + ResourceType: 'Parameter', + }); + return { + TagList: [{ + Key: 'cdk-strong-ref:MyStack', + Value: 'true', + }], + }; + }); + + // THEN + await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); }); }); @@ -134,33 +170,27 @@ describe('cross-region-ssm-writer entrypoint', () => { Type: 'String', }); expect(mockPutParameter).toHaveBeenCalledTimes(1); - expect(mockDeleteParameters).toHaveBeenCalledTimes(0); - expect(mockGetParameters).toHaveBeenCalledTimes(1); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); }); - test('Update event', async () => { + test('Create event does not throw for new parameters', async () => { // GIVEN const event = makeEvent({ - RequestType: 'Update', - OldResourceProperties: { - ServiceToken: '', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', - }, - }, + RequestType: 'Create', ResourceProperties: { ServiceToken: '', Region: 'us-east-1', StackName: 'MyStack', Exports: { - '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', '/cdk/exports/MyStack/MyExport': 'Value', }, }, }); // WHEN + mocklistTagsForResource.mockRejectedValue({ + code: 'InvalidResourceId', + }); await handler(event); // THEN @@ -170,11 +200,10 @@ describe('cross-region-ssm-writer entrypoint', () => { Type: 'String', }); expect(mockPutParameter).toHaveBeenCalledTimes(1); - expect(mockDeleteParameters).toHaveBeenCalledTimes(0); - expect(mockGetParameters).toHaveBeenCalledTimes(1); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); }); - test('Update event with delete', async () => { + test('Update event', async () => { // GIVEN const event = makeEvent({ RequestType: 'Update', @@ -182,7 +211,7 @@ describe('cross-region-ssm-writer entrypoint', () => { ServiceToken: '', StackName: 'MyStack', Exports: { - '/cdk/exports/MyStack/RemovedExport': 'MyRemovedValue', + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', }, }, ResourceProperties: { @@ -190,8 +219,8 @@ describe('cross-region-ssm-writer entrypoint', () => { Region: 'us-east-1', StackName: 'MyStack', Exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/MyOtherExport': 'MyOtherValue', }, }, }); @@ -205,18 +234,10 @@ describe('cross-region-ssm-writer entrypoint', () => { Value: 'Value', Type: 'String', }); - expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyOtherExport`, - Value: 'MyOtherValue', - Type: 'String', - }); - expect(mockDeleteParameters).toHaveBeenCalledWith({ - Names: ['/cdk/exports/MyStack/RemovedExport'], - }); - expect(mockPutParameter).toHaveBeenCalledTimes(2); - expect(mockDeleteParameters).toHaveBeenCalledTimes(1); - expect(mockGetParameters).toHaveBeenCalledTimes(1); + expect(mockPutParameter).toHaveBeenCalledTimes(1); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); }); + test('Delete event', async () => { // GIVEN const event = makeEvent({ @@ -235,12 +256,8 @@ describe('cross-region-ssm-writer entrypoint', () => { await handler(event); // THEN - expect(mockDeleteParameters).toHaveBeenCalledWith({ - Names: ['/cdk/exports/MyStack/RemovedExport'], - }); expect(mockPutParameter).toHaveBeenCalledTimes(0); - expect(mockDeleteParameters).toHaveBeenCalledTimes(1); - expect(mockGetParameters).toHaveBeenCalledTimes(0); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(0); }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index c1dd08af8778f..7346492f15e26 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -8,6 +8,7 @@ describe('export writer provider', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'Stack1'); + const stack2 = new Stack(app, 'Stack2'); const resource = new CfnResource(stack, 'MyResource', { type: 'Custom::MyResource', }); @@ -16,10 +17,11 @@ describe('export writer provider', () => { const exportWriter = new ExportWriter(stack, 'ExportWriter', { region: 'us-east-1', }); - const exportValue = exportWriter.exportValue('MyResourceName', resource.getAtt('arn')); + const exportValue = exportWriter.exportValue('MyResourceName', resource.getAtt('arn'), stack2); // THEN const cfn = toCloudFormation(stack); + const stack2Cfn = toCloudFormation(stack2); const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; const assetHash = staging.assetHash; @@ -50,9 +52,9 @@ describe('export writer provider', () => { Statement: [ { Action: [ + 'ssm:ListTagsForResource', 'ssm:GetParameters', 'ssm:PutParameter', - 'ssm:DeleteParameters', ], Effect: 'Allow', Resource: { @@ -85,7 +87,7 @@ describe('export writer provider', () => { ], }, }, - ExportWriter: { + ExportWriterA770449C: { DeletionPolicy: 'Delete', Properties: { Region: 'us-east-1', @@ -133,5 +135,116 @@ describe('export writer provider', () => { }, }, }); + expect(stack2Cfn).toEqual({ + Resources: { + CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + DependsOn: [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + ], + Properties: { + Code: { + S3Bucket: { + 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', + }, + S3Key: 'abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip', + }, + Handler: '__entrypoint__.handler', + MemorySize: 128, + Role: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + 'Arn', + ], + }, + Runtime: 'nodejs14.x', + Timeout: 900, + }, + Type: 'AWS::Lambda::Function', + }, + CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD: { + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + ManagedPolicyArns: [ + { + 'Fn::Sub': 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', + }, + ], + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssm:DeleteParameters', + 'ssm:AddTagsToResource', + 'ssm:RemoveTagsFromResource', + 'ssm:GetParametersByPath', + 'ssm:GetParameters', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':ssm:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':parameter/cdk/exports/Stack2/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Inline', + }, + ], + }, + Type: 'AWS::IAM::Role', + }, + ExportsReader8B249524: { + DeletionPolicy: 'Delete', + Properties: { + Imports: [ + '/cdk/exports/MyResourceName', + ], + Region: { + Ref: 'AWS::Region', + }, + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', + 'Arn', + ], + }, + StackName: 'Stack2', + }, + Type: 'Custom::CrossRegionExportReader', + UpdateReplacePolicy: 'Delete', + }, + }, + + }); }); }); diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 8228f5f7f987f..3ce5c150fc4c3 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -52,45 +52,75 @@ describe('nested-stack', () => { }, }); stack2.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); - const nestedStack = new NestedStack(stack1, 'MyNestedStack'); - const nestedStack2 = new NestedStack(stack2, 'MyNestedStack'); + const nestedStack = new NestedStack(stack1, 'Nested1'); + const nestedStack2 = new NestedStack(stack2, 'Nested2'); // WHEN - const myResource = new MyResource(nestedStack, 'MyResource'); + const myResource = new MyResource(nestedStack, 'Resource1'); - new CfnOutput(nestedStack2, 'Output', { - value: myResource.name, + new CfnResource(nestedStack2, 'Resource2', { + type: 'My::Resource', + properties: { + Prop1: myResource.name, + }, }); // THEN const assembly = app.synth(); const nestedTemplate2 = JSON.parse(readFileSync(path.join(assembly.directory, `${nestedStack2.artifactId}.nested.template.json`), 'utf8')); expect(nestedTemplate2).toMatchObject({ - Outputs: { - Output: { - Value: '{{resolve:ssm:/cdk/exports/Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D}}', + Resources: { + Resource2: { + Properties: { + Prop1: '{{resolve:ssm:/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E}}', + }, + Type: 'My::Resource', }, }, }); + const template2 = assembly.getStackByName(stack2.stackName).template; + expect(template2?.Resources).toMatchObject({ + ExportsReader8B249524: { + DeletionPolicy: 'Delete', + Properties: { + Imports: [ + '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E', + ], + Region: 'bermuda-triangle-42', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', + 'Arn', + ], + }, + StackName: 'Stack2', + }, + DependsOn: [ + 'Nested2NestedStackNested2NestedStackResource877A1112', + ], + Type: 'Custom::CrossRegionExportReader', + UpdateReplacePolicy: 'Delete', + }, + }); const template1 = assembly.getStackByName(stack1.stackName).template; const nestedTemplate1 = JSON.parse(readFileSync(path.join(assembly.directory, `${nestedStack.artifactId}.nested.template.json`), 'utf8')); expect(nestedTemplate1?.Outputs).toEqual({ - Stack1MyNestedStackMyResourceEDA18296Ref: { + Stack1Nested1Resource178AEB067Ref: { Value: { - Ref: 'MyResource6073B41F', + Ref: 'Resource1CCD41AB7', }, }, }); expect(template1?.Resources).toMatchObject({ - ExportsWriterbermudatriangle42E5959427: { + ExportsWriterbermudatriangle42E59594276156AC73: { DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1-MyNestedStackNestedStackMyNestedStackNestedStackResourceFnGetAttMyNestedStackNestedStackMyNestedStackNestedStackResource9C617903OutputsStack1MyNestedStackMyResourceEDA18296Ref94C8BA6D': { + '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E': { 'Fn::GetAtt': [ - 'MyNestedStackNestedStackMyNestedStackNestedStackResource9C617903', - 'Outputs.Stack1MyNestedStackMyResourceEDA18296Ref', + 'Nested1NestedStackNested1NestedStackResourceCD0AD36B', + 'Outputs.Stack1Nested1Resource178AEB067Ref', ], }, }, diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 9a40c71bc16f3..fad576fbdbcbc 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -490,12 +490,12 @@ describe('stack', () => { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - ExportsWriteruseast2828FA26B: { + ExportsWriteruseast2828FA26B86FBEFA7: { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', @@ -519,7 +519,7 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', + Name: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F}}', }, }, }, @@ -581,12 +581,73 @@ describe('stack', () => { // THEN expect(template2).toMatchObject({ Resources: { + CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD: { + Properties: { + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssm:DeleteParameters', + 'ssm:AddTagsToResource', + 'ssm:RemoveTagsFromResource', + 'ssm:GetParametersByPath', + 'ssm:GetParameters', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':ssm:us-east-2:', + { + Ref: 'AWS::AccountId', + }, + ':parameter/cdk/exports/Stack2/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Inline', + }, + ], + }, + Type: 'AWS::IAM::Role', + }, + ExportsReader8B249524: { + DeletionPolicy: 'Delete', + Properties: { + Imports: [ + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1', + '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B', + ], + Region: 'us-east-2', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', + 'Arn', + ], + }, + StackName: 'Stack2', + }, + Type: 'Custom::CrossRegionExportReader', + UpdateReplacePolicy: 'Delete', + }, SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', - Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', + Name: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F}}', + Other: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B}}', }, }, }, @@ -596,12 +657,12 @@ describe('stack', () => { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - ExportsWriteruseast2828FA26B: { + ExportsWriteruseast2828FA26B86FBEFA7: { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other2', @@ -624,18 +685,18 @@ describe('stack', () => { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - ExportsWriteruseast2828FA26B: { + ExportsWriteruseast2828FA26B86FBEFA7: { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', ], }, - '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other', @@ -697,9 +758,9 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914}}', - Other: '{{resolve:ssm:/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5}}', + Name: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F}}', + Other: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1}}', + Other2: '{{resolve:ssm:/cdk/exports/Stack2/Stack3uswest1FnGetAttSomeResourceExportother2491B5DA7}}', }, }, }, @@ -709,12 +770,12 @@ describe('stack', () => { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - ExportsWriteruseast2828FA26B: { + ExportsWriteruseast2828FA26B86FBEFA7: { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack3-SomeResourceExportFnGetAttSomeResourceExportother297A3A2C5': { + '/cdk/exports/Stack2/Stack3uswest1FnGetAttSomeResourceExportother2491B5DA7': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other2', @@ -737,18 +798,18 @@ describe('stack', () => { SomeResourceExport: { Type: 'AWS::S3::Bucket', }, - ExportsWriteruseast2828FA26B: { + ExportsWriteruseast2828FA26B86FBEFA7: { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { Exports: { - '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportname1C71E914': { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { 'Fn::GetAtt': [ 'SomeResourceExport', 'name', ], }, - '/cdk/exports/Stack1-SomeResourceExportFnGetAttSomeResourceExportotherB49CE033': { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1': { 'Fn::GetAtt': [ 'SomeResourceExport', 'other', From b02046d902d99796e0101c2b8bde4100075c6f8d Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 30 Sep 2022 19:12:51 +0000 Subject: [PATCH 18/30] updating docs --- packages/@aws-cdk/core/README.md | 6 +- .../export-writer-provider.test.ts | 256 +++++++++++++++++- packages/aws-cdk-lib/README.md | 13 +- packages/aws-cdk-lib/package.json | 3 + 4 files changed, 272 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index cbb622ec25fed..28f317e533963 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -189,10 +189,14 @@ new cloudfront.Distribution(stack2, 'Distribution', { When the AWS CDK determines that the resource is in a different stack _and_ is in a different region, it will "export" the value by creating a custom resource in the producing stack which creates SSM Parameters in the consuming region for each exported value. The parameters will be -created with the name '/cdk/exports/${export-name}'. +created with the name '/cdk/exports/${consumingStackName}/${export-name}'. In order to "import" the exports into the consuming stack a [SSM Dynamic reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-ssm) is used to reference the SSM parameter which was created. +In order to mimic strong references, a Custom Resource is also created in the consuming +stack which marks the SSM parameters as being "imported". When a parameter has been successfully +imported, the producing stack cannot update the value. + ### Removing automatic cross-stack references The automatic references created by CDK when you use resources across stacks diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index 7346492f15e26..683fea1ccb908 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -1,4 +1,4 @@ -import { App, Stack, AssetStaging, CfnResource } from '../../lib'; +import { App, Stack, AssetStaging, CfnResource, NestedStack } from '../../lib'; import { ExportWriter } from '../../lib/custom-resource-provider/export-writer-provider'; import { toCloudFormation } from '../util'; @@ -244,7 +244,261 @@ describe('export writer provider', () => { UpdateReplacePolicy: 'Delete', }, }, + }); + }); + + test('when consumer is a nested stack, ExportReader is created in the parent stack', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'Stack1'); + const stack2 = new Stack(app, 'Stack2'); + const nested2 = new NestedStack(stack2, 'Nested1'); + const resource = new CfnResource(stack, 'MyResource', { + type: 'Custom::MyResource', + }); + + // WHEN + const exportWriter = new ExportWriter(stack, 'ExportWriter', { + region: 'us-east-1', + }); + const exportValue = exportWriter.exportValue('MyResourceName', resource.getAtt('arn'), nested2); + // THEN + const cfn = toCloudFormation(stack); + const stack2Cfn = toCloudFormation(stack2); + const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; + const assetHash = staging.assetHash; + + expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/MyResourceName}}'); + expect(cfn).toEqual({ + Resources: { + MyResource: { + Type: 'Custom::MyResource', + }, + CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1: { + Type: 'AWS::IAM::Role', + Properties: { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + }, + ], + }, + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssm:ListTagsForResource', + 'ssm:GetParameters', + 'ssm:PutParameter', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':ssm:us-east-1:', + { + Ref: 'AWS::AccountId', + }, + ':parameter/cdk/exports/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Inline', + }, + ], + ManagedPolicyArns: [ + { + 'Fn::Sub': 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', + }, + ], + }, + }, + ExportWriterA770449C: { + DeletionPolicy: 'Delete', + Properties: { + Region: 'us-east-1', + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', + 'Arn', + ], + }, + Exports: { + '/cdk/exports/MyResourceName': { + 'Fn::GetAtt': [ + 'MyResource', + 'arn', + ], + }, + }, + }, + Type: 'Custom::CrossRegionExportWriter', + UpdateReplacePolicy: 'Delete', + }, + CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A: { + Type: 'AWS::Lambda::Function', + Properties: { + Code: { + S3Bucket: { + 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', + }, + S3Key: `${assetHash}.zip`, + }, + Timeout: 900, + MemorySize: 128, + Handler: '__entrypoint__.handler', + Role: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1', + 'Arn', + ], + }, + Runtime: 'nodejs14.x', + }, + DependsOn: [ + 'CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1', + ], + }, + }, + }); + expect(stack2Cfn).toEqual({ + Resources: { + CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68: { + DependsOn: [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + ], + Properties: { + Code: { + S3Bucket: { + 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', + }, + S3Key: 'abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip', + }, + Handler: '__entrypoint__.handler', + MemorySize: 128, + Role: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD', + 'Arn', + ], + }, + Runtime: 'nodejs14.x', + Timeout: 900, + }, + Type: 'AWS::Lambda::Function', + }, + CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD: { + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + ManagedPolicyArns: [ + { + 'Fn::Sub': 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', + }, + ], + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Action: [ + 'ssm:DeleteParameters', + 'ssm:AddTagsToResource', + 'ssm:RemoveTagsFromResource', + 'ssm:GetParametersByPath', + 'ssm:GetParameters', + ], + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':ssm:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':parameter/cdk/exports/Stack2/*', + ], + ], + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'Inline', + }, + ], + }, + Type: 'AWS::IAM::Role', + }, + ExportsReader8B249524: { + DeletionPolicy: 'Delete', + Properties: { + Imports: [ + '/cdk/exports/MyResourceName', + ], + Region: { + Ref: 'AWS::Region', + }, + ServiceToken: { + 'Fn::GetAtt': [ + 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', + 'Arn', + ], + }, + StackName: 'Stack2', + }, + Type: 'Custom::CrossRegionExportReader', + UpdateReplacePolicy: 'Delete', + DependsOn: [ + 'Nested1NestedStackNested1NestedStackResourceCD0AD36B', + ], + }, + Nested1NestedStackNested1NestedStackResourceCD0AD36B: { + DeletionPolicy: 'Delete', + Properties: { + TemplateURL: '', + }, + Type: 'AWS::CloudFormation::Stack', + UpdateReplacePolicy: 'Delete', + }, + }, }); }); }); diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index e305ad50cdec8..6f5eb7ba8fcaf 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -218,10 +218,15 @@ new cloudfront.Distribution(stack2, 'Distribution', { ``` When the AWS CDK determines that the resource is in a different stack _and_ is in a different -region, it automatically synthesizes AWS -CloudFormation [Exports](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html) -in the producing stack. In order to "import" the exports into the consuming stack a CloudFormation -Custom Resource is created which "imports" the values from the cross region stack. +region, it will "export" the value by creating a custom resource in the producing stack which +creates SSM Parameters in the consuming region for each exported value. The parameters will be +created with the name '/cdk/exports/${consumingStackName}/${export-name}'. +In order to "import" the exports into the consuming stack a [SSM Dynamic reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-ssm) +is used to reference the SSM parameter which was created. + +In order to mimic strong references, a Custom Resource is also created in the consuming +stack which marks the SSM parameters as being "imported". When a parameter has been successfully +imported, the producing stack cannot update the value. ### Removing automatic cross-stack references diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 9b4c336db9c08..570249e0a1017 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -450,6 +450,7 @@ "./aws-cognito": "./aws-cognito/index.js", "./aws-config": "./aws-config/index.js", "./aws-connect": "./aws-connect/index.js", + "./aws-controltower": "./aws-controltower/index.js", "./aws-cur": "./aws-cur/index.js", "./aws-customerprofiles": "./aws-customerprofiles/index.js", "./aws-databrew": "./aws-databrew/index.js", @@ -536,6 +537,7 @@ "./aws-lookoutequipment": "./aws-lookoutequipment/index.js", "./aws-lookoutmetrics": "./aws-lookoutmetrics/index.js", "./aws-lookoutvision": "./aws-lookoutvision/index.js", + "./aws-m2": "./aws-m2/index.js", "./aws-macie": "./aws-macie/index.js", "./aws-managedblockchain": "./aws-managedblockchain/index.js", "./aws-mediaconnect": "./aws-mediaconnect/index.js", @@ -602,6 +604,7 @@ "./aws-sso": "./aws-sso/index.js", "./aws-stepfunctions": "./aws-stepfunctions/index.js", "./aws-stepfunctions-tasks": "./aws-stepfunctions-tasks/index.js", + "./aws-supportapp": "./aws-supportapp/index.js", "./aws-synthetics": "./aws-synthetics/index.js", "./aws-timestream": "./aws-timestream/index.js", "./aws-transfer": "./aws-transfer/index.js", From b833df289767a4c58ff4d41f02b5cab1b7f775b5 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 30 Sep 2022 19:36:38 +0000 Subject: [PATCH 19/30] fixing integ tests --- .../index.js | 88 -------------- .../__entrypoint__.js | 0 .../index.d.ts | 0 .../index.js | 102 +++++++++++++++++ .../index.ts | 60 ++++++---- .../cross-region-consumer.assets.json | 22 +++- .../cross-region-consumer.template.json | 108 +++++++++++++++++- .../cross-region-producer.assets.json | 10 +- .../cross-region-producer.template.json | 12 +- ...erIntegNested815BEF8A.nested.template.json | 4 +- .../manifest.json | 26 ++++- .../index.js | 88 -------------- .../__entrypoint__.js | 0 .../index.d.ts | 0 .../index.js | 102 +++++++++++++++++ .../index.ts | 60 ++++++---- .../integ-pipeline-consumer-stack.assets.json | 18 ++- ...nteg-pipeline-consumer-stack.template.json | 101 +++++++++++++++- .../integ-pipeline-producer-stack.assets.json | 10 +- ...nteg-pipeline-producer-stack.template.json | 12 +- .../manifest.json | 26 ++++- 21 files changed, 586 insertions(+), 263 deletions(-) delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js rename packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/{asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4 => asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc}/__entrypoint__.js (100%) rename packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/{asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4 => asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js rename packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/{asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4 => asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc}/index.ts (57%) delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4 => asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc}/__entrypoint__.js (100%) rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4 => asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4 => asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc}/index.ts (57%) diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js deleted file mode 100644 index 9bc3257fb6397..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties; - const exports = props.Exports; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyExistingParameters(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); - await throwIfAnyExistingParameters(ssm, newExports); - const paramsToDelete = filterExports(oldExports, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (Object.keys(paramsToDelete).length > 0) { - await ssm.deleteParameters({ - Names: Object.keys(paramsToDelete), - }).promise(); - } - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(exports)), - }).promise(); - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters - */ -async function throwIfAnyExistingParameters(ssm, parameters) { - const result = await ssm.getParameters({ - Names: Object.keys(parameters), - }).promise(); - if ((result.Parameters ?? []).length > 0) { - const existing = result.Parameters.map(param => param.Name); - throw new Error(`Exports already exist: \n${existing.join('\n')}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function filterExports(source, filter) { - return Object.keys(source) - .filter(key => !filter.hasOwnProperty(key)) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSw0QkFBNEIsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2pELE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMscUJBQXFCLENBQUM7Z0JBQzdDLE1BQU0sVUFBVSxHQUF1QixRQUFRLENBQUMsT0FBTyxDQUFDO2dCQUN4RCxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsT0FBTyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN0RCxNQUFNLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDcEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUks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IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgYXdhaXQgcHV0UGFyYW1ldGVycyhzc20sIGV4cG9ydHMpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogQ3Jvc3NSZWdpb25FeHBvcnRzID0gb2xkUHJvcHMuRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGZpbHRlckV4cG9ydHMoZXhwb3J0cywgb2xkRXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgY29uc3QgcGFyYW1zVG9EZWxldGUgPSBmaWx0ZXJFeHBvcnRzKG9sZEV4cG9ydHMsIGV4cG9ydHMpO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIHVudXNlZCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgICBOYW1lczogT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgYWxsIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHNzbS5kZWxldGVQYXJhbWV0ZXJzKHtcbiAgICAgICAgICBOYW1lczogQXJyYXkuZnJvbShPYmplY3Qua2V5cyhleHBvcnRzKSksXG4gICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByZXN1bHQgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLFxuICB9KS5wcm9taXNlKCk7XG4gIGlmICgocmVzdWx0LlBhcmFtZXRlcnMgPz8gW10pLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBleGlzdGluZyA9IHJlc3VsdC5QYXJhbWV0ZXJzIS5tYXAocGFyYW0gPT4gcGFyYW0uTmFtZSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBFeHBvcnRzIGFscmVhZHkgZXhpc3Q6IFxcbiR7ZXhpc3Rpbmcuam9pbignXFxuJyl9YCk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZXR1cm4gb25seSB0aGUgaXRlbXMgZnJvbSBzb3VyY2UgdGhhdCBkbyBub3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICpcbiAqIEBwYXJhbSBzb3VyY2UgdGhlIHNvdXJjZSBvYmplY3QgdG8gcGVyZm9ybSB0aGUgZmlsdGVyIG9uXG4gKiBAcGFyYW0gZmlsdGVyIGZpbHRlciBvdXQgaXRlbXMgdGhhdCBleGlzdCBpbiB0aGlzIG9iamVjdFxuICovXG5mdW5jdGlvbiBmaWx0ZXJFeHBvcnRzKHNvdXJjZTogQ3Jvc3NSZWdpb25FeHBvcnRzLCBmaWx0ZXI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IENyb3NzUmVnaW9uRXhwb3J0cyB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhzb3VyY2UpXG4gICAgLmZpbHRlcihrZXkgPT4gIWZpbHRlci5oYXNPd25Qcm9wZXJ0eShrZXkpKVxuICAgIC5yZWR1Y2UoKGFjYzogQ3Jvc3NSZWdpb25FeHBvcnRzLCBjdXJyOiBzdHJpbmcpID0+IHtcbiAgICAgIGFjY1tjdXJyXSA9IHNvdXJjZVtjdXJyXTtcbiAgICAgIHJldHVybiBhY2M7XG4gICAgfSwge30pO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js new file mode 100644 index 0000000000000..a487b651f8d38 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js @@ -0,0 +1,102 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties; + const exports = props.Exports; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // consuming stack will delete parameters + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'cdk-strong-ref') { + tagResults.has(name) + ? tagResults.get(name).add(tagParts[1]) + : tagResults.set(name, new Set([tagParts[1]])); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNwQyxNQUFNLGFBQWEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2xDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDO2dCQUM3QyxNQUFNLFVBQVUsR0FBdUIsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDeEQsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN2QyxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxhQUFhLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFR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SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztTQUN2QixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDM0UsTUFBTSxDQUFDLENBQUMsR0FBdUIsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUNoRCxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ1gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSBvbGRQcm9wcy5FeHBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdjZGstc3Ryb25nLXJlZicpIHtcbiAgICAgICAgICB0YWdSZXN1bHRzLmhhcyhuYW1lKVxuICAgICAgICAgICAgPyB0YWdSZXN1bHRzLmdldChuYW1lKSEuYWRkKHRhZ1BhcnRzWzFdKVxuICAgICAgICAgICAgOiB0YWdSZXN1bHRzLnNldChuYW1lLCBuZXcgU2V0KFt0YWdQYXJ0c1sxXV0pKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBhbiBJbnZhbGlkUmVzb3VyY2VJZCBtZWFucyB0aGF0IHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdFxuICAgICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgICAgaWYgKGUuY29kZSA9PT0gJ0ludmFsaWRSZXNvdXJjZUlkJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cblxuICB9KSk7XG5cbiAgaWYgKHRhZ1Jlc3VsdHMuc2l6ZSA+IDApIHtcbiAgICBjb25zdCBtZXNzYWdlOiBzdHJpbmcgPSBPYmplY3QuZW50cmllcyh0YWdSZXN1bHRzKVxuICAgICAgLm1hcCgocmVzdWx0OiBbc3RyaW5nLCBzdHJpbmdbXV0pID0+IGAke3Jlc3VsdFswXX0gaXMgaW4gdXNlIGJ5IHN0YWNrKHMpICR7cmVzdWx0WzFdLmpvaW4oJyAnKX1gKVxuICAgICAgLmpvaW4oJ1xcbicpO1xuICAgIHRocm93IG5ldyBFcnJvcihgRXhwb3J0cyBjYW5ub3QgYmUgdXBkYXRlZDogXFxuJHttZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts similarity index 57% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts index 8a829ea1a2cb4..69866b354da99 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts @@ -12,29 +12,19 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent switch (event.RequestType) { case 'Create': console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyExistingParameters(ssm, exports); + await throwIfAnyInUse(ssm, exports); await putParameters(ssm, exports); return; case 'Update': const oldProps = event.OldResourceProperties; const oldExports: CrossRegionExports = oldProps.Exports; const newExports = filterExports(exports, oldExports); - await throwIfAnyExistingParameters(ssm, newExports); - const paramsToDelete = filterExports(oldExports, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (Object.keys(paramsToDelete).length > 0) { - await ssm.deleteParameters({ - Names: Object.keys(paramsToDelete), - }).promise(); - } + await throwIfAnyInUse(ssm, newExports); console.info(`Creating new SSM Parameter exports in region ${props.Region}`); await putParameters(ssm, newExports); return; case 'Delete': - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(exports)), - }).promise(); + // consuming stack will delete parameters return; default: return; @@ -59,15 +49,41 @@ async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise< } /** - * Query for existing parameters + * Query for existing parameters that are in use */ -async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - const result = await ssm.getParameters({ - Names: Object.keys(parameters), - }).promise(); - if ((result.Parameters ?? []).length > 0) { - const existing = result.Parameters!.map(param => param.Name); - throw new Error(`Exports already exist: \n${existing.join('\n')}`); +async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { + const tagResults: Map> = new Map(); + await Promise.all(Object.keys(parameters).map(async (name: string) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'cdk-strong-ref') { + tagResults.has(name) + ? tagResults.get(name)!.add(tagParts[1]) + : tagResults.set(name, new Set([tagParts[1]])); + } + }); + + } catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + + })); + + if (tagResults.size > 0) { + const message: string = Object.entries(tagResults) + .map((result: [string, string[]]) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); } } @@ -79,7 +95,7 @@ async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExp */ function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) - .filter(key => !filter.hasOwnProperty(key)) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) .reduce((acc: CrossRegionExports, curr: string) => { acc[curr] = source[curr]; return acc; diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json index 0b1381df8b5ff..4bff56202abef 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -1,7 +1,21 @@ { "version": "21.0.0", "files": { - "61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404": { + "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915": { + "source": { + "path": "asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915", + "packaging": "zip" + }, + "destinations": { + "current_account-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", + "objectKey": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" + } + } + }, + "8951083c42b14c02bc23ef787fdc9dd81379182eba5b19431fb87aae9824399c": { "source": { "path": "crossregionconsumerIntegNested815BEF8A.nested.template.json", "packaging": "file" @@ -9,13 +23,13 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404.json", + "objectKey": "8951083c42b14c02bc23ef787fdc9dd81379182eba5b19431fb87aae9824399c.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "073479a1920f44222959fc9231ad62d389ab3fe14dc0a6cdc14a3f1f54fb8dfc": { + "f81d7674cf434dae5287040174f1f3f91843c4d8385d3b6b322b6ffbe47ae1f7": { "source": { "path": "cross-region-consumer.template.json", "packaging": "file" @@ -23,7 +37,7 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "073479a1920f44222959fc9231ad62d389ab3fe14dc0a6cdc14a3f1f54fb8dfc.json", + "objectKey": "f81d7674cf434dae5287040174f1f3f91843c4d8385d3b6b322b6ffbe47ae1f7.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json index 64beb86477802..2ab0e7b2fbc18 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -15,7 +15,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "/61c5467a13581c441143dacb7d8878bb691678bb777668c0d4bbf28d05538404.json" + "/8951083c42b14c02bc23ef787fdc9dd81379182eba5b19431fb87aae9824399c.json" ] ] } @@ -27,7 +27,7 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429}}", + "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B}}", "Name": "integ-parameter0" } }, @@ -35,9 +35,111 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698}}", + "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E}}", "Name": "integ-parameter1" } + }, + "ExportsReader8B249524": { + "Type": "Custom::CrossRegionExportReader", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", + "Arn" + ] + }, + "Region": "us-east-2", + "StackName": "cross-region-consumer", + "Imports": [ + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" + ] + }, + "DependsOn": [ + "IntegNestedNestedStackIntegNestedNestedStackResource168C5881" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:us-east-2:", + { + "Ref": "AWS::AccountId" + }, + ":parameter/cdk/exports/cross-region-consumer/*" + ] + ] + }, + "Action": [ + "ssm:DeleteParameters", + "ssm:AddTagsToResource", + "ssm:RemoveTagsFromResource", + "ssm:GetParametersByPath", + "ssm:GetParameters" + ] + } + ] + } + } + ] + } + }, + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" + }, + "S3Key": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json index cc754585b35fd..f9d782c1a9ba4 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -1,15 +1,15 @@ { "version": "21.0.0", "files": { - "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4": { + "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc": { "source": { - "path": "asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4", + "path": "asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip", + "objectKey": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } @@ -29,7 +29,7 @@ } } }, - "7a3fc43a5189fb6d6a3bfc72db7aa7ac212db1e56232e932ed0fef91147422ba": { + "390176c29eb237a2593e7747bd0a888561316b8ef0e3f6bc79cab1169443e61d": { "source": { "path": "cross-region-producer.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "7a3fc43a5189fb6d6a3bfc72db7aa7ac212db1e56232e932ed0fef91147422ba.json", + "objectKey": "390176c29eb237a2593e7747bd0a888561316b8ef0e3f6bc79cab1169443e61d.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json index 9cb0a09c980e8..a9373164e5cd8 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -28,7 +28,7 @@ "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "ExportsWriteruseast2828FA26B": { + "ExportsWriteruseast2828FA26B86FBEFA7": { "Type": "Custom::CrossRegionExportWriter", "Properties": { "ServiceToken": { @@ -39,13 +39,13 @@ }, "Region": "us-east-2", "Exports": { - "/cdk/exports/cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429": { + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B": { "Fn::GetAtt": [ "IntegQueue3A18718A", "QueueName" ] }, - "/cdk/exports/cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698": { + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E": { "Fn::GetAtt": [ "IntegNestedNestedStackIntegNestedNestedStackResource168C5881", "Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" @@ -97,9 +97,9 @@ ] }, "Action": [ + "ssm:ListTagsForResource", "ssm:GetParameters", - "ssm:PutParameter", - "ssm:DeleteParameters" + "ssm:PutParameter" ] } ] @@ -115,7 +115,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip" + "S3Key": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json index 616364407930f..0086d1fcae47f 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json @@ -4,7 +4,7 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegQueueFnGetAttIntegQueue3A18718AQueueName6E52E429}}", + "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B}}", "Name": "integ-nested-parameter0" } }, @@ -12,7 +12,7 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-producer-IntegNestedNestedStackIntegNestedNestedStackResourceFnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC3296698}}", + "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E}}", "Name": "integ-nested-parameter1" } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index eda8d9769ed06..f76eb244585da 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/7a3fc43a5189fb6d6a3bfc72db7aa7ac212db1e56232e932ed0fef91147422ba.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/390176c29eb237a2593e7747bd0a888561316b8ef0e3f6bc79cab1169443e61d.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -57,10 +57,10 @@ "data": "IntegQueue3A18718A" } ], - "/cross-region-producer/ExportsWriteruseast2828FA26B/Default/Default": [ + "/cross-region-producer/ExportsWriteruseast2828FA26B/Resource/Default": [ { "type": "aws:cdk:logicalId", - "data": "ExportsWriteruseast2828FA26B" + "data": "ExportsWriteruseast2828FA26B86FBEFA7" } ], "/cross-region-producer/Custom::CrossRegionExportWriterCustomResourceProvider/Role": [ @@ -106,7 +106,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/073479a1920f44222959fc9231ad62d389ab3fe14dc0a6cdc14a3f1f54fb8dfc.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/f81d7674cf434dae5287040174f1f3f91843c4d8385d3b6b322b6ffbe47ae1f7.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -153,6 +153,24 @@ "data": "IntegParameter1EDBEF1C6" } ], + "/cross-region-consumer/ExportsReader/Resource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsReader8B249524" + } + ], + "/cross-region-consumer/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + } + ], + "/cross-region-consumer/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" + } + ], "/cross-region-consumer/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js deleted file mode 100644 index 9bc3257fb6397..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties; - const exports = props.Exports; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyExistingParameters(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); - await throwIfAnyExistingParameters(ssm, newExports); - const paramsToDelete = filterExports(oldExports, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (Object.keys(paramsToDelete).length > 0) { - await ssm.deleteParameters({ - Names: Object.keys(paramsToDelete), - }).promise(); - } - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(exports)), - }).promise(); - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters - */ -async function throwIfAnyExistingParameters(ssm, parameters) { - const result = await ssm.getParameters({ - Names: Object.keys(parameters), - }).promise(); - if ((result.Parameters ?? []).length > 0) { - const existing = result.Parameters.map(param => param.Name); - throw new Error(`Exports already exist: \n${existing.join('\n')}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function filterExports(source, filter) { - return Object.keys(source) - .filter(key => !filter.hasOwnProperty(key)) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSw0QkFBNEIsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2pELE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMscUJBQXFCLENBQUM7Z0JBQzdDLE1BQU0sVUFBVSxHQUF1QixRQUFRLENBQUMsT0FBTyxDQUFDO2dCQUN4RCxNQUFNLFVBQVUsR0FBRyxhQUFhLENBQUMsT0FBTyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN0RCxNQUFNLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDcEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyxtREFBbUQsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQ2hGLElBQUks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IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgYXdhaXQgcHV0UGFyYW1ldGVycyhzc20sIGV4cG9ydHMpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogQ3Jvc3NSZWdpb25FeHBvcnRzID0gb2xkUHJvcHMuRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGZpbHRlckV4cG9ydHMoZXhwb3J0cywgb2xkRXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgY29uc3QgcGFyYW1zVG9EZWxldGUgPSBmaWx0ZXJFeHBvcnRzKG9sZEV4cG9ydHMsIGV4cG9ydHMpO1xuICAgICAgICBjb25zb2xlLmluZm8oYERlbGV0aW5nIHVudXNlZCBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgICBOYW1lczogT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLFxuICAgICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zb2xlLmluZm8oYENyZWF0aW5nIG5ldyBTU00gUGFyYW1ldGVyIGV4cG9ydHMgaW4gcmVnaW9uICR7cHJvcHMuUmVnaW9ufWApO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgRGVsZXRpbmcgYWxsIFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHNzbS5kZWxldGVQYXJhbWV0ZXJzKHtcbiAgICAgICAgICBOYW1lczogQXJyYXkuZnJvbShPYmplY3Qua2V5cyhleHBvcnRzKSksXG4gICAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHRocm93SWZBbnlFeGlzdGluZ1BhcmFtZXRlcnMoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByZXN1bHQgPSBhd2FpdCBzc20uZ2V0UGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLFxuICB9KS5wcm9taXNlKCk7XG4gIGlmICgocmVzdWx0LlBhcmFtZXRlcnMgPz8gW10pLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBleGlzdGluZyA9IHJlc3VsdC5QYXJhbWV0ZXJzIS5tYXAocGFyYW0gPT4gcGFyYW0uTmFtZSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBFeHBvcnRzIGFscmVhZHkgZXhpc3Q6IFxcbiR7ZXhpc3Rpbmcuam9pbignXFxuJyl9YCk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZXR1cm4gb25seSB0aGUgaXRlbXMgZnJvbSBzb3VyY2UgdGhhdCBkbyBub3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICpcbiAqIEBwYXJhbSBzb3VyY2UgdGhlIHNvdXJjZSBvYmplY3QgdG8gcGVyZm9ybSB0aGUgZmlsdGVyIG9uXG4gKiBAcGFyYW0gZmlsdGVyIGZpbHRlciBvdXQgaXRlbXMgdGhhdCBleGlzdCBpbiB0aGlzIG9iamVjdFxuICovXG5mdW5jdGlvbiBmaWx0ZXJFeHBvcnRzKHNvdXJjZTogQ3Jvc3NSZWdpb25FeHBvcnRzLCBmaWx0ZXI6IENyb3NzUmVnaW9uRXhwb3J0cyk6IENyb3NzUmVnaW9uRXhwb3J0cyB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhzb3VyY2UpXG4gICAgLmZpbHRlcihrZXkgPT4gIWZpbHRlci5oYXNPd25Qcm9wZXJ0eShrZXkpKVxuICAgIC5yZWR1Y2UoKGFjYzogQ3Jvc3NSZWdpb25FeHBvcnRzLCBjdXJyOiBzdHJpbmcpID0+IHtcbiAgICAgIGFjY1tjdXJyXSA9IHNvdXJjZVtjdXJyXTtcbiAgICAgIHJldHVybiBhY2M7XG4gICAgfSwge30pO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/__entrypoint__.js rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.d.ts rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js new file mode 100644 index 0000000000000..a487b651f8d38 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js @@ -0,0 +1,102 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties; + const exports = props.Exports; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // consuming stack will delete parameters + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'cdk-strong-ref') { + tagResults.has(name) + ? tagResults.get(name).add(tagParts[1]) + : tagResults.set(name, new Set([tagParts[1]])); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNwQyxNQUFNLGFBQWEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2xDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDO2dCQUM3QyxNQUFNLFVBQVUsR0FBdUIsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDeEQsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN2QyxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxhQUFhLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFR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SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztTQUN2QixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDM0UsTUFBTSxDQUFDLENBQUMsR0FBdUIsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUNoRCxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ1gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSBvbGRQcm9wcy5FeHBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdjZGstc3Ryb25nLXJlZicpIHtcbiAgICAgICAgICB0YWdSZXN1bHRzLmhhcyhuYW1lKVxuICAgICAgICAgICAgPyB0YWdSZXN1bHRzLmdldChuYW1lKSEuYWRkKHRhZ1BhcnRzWzFdKVxuICAgICAgICAgICAgOiB0YWdSZXN1bHRzLnNldChuYW1lLCBuZXcgU2V0KFt0YWdQYXJ0c1sxXV0pKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBhbiBJbnZhbGlkUmVzb3VyY2VJZCBtZWFucyB0aGF0IHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdFxuICAgICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgICAgaWYgKGUuY29kZSA9PT0gJ0ludmFsaWRSZXNvdXJjZUlkJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cblxuICB9KSk7XG5cbiAgaWYgKHRhZ1Jlc3VsdHMuc2l6ZSA+IDApIHtcbiAgICBjb25zdCBtZXNzYWdlOiBzdHJpbmcgPSBPYmplY3QuZW50cmllcyh0YWdSZXN1bHRzKVxuICAgICAgLm1hcCgocmVzdWx0OiBbc3RyaW5nLCBzdHJpbmdbXV0pID0+IGAke3Jlc3VsdFswXX0gaXMgaW4gdXNlIGJ5IHN0YWNrKHMpICR7cmVzdWx0WzFdLmpvaW4oJyAnKX1gKVxuICAgICAgLmpvaW4oJ1xcbicpO1xuICAgIHRocm93IG5ldyBFcnJvcihgRXhwb3J0cyBjYW5ub3QgYmUgdXBkYXRlZDogXFxuJHttZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts similarity index 57% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts index 8a829ea1a2cb4..69866b354da99 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4/index.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts @@ -12,29 +12,19 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent switch (event.RequestType) { case 'Create': console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyExistingParameters(ssm, exports); + await throwIfAnyInUse(ssm, exports); await putParameters(ssm, exports); return; case 'Update': const oldProps = event.OldResourceProperties; const oldExports: CrossRegionExports = oldProps.Exports; const newExports = filterExports(exports, oldExports); - await throwIfAnyExistingParameters(ssm, newExports); - const paramsToDelete = filterExports(oldExports, exports); - console.info(`Deleting unused SSM Parameter exports in region ${props.Region}`); - if (Object.keys(paramsToDelete).length > 0) { - await ssm.deleteParameters({ - Names: Object.keys(paramsToDelete), - }).promise(); - } + await throwIfAnyInUse(ssm, newExports); console.info(`Creating new SSM Parameter exports in region ${props.Region}`); await putParameters(ssm, newExports); return; case 'Delete': - console.info(`Deleting all SSM Parameter exports in region ${props.Region}`); - await ssm.deleteParameters({ - Names: Array.from(Object.keys(exports)), - }).promise(); + // consuming stack will delete parameters return; default: return; @@ -59,15 +49,41 @@ async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise< } /** - * Query for existing parameters + * Query for existing parameters that are in use */ -async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - const result = await ssm.getParameters({ - Names: Object.keys(parameters), - }).promise(); - if ((result.Parameters ?? []).length > 0) { - const existing = result.Parameters!.map(param => param.Name); - throw new Error(`Exports already exist: \n${existing.join('\n')}`); +async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { + const tagResults: Map> = new Map(); + await Promise.all(Object.keys(parameters).map(async (name: string) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'cdk-strong-ref') { + tagResults.has(name) + ? tagResults.get(name)!.add(tagParts[1]) + : tagResults.set(name, new Set([tagParts[1]])); + } + }); + + } catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + + })); + + if (tagResults.size > 0) { + const message: string = Object.entries(tagResults) + .map((result: [string, string[]]) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); } } @@ -79,7 +95,7 @@ async function throwIfAnyExistingParameters(ssm: SSM, parameters: CrossRegionExp */ function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) - .filter(key => !filter.hasOwnProperty(key)) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) .reduce((acc: CrossRegionExports, curr: string) => { acc[curr] = source[curr]; return acc; diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json index f1c403c2ebdea..009fba5ba2707 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json @@ -15,7 +15,21 @@ } } }, - "e7cbbedc5cef94e341632c341639a71c30895ee9de9827d4fcb3450a70c7c85c": { + "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915": { + "source": { + "path": "asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915", + "packaging": "zip" + }, + "destinations": { + "current_account-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", + "objectKey": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" + } + } + }, + "a5134211444edbb4f9d3853d1801a105e752559035941a285e5b2ae3e30d8152": { "source": { "path": "integ-pipeline-consumer-stack.template.json", "packaging": "file" @@ -23,7 +37,7 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "e7cbbedc5cef94e341632c341639a71c30895ee9de9827d4fcb3450a70c7c85c.json", + "objectKey": "a5134211444edbb4f9d3853d1801a105e752559035941a285e5b2ae3e30d8152.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json index 64c4d85591491..d3972490fd4a9 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json @@ -302,10 +302,10 @@ { "ArtifactStore": { "EncryptionKey": { - "Id": "{{resolve:ssm:/cdk/exports/integ-pipeline-producer-stack-ReplicationKeyFnGetAttReplicationKeyFCE40BF4ArnFBFE312D}}", + "Id": "{{resolve:ssm:/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73}}", "Type": "KMS" }, - "Location": "{{resolve:ssm:/cdk/exports/integ-pipeline-producer-stack-ReplicationBucketRefReplicationBucket70D6873707044AE1}}", + "Location": "{{resolve:ssm:/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D}}", "Type": "S3" }, "Region": "us-east-1" @@ -835,6 +835,103 @@ ] } } + }, + "ExportsReader8B249524": { + "Type": "Custom::CrossRegionExportReader", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", + "Arn" + ] + }, + "Region": "us-east-2", + "StackName": "integ-pipeline-consumer-stack", + "Imports": [ + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D", + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73" + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:us-east-2:", + { + "Ref": "AWS::AccountId" + }, + ":parameter/cdk/exports/integ-pipeline-consumer-stack/*" + ] + ] + }, + "Action": [ + "ssm:DeleteParameters", + "ssm:AddTagsToResource", + "ssm:RemoveTagsFromResource", + "ssm:GetParametersByPath", + "ssm:GetParameters" + ] + } + ] + } + } + ] + } + }, + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" + }, + "S3Key": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json index 8fc006d2da670..627286dc43199 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json @@ -15,21 +15,21 @@ } } }, - "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4": { + "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc": { "source": { - "path": "asset.45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4", + "path": "asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip", + "objectKey": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "b999b67fa19cd155ebe11ceceae9a49374d11b1a245cdc0fe354761ab831646b": { + "abc19ca62a7325852844f25fd3eacf14242467233199d0107a8932113f751377": { "source": { "path": "integ-pipeline-producer-stack.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "b999b67fa19cd155ebe11ceceae9a49374d11b1a245cdc0fe354761ab831646b.json", + "objectKey": "abc19ca62a7325852844f25fd3eacf14242467233199d0107a8932113f751377.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json index fa079b36f4f9d..f2cf8c6e062fd 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json @@ -187,7 +187,7 @@ "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" ] }, - "ExportsWriteruseast2828FA26B": { + "ExportsWriteruseast2828FA26B86FBEFA7": { "Type": "Custom::CrossRegionExportWriter", "Properties": { "ServiceToken": { @@ -198,10 +198,10 @@ }, "Region": "us-east-2", "Exports": { - "/cdk/exports/integ-pipeline-producer-stack-ReplicationBucketRefReplicationBucket70D6873707044AE1": { + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D": { "Ref": "ReplicationBucket70D68737" }, - "/cdk/exports/integ-pipeline-producer-stack-ReplicationKeyFnGetAttReplicationKeyFCE40BF4ArnFBFE312D": { + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73": { "Fn::GetAtt": [ "ReplicationKeyFCE40BF4", "Arn" @@ -253,9 +253,9 @@ ] }, "Action": [ + "ssm:ListTagsForResource", "ssm:GetParameters", - "ssm:PutParameter", - "ssm:DeleteParameters" + "ssm:PutParameter" ] } ] @@ -271,7 +271,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "45a896e4815eddbd9cf3bcc74e93517fbdcbd9275ee272d5d84a2b09f32aa6d4.zip" + "S3Key": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json index d95e5eb95f850..1558b6bd2e586 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/b999b67fa19cd155ebe11ceceae9a49374d11b1a245cdc0fe354761ab831646b.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/abc19ca62a7325852844f25fd3eacf14242467233199d0107a8932113f751377.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -69,10 +69,10 @@ "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" } ], - "/integ-pipeline-producer-stack/ExportsWriteruseast2828FA26B/Default/Default": [ + "/integ-pipeline-producer-stack/ExportsWriteruseast2828FA26B/Resource/Default": [ { "type": "aws:cdk:logicalId", - "data": "ExportsWriteruseast2828FA26B" + "data": "ExportsWriteruseast2828FA26B86FBEFA7" } ], "/integ-pipeline-producer-stack/Custom::CrossRegionExportWriterCustomResourceProvider/Role": [ @@ -118,7 +118,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/e7cbbedc5cef94e341632c341639a71c30895ee9de9827d4fcb3450a70c7c85c.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/a5134211444edbb4f9d3853d1801a105e752559035941a285e5b2ae3e30d8152.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -249,6 +249,24 @@ "data": "Build45A36621" } ], + "/integ-pipeline-consumer-stack/ExportsReader/Resource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsReader8B249524" + } + ], + "/integ-pipeline-consumer-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + } + ], + "/integ-pipeline-consumer-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" + } + ], "/integ-pipeline-consumer-stack/BootstrapVersion": [ { "type": "aws:cdk:logicalId", From a58b1e703fd033ec3ff8257343ffe661fdb8eb5c Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 30 Sep 2022 19:56:55 +0000 Subject: [PATCH 20/30] adding another integ tests for cloudfront cross region acm certificates --- .../integ.core-cross-region-references.ts | 4 +- packages/@aws-cdk/aws-cloudfront/package.json | 1 + .../__entrypoint__.js | 118 +++++++++++ .../index.d.ts | 1 + .../index.js | 102 +++++++++ .../index.ts | 103 +++++++++ .../__entrypoint__.js | 118 +++++++++++ .../index.d.ts | 1 + .../index.js | 127 ++++++++++++ .../index.ts | 125 +++++++++++ .../cdk.out | 1 + .../integ-acm-stack.assets.json | 34 +++ .../integ-acm-stack.template.json | 133 ++++++++++++ .../integ-cloudfront-stack.assets.json | 34 +++ .../integ-cloudfront-stack.template.json | 154 ++++++++++++++ .../integ.json | 14 ++ ...efaultTestDeployAssertD48673AA.assets.json | 19 ++ ...aultTestDeployAssertD48673AA.template.json | 36 ++++ .../manifest.json | 195 ++++++++++++++++++ .../integ.cloudfront-cross-region-cert.ts | 58 ++++++ .../test/integ.pipeline-with-replication.ts | 4 +- .../integ-runner/lib/runner/runner-base.ts | 3 + 22 files changed, 1383 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/__entrypoint__.js create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.d.ts create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.ts create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.template.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts index 0b214aa4cd9a2..d3bdb1bee9ef3 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts @@ -8,8 +8,10 @@ import { Construct } from 'constructs'; // GIVEN const app = new App({ treeMetadata: false, + postCliContext: { + [ENABLE_CROSS_REGION_REFERENCES]: true, + }, }); -app.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); class ProducerStack extends Stack { public readonly queue: IQueue; diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index 3eb58a20b10c7..76829339fa3f2 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -84,6 +84,7 @@ "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js new file mode 100644 index 0000000000000..9df94382cc74e --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + await exports.external.sendHttpRequest(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts new file mode 100644 index 0000000000000..3554dc94d4617 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts @@ -0,0 +1 @@ +export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js new file mode 100644 index 0000000000000..a487b651f8d38 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js @@ -0,0 +1,102 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties; + const exports = props.Exports; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // consuming stack will delete parameters + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'cdk-strong-ref') { + tagResults.has(name) + ? tagResults.get(name).add(tagParts[1]) + : tagResults.set(name, new Set([tagParts[1]])); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNwQyxNQUFNLGFBQWEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2xDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDO2dCQUM3QyxNQUFNLFVBQVUsR0FBdUIsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDeEQsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN2QyxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxhQUFhLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFR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SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztTQUN2QixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDM0UsTUFBTSxDQUFDLENBQUMsR0FBdUIsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUNoRCxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ1gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSBvbGRQcm9wcy5FeHBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdjZGstc3Ryb25nLXJlZicpIHtcbiAgICAgICAgICB0YWdSZXN1bHRzLmhhcyhuYW1lKVxuICAgICAgICAgICAgPyB0YWdSZXN1bHRzLmdldChuYW1lKSEuYWRkKHRhZ1BhcnRzWzFdKVxuICAgICAgICAgICAgOiB0YWdSZXN1bHRzLnNldChuYW1lLCBuZXcgU2V0KFt0YWdQYXJ0c1sxXV0pKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBhbiBJbnZhbGlkUmVzb3VyY2VJZCBtZWFucyB0aGF0IHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdFxuICAgICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgICAgaWYgKGUuY29kZSA9PT0gJ0ludmFsaWRSZXNvdXJjZUlkJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cblxuICB9KSk7XG5cbiAgaWYgKHRhZ1Jlc3VsdHMuc2l6ZSA+IDApIHtcbiAgICBjb25zdCBtZXNzYWdlOiBzdHJpbmcgPSBPYmplY3QuZW50cmllcyh0YWdSZXN1bHRzKVxuICAgICAgLm1hcCgocmVzdWx0OiBbc3RyaW5nLCBzdHJpbmdbXV0pID0+IGAke3Jlc3VsdFswXX0gaXMgaW4gdXNlIGJ5IHN0YWNrKHMpICR7cmVzdWx0WzFdLmpvaW4oJyAnKX1gKVxuICAgICAgLmpvaW4oJ1xcbicpO1xuICAgIHRocm93IG5ldyBFcnJvcihgRXhwb3J0cyBjYW5ub3QgYmUgdXBkYXRlZDogXFxuJHttZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts new file mode 100644 index 0000000000000..69866b354da99 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts @@ -0,0 +1,103 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; +type CrossRegionExports = { [exportName: string]: string }; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const exports: CrossRegionExports = props.Exports; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports: CrossRegionExports = oldProps.Exports; + const newExports = filterExports(exports, oldExports); + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // consuming stack will delete parameters + return; + default: + return; + } + } catch (e) { + console.error('Error processing event: ', e); + throw e; + } +}; + +/** + * Create parameters for existing exports + */ +async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} + +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { + const tagResults: Map> = new Map(); + await Promise.all(Object.keys(parameters).map(async (name: string) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'cdk-strong-ref') { + tagResults.has(name) + ? tagResults.get(name)!.add(tagParts[1]) + : tagResults.set(name, new Set([tagParts[1]])); + } + }); + + } catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + + })); + + if (tagResults.size > 0) { + const message: string = Object.entries(tagResults) + .map((result: [string, string[]]) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} + +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .reduce((acc: CrossRegionExports, curr: string) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/__entrypoint__.js new file mode 100644 index 0000000000000..9df94382cc74e --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/__entrypoint__.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + await exports.external.sendHttpRequest(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.d.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.d.ts new file mode 100644 index 0000000000000..3554dc94d4617 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.d.ts @@ -0,0 +1 @@ +export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js new file mode 100644 index 0000000000000..c9a612ee4b4aa --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js @@ -0,0 +1,127 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties; + const imports = props.Imports; + const keyName = `cdk-strong-ref:${props.StackName}`; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info('Tagging SSM Parameter imports'); + await addTags(ssm, imports, keyName); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports = oldProps.Imports; + const newExports = filterExports(imports, oldExports); + const paramsToDelete = filterExports(oldExports, imports); + console.info('Releasing unused SSM Parameter imports'); + if (Object.keys(paramsToDelete).length > 0) { + await removeTags(ssm, paramsToDelete, keyName); + } + console.info('Tagging new SSM Parameter imports'); + await addTags(ssm, newExports, keyName); + return; + case 'Delete': + console.info('Deleting all SSM Parameter exports'); + await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); + return; + default: + return; + } + } + catch (e) { + console.error('Error importing cross region stack exports: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Add tag to parameters for existing exports + */ +async function addTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return ssm.addTagsToResource({ + ResourceId: name, + ResourceType: 'Parameter', + Tags: [{ + Key: keyName, + Value: 'true', + }], + }).promise(); + } + catch (e) { + throw new Error(`Error importing ${name}: ${e}`); + } + })); +} +/** + * Remove tags from parameters + */ +async function removeTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return ssm.removeTagsFromResource({ + TagKeys: [keyName], + ResourceType: 'Parameter', + ResourceId: name, + }).promise(); + } + catch (e) { + switch (e.code) { + // if the parameter doesn't exist then there is nothing to release + case 'InvalidResourceId': + return; + default: + throw new Error(`Error releasing import ${name}: ${e}`); + } + } + })); +} +/** + * Get all parameters in a given path + * + * If the request fails for any reason it will fail the custom resource event. + * Since this is only run when the resource is deleted that is probably the behavior + * that is desired. + */ +async function getParametersByPath(ssm, path, nextToken) { + const parameters = []; + return ssm.getParametersByPath({ + Path: path, + NextToken: nextToken, + }).promise().then(async (getParametersByPathResult) => { + parameters.push(...getParametersByPathResult.Parameters ?? []); + if (getParametersByPathResult.NextToken) { + parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); + } + return parameters; + }); +} +/** + * Delete all parameters in a give path + */ +async function deleteParametersByPath(ssm, path) { + const allParams = await getParametersByPath(ssm, path); + const names = allParams.map(param => param.Name).filter(x => !!x); + await ssm.deleteParameters({ + Names: names, + }).promise(); +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source, filter) { + return source.filter(key => !filter.includes(key)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUV2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBYSxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ3hDLE1BQU0sT0FBTyxHQUFXLGtCQUFrQixLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7SUFFNUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO2dCQUM5QyxNQUFNLE9BQU8sQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFRO2dCQUNYLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQztnQkFDN0MsTUFBTSxVQUFVLEdBQWEsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDOUMsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFDO2dCQUN2RCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDMUMsTUFBTSxVQUFVLENBQUMs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QUMsR0FBRyxFQUFFLElBQUksRUFBRSx5QkFBeUIsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1NBQy9GO1FBQ0MsT0FBTyxVQUFVLENBQUM7SUFDdEIsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxLQUFLLFVBQVUsc0JBQXNCLENBQUMsR0FBUSxFQUFFLElBQVk7SUFDMUQsTUFBTSxTQUFTLEdBQUcsTUFBTSxtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDdkQsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFhLENBQUM7SUFDOUUsTUFBTSxHQUFHLENBQUMsZ0JBQWdCLENBQUM7UUFDekIsS0FBSyxFQUFFLEtBQUs7S0FDYixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7QUFDZixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLGFBQWEsQ0FBQyxNQUFnQixFQUFFLE1BQWdCO0lBQ3ZELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQ3JELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGltcG9ydHM6IHN0cmluZ1tdID0gcHJvcHMuSW1wb3J0cztcbiAgY29uc3Qga2V5TmFtZTogc3RyaW5nID0gYGNkay1zdHJvbmctcmVmOiR7cHJvcHMuU3RhY2tOYW1lfWA7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgYXdhaXQgYWRkVGFncyhzc20sIGltcG9ydHMsIGtleU5hbWUpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogc3RyaW5nW10gPSBvbGRQcm9wcy5JbXBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhpbXBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgY29uc3QgcGFyYW1zVG9EZWxldGUgPSBmaWx0ZXJFeHBvcnRzKG9sZEV4cG9ydHMsIGltcG9ydHMpO1xuICAgICAgICBjb25zb2xlLmluZm8oJ1JlbGVhc2luZyB1bnVzZWQgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGlmIChPYmplY3Qua2V5cyhwYXJhbXNUb0RlbGV0ZSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGF3YWl0IHJlbW92ZVRhZ3Moc3NtLCBwYXJhbXNUb0RlbGV0ZSwga2V5TmFtZSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc29sZS5pbmZvKCdUYWdnaW5nIG5ldyBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgYXdhaXQgYWRkVGFncyhzc20sIG5ld0V4cG9ydHMsIGtleU5hbWUpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdEZWxldGUnOlxuICAgICAgICBjb25zb2xlLmluZm8oJ0RlbGV0aW5nIGFsbCBTU00gUGFyYW1ldGVyIGV4cG9ydHMnKTtcbiAgICAgICAgYXdhaXQgZGVsZXRlUGFyYW1ldGVyc0J5UGF0aChzc20sIGAvY2RrL2V4cG9ydHMvJHtwcm9wcy5TdGFja05hbWV9L2ApO1xuICAgICAgICByZXR1cm47XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm47XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc29sZS5lcnJvcignRXJyb3IgaW1wb3J0aW5nIGNyb3NzIHJlZ2lvbiBzdGFjayBleHBvcnRzOiAnLCBlKTtcbiAgICB0aHJvdyBlO1xuICB9XG59O1xuXG4vKipcbiAqIEFkZCB0YWcgdG8gcGFyYW1ldGVycyBmb3IgZXhpc3RpbmcgZXhwb3J0c1xuICovXG5hc3luYyBmdW5jdGlvbiBhZGRUYWdzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBzdHJpbmdbXSwga2V5TmFtZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKHBhcmFtZXRlcnMubWFwKGFzeW5jIG5hbWUgPT4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gc3NtLmFkZFRhZ3NUb1Jlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgICAgVGFnczogW3tcbiAgICAgICAgICBLZXk6IGtleU5hbWUsXG4gICAgICAgICAgVmFsdWU6ICd0cnVlJyxcbiAgICAgICAgfV0sXG4gICAgICB9KS5wcm9taXNlKCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciBpbXBvcnRpbmcgJHtuYW1lfTogJHtlfWApO1xuICAgIH1cbiAgfSkpO1xufVxuXG4vKipcbiAqIFJlbW92ZSB0YWdzIGZyb20gcGFyYW1ldGVyc1xuICovXG5hc3luYyBmdW5jdGlvbiByZW1vdmVUYWdzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBzdHJpbmdbXSwga2V5TmFtZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKHBhcmFtZXRlcnMubWFwKGFzeW5jIG5hbWUgPT4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gc3NtLnJlbW92ZVRhZ3NGcm9tUmVzb3VyY2Uoe1xuICAgICAgICBUYWdLZXlzOiBba2V5TmFtZV0sXG4gICAgICAgIFJlc291cmNlVHlwZTogJ1BhcmFtZXRlcicsXG4gICAgICAgIFJlc291cmNlSWQ6IG5hbWUsXG4gICAgICB9KS5wcm9taXNlKCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgc3dpdGNoIChlLmNvZGUpIHtcbiAgICAgICAgLy8gaWYgdGhlIHBhcmFtZXRlciBkb2Vzbid0IGV4aXN0IHRoZW4gdGhlcmUgaXMgbm90aGluZyB0byByZWxlYXNlXG4gICAgICAgIGNhc2UgJ0ludmFsaWRSZXNvdXJjZUlkJzpcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciByZWxlYXNpbmcgaW1wb3J0ICR7bmFtZX06ICR7ZX1gKTtcbiAgICAgIH1cbiAgICB9XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBHZXQgYWxsIHBhcmFtZXRlcnMgaW4gYSBnaXZlbiBwYXRoXG4gKlxuICogSWYgdGhlIHJlcXVlc3QgZmFpbHMgZm9yIGFueSByZWFzb24gaXQgd2lsbCBmYWlsIHRoZSBjdXN0b20gcmVzb3VyY2UgZXZlbnQuXG4gKiBTaW5jZSB0aGlzIGlzIG9ubHkgcnVuIHdoZW4gdGhlIHJlc291cmNlIGlzIGRlbGV0ZWQgdGhhdCBpcyBwcm9iYWJseSB0aGUgYmVoYXZpb3JcbiAqIHRoYXQgaXMgZGVzaXJlZC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0UGFyYW1ldGVyc0J5UGF0aChzc206IFNTTSwgcGF0aDogc3RyaW5nLCBuZXh0VG9rZW4/OiBzdHJpbmcpOiBQcm9taXNlPFNTTS5QYXJhbWV0ZXJbXT4ge1xuICBjb25zdCBwYXJhbWV0ZXJzOiBTU00uUGFyYW1ldGVyW10gPSBbXTtcbiAgcmV0dXJuIHNzbS5nZXRQYXJhbWV0ZXJzQnlQYXRoKHtcbiAgICBQYXRoOiBwYXRoLFxuICAgIE5leHRUb2tlbjogbmV4dFRva2VuLFxuICB9KS5wcm9taXNlKCkudGhlbihhc3luYyBnZXRQYXJhbWV0ZXJzQnlQYXRoUmVzdWx0ID0+IHtcbiAgICBwYXJhbWV0ZXJzLnB1c2goLi4uZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdC5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICBpZiAoZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdC5OZXh0VG9rZW4pIHtcbiAgICAgIHBhcmFtZXRlcnMucHVzaCguLi5hd2FpdCBnZXRQYXJhbWV0ZXJzQnlQYXRoKHNzbSwgcGF0aCwgZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdC5OZXh0VG9rZW4pKTtcbiAgICB9XG4gICAgICByZXR1cm4gcGFyYW1ldGVycztcbiAgfSk7XG59XG5cbi8qKlxuICogRGVsZXRlIGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZSBwYXRoXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBhbGxQYXJhbXMgPSBhd2FpdCBnZXRQYXJhbWV0ZXJzQnlQYXRoKHNzbSwgcGF0aCk7XG4gIGNvbnN0IG5hbWVzID0gYWxsUGFyYW1zLm1hcChwYXJhbSA9PiBwYXJhbS5OYW1lKS5maWx0ZXIoeCA9PiAhIXgpIGFzIHN0cmluZ1tdO1xuICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IG5hbWVzLFxuICB9KS5wcm9taXNlKCk7XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IHN0cmluZ1tdLCBmaWx0ZXI6IHN0cmluZ1tdKTogc3RyaW5nW10ge1xuICByZXR1cm4gc291cmNlLmZpbHRlcihrZXkgPT4gIWZpbHRlci5pbmNsdWRlcyhrZXkpKTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.ts new file mode 100644 index 0000000000000..23ec4ad44a7f8 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.ts @@ -0,0 +1,125 @@ +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +import { SSM } from 'aws-sdk'; + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + const props = event.ResourceProperties; + const imports: string[] = props.Imports; + const keyName: string = `cdk-strong-ref:${props.StackName}`; + + const ssm = new SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info('Tagging SSM Parameter imports'); + await addTags(ssm, imports, keyName); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports: string[] = oldProps.Imports; + const newExports = filterExports(imports, oldExports); + const paramsToDelete = filterExports(oldExports, imports); + console.info('Releasing unused SSM Parameter imports'); + if (Object.keys(paramsToDelete).length > 0) { + await removeTags(ssm, paramsToDelete, keyName); + } + console.info('Tagging new SSM Parameter imports'); + await addTags(ssm, newExports, keyName); + return; + case 'Delete': + console.info('Deleting all SSM Parameter exports'); + await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); + return; + default: + return; + } + } catch (e) { + console.error('Error importing cross region stack exports: ', e); + throw e; + } +}; + +/** + * Add tag to parameters for existing exports + */ +async function addTags(ssm: SSM, parameters: string[], keyName: string): Promise { + await Promise.all(parameters.map(async name => { + try { + return ssm.addTagsToResource({ + ResourceId: name, + ResourceType: 'Parameter', + Tags: [{ + Key: keyName, + Value: 'true', + }], + }).promise(); + } catch (e) { + throw new Error(`Error importing ${name}: ${e}`); + } + })); +} + +/** + * Remove tags from parameters + */ +async function removeTags(ssm: SSM, parameters: string[], keyName: string): Promise { + await Promise.all(parameters.map(async name => { + try { + return ssm.removeTagsFromResource({ + TagKeys: [keyName], + ResourceType: 'Parameter', + ResourceId: name, + }).promise(); + } catch (e) { + switch (e.code) { + // if the parameter doesn't exist then there is nothing to release + case 'InvalidResourceId': + return; + default: + throw new Error(`Error releasing import ${name}: ${e}`); + } + } + })); +} + +/** + * Get all parameters in a given path + * + * If the request fails for any reason it will fail the custom resource event. + * Since this is only run when the resource is deleted that is probably the behavior + * that is desired. + */ +async function getParametersByPath(ssm: SSM, path: string, nextToken?: string): Promise { + const parameters: SSM.Parameter[] = []; + return ssm.getParametersByPath({ + Path: path, + NextToken: nextToken, + }).promise().then(async getParametersByPathResult => { + parameters.push(...getParametersByPathResult.Parameters ?? []); + if (getParametersByPathResult.NextToken) { + parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); + } + return parameters; + }); +} + +/** + * Delete all parameters in a give path + */ +async function deleteParametersByPath(ssm: SSM, path: string): Promise { + const allParams = await getParametersByPath(ssm, path); + const names = allParams.map(param => param.Name).filter(x => !!x) as string[]; + await ssm.deleteParameters({ + Names: names, + }).promise(); +} + +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source: string[], filter: string[]): string[] { + return source.filter(key => !filter.includes(key)); +} diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json new file mode 100644 index 0000000000000..33b3b650da092 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json @@ -0,0 +1,34 @@ +{ + "version": "21.0.0", + "files": { + "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc": { + "source": { + "path": "asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc", + "packaging": "zip" + }, + "destinations": { + "12345678-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "objectKey": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + } + } + }, + "4419fd51b8084fccb4113ddda58c48d294c907c5ed5a69b01405d0bdf4b7aee2": { + "source": { + "path": "integ-acm-stack.template.json", + "packaging": "file" + }, + "destinations": { + "12345678-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "objectKey": "4419fd51b8084fccb4113ddda58c48d294c907c5ed5a69b01405d0bdf4b7aee2.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json new file mode 100644 index 0000000000000..3bea1a4fa002e --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json @@ -0,0 +1,133 @@ +{ + "Resources": { + "Cert5C9FAEC1": { + "Type": "AWS::CertificateManager::Certificate", + "Properties": { + "DomainName": "*.example.com", + "DomainValidationOptions": [ + { + "DomainName": "*.example.com", + "HostedZoneId": "Z23ABC4XYZL05B" + } + ], + "ValidationMethod": "DNS" + } + }, + "ExportsWriteruseast2828FA26B86FBEFA7": { + "Type": "Custom::CrossRegionExportWriter", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A", + "Arn" + ] + }, + "Region": "us-east-2", + "Exports": { + "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2": { + "Ref": "Cert5C9FAEC1" + } + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": "arn:aws:ssm:us-east-2:12345678:parameter/cdk/exports/*", + "Action": [ + "ssm:ListTagsForResource", + "ssm:GetParameters", + "ssm:PutParameter" + ] + } + ] + } + } + ] + } + }, + "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-1", + "S3Key": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json new file mode 100644 index 0000000000000..889e19b14d748 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json @@ -0,0 +1,34 @@ +{ + "version": "21.0.0", + "files": { + "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915": { + "source": { + "path": "asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915", + "packaging": "zip" + }, + "destinations": { + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", + "objectKey": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" + } + } + }, + "c6b357f1b87eb64cf51942fb6defee308cb2c19a05b1780418ff0b3fbd74758e": { + "source": { + "path": "integ-cloudfront-stack.template.json", + "packaging": "file" + }, + "destinations": { + "12345678-us-east-2": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", + "objectKey": "c6b357f1b87eb64cf51942fb6defee308cb2c19a05b1780418ff0b3fbd74758e.json", + "region": "us-east-2", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json new file mode 100644 index 0000000000000..06bb2c28e3cde --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json @@ -0,0 +1,154 @@ +{ + "Resources": { + "Distro87EBE6BA": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "Aliases": [ + "*.example.com" + ], + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "integcloudfrontstackDistroOrigin1746AED30", + "ViewerProtocolPolicy": "allow-all" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": "*.example.com", + "Id": "integcloudfrontstackDistroOrigin1746AED30" + } + ], + "ViewerCertificate": { + "AcmCertificateArn": "{{resolve:ssm:/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2}}", + "MinimumProtocolVersion": "TLSv1.2_2021", + "SslSupportMethod": "sni-only" + } + } + } + }, + "ExportsReader8B249524": { + "Type": "Custom::CrossRegionExportReader", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68", + "Arn" + ] + }, + "Region": "us-east-2", + "StackName": "integ-cloudfront-stack", + "Imports": [ + "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2" + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": "arn:aws:ssm:us-east-2:12345678:parameter/cdk/exports/integ-cloudfront-stack/*", + "Action": [ + "ssm:DeleteParameters", + "ssm:AddTagsToResource", + "ssm:RemoveTagsFromResource", + "ssm:GetParametersByPath", + "ssm:GetParameters" + ] + } + ] + } + } + ] + } + }, + "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", + "S3Key": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD", + "Arn" + ] + }, + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ.json new file mode 100644 index 0000000000000..22501b4eb0504 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ.json @@ -0,0 +1,14 @@ +{ + "enableLookups": true, + "version": "21.0.0", + "testCases": { + "integ-cloudfront-cross-region-acm/DefaultTest": { + "stacks": [ + "integ-cloudfront-stack" + ], + "diffAssets": false, + "assertionStack": "integ-cloudfront-cross-region-acm/DefaultTest/DeployAssert", + "assertionStackName": "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets.json new file mode 100644 index 0000000000000..34d217e8f2958 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..0c9e8cb0e02aa --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json @@ -0,0 +1,195 @@ +{ + "version": "21.0.0", + "artifacts": { + "integ-acm-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-acm-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-acm-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://12345678/us-east-1", + "properties": { + "templateFile": "integ-acm-stack.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/4419fd51b8084fccb4113ddda58c48d294c907c5ed5a69b01405d0bdf4b7aee2.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-acm-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-acm-stack.assets" + ], + "metadata": { + "/integ-acm-stack/Cert/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Cert5C9FAEC1" + } + ], + "/integ-acm-stack/ExportsWriteruseast2828FA26B/Resource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsWriteruseast2828FA26B86FBEFA7" + } + ], + "/integ-acm-stack/Custom::CrossRegionExportWriterCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportWriterCustomResourceProviderRoleC951B1E1" + } + ], + "/integ-acm-stack/Custom::CrossRegionExportWriterCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A" + } + ], + "/integ-acm-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-acm-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-acm-stack" + }, + "integ-cloudfront-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-cloudfront-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-cloudfront-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://12345678/us-east-2", + "properties": { + "templateFile": "integ-cloudfront-stack.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/c6b357f1b87eb64cf51942fb6defee308cb2c19a05b1780418ff0b3fbd74758e.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-cloudfront-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-2", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-acm-stack", + "integ-cloudfront-stack.assets" + ], + "metadata": { + "/integ-cloudfront-stack/Distro/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Distro87EBE6BA" + } + ], + "/integ-cloudfront-stack/ExportsReader/Resource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsReader8B249524" + } + ], + "/integ-cloudfront-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderRole10531BBD" + } + ], + "/integ-cloudfront-stack/Custom::CrossRegionExportReaderCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68" + } + ], + "/integ-cloudfront-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-cloudfront-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-cloudfront-stack" + }, + "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integcloudfrontcrossregionacmDefaultTestDeployAssertD48673AA.assets" + ], + "metadata": { + "/integ-cloudfront-cross-region-acm/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-cloudfront-cross-region-acm/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-cloudfront-cross-region-acm/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts new file mode 100644 index 0000000000000..132de7f38185e --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts @@ -0,0 +1,58 @@ +import * as acm from '@aws-cdk/aws-certificatemanager'; +import * as route53 from '@aws-cdk/aws-route53'; +import * as cdk from '@aws-cdk/core'; +import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import * as cloudfront from '../lib'; +import { TestOrigin } from './test-origin'; + +const account = process.env.CDK_INTEG_ACCOUNT ?? process.env.CDK_DEFAULT_ACCOUNT; +const hostedZoneId = process.env.CDK_INTEG_HOSTED_ZONE_ID ?? process.env.HOSTED_ZONE_ID; +if (!hostedZoneId) throw new Error('For this test you must provide your own HostedZoneId as an env var "HOSTED_ZONE_ID"'); +const hostedZoneName = process.env.CDK_INTEG_HOSTED_ZONE_NAME ?? process.env.HOSTED_ZONE_NAME; +if (!hostedZoneName) throw new Error('For this test you must provide your own HostedZoneName as an env var "HOSTED_ZONE_NAME"'); +const domainName = process.env.CDK_INTEG_DOMAIN_NAME ?? process.env.DOMAIN_NAME; +if (!domainName) throw new Error('For this test you must provide your own Domain Name as an env var "DOMAIN_NAME"'); + +const app = new cdk.App({ + treeMetadata: false, + postCliContext: { + [ENABLE_CROSS_REGION_REFERENCES]: true, + }, +}); +const acmStack = new cdk.Stack(app, 'integ-acm-stack', { + env: { + region: 'us-east-1', + account, + }, +}); + +const cloudFrontStack = new cdk.Stack(app, 'integ-cloudfront-stack', { + env: { + region: 'us-east-2', + account, + }, +}); + + +const hostedZone = route53.PublicHostedZone.fromHostedZoneAttributes(acmStack, 'HostedZone', { + hostedZoneId, + zoneName: hostedZoneName, +}); + +const cert = new acm.Certificate(acmStack, 'Cert', { + domainName, + validation: acm.CertificateValidation.fromDns(hostedZone), +}); + +new cloudfront.Distribution(cloudFrontStack, 'Distro', { + defaultBehavior: { origin: new TestOrigin(domainName) }, + certificate: cert, + domainNames: [domainName], +}); + +new IntegTest(app, 'integ-cloudfront-cross-region-acm', { + testCases: [cloudFrontStack], + diffAssets: false, + enableLookups: true, +}); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts index 5d32b281a35b2..b7d18a64c0d2c 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts @@ -10,8 +10,10 @@ import { S3SourceAction, CodeBuildAction } from '../lib'; const app = new App({ treeMetadata: false, + postCliContext: { + [ENABLE_CROSS_REGION_REFERENCES]: true, + }, }); -app.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); const stack1 = new Stack(app, 'integ-pipeline-producer-stack', { env: { region: 'us-east-1', diff --git a/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts b/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts index c8fc073d89918..9ddaf2e7e4de4 100644 --- a/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts +++ b/packages/@aws-cdk/integ-runner/lib/runner/runner-base.ts @@ -417,5 +417,8 @@ export const DEFAULT_SYNTH_OPTIONS = { env: { CDK_INTEG_ACCOUNT: '12345678', CDK_INTEG_REGION: 'test-region', + CDK_INTEG_HOSTED_ZONE_ID: 'Z23ABC4XYZL05B', + CDK_INTEG_HOSTED_ZONE_NAME: 'example.com', + CDK_INTEG_DOMAIN_NAME: '*.example.com', }, }; From d702e0f24549bdfee6b418eb90ce2d8581d9a8ca Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 30 Sep 2022 19:59:19 +0000 Subject: [PATCH 21/30] fixing formatting --- .../cross-region-ssm-reader-handler/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts index 23ec4ad44a7f8..fd5e4422c2df2 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts @@ -45,7 +45,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent async function addTags(ssm: SSM, parameters: string[], keyName: string): Promise { await Promise.all(parameters.map(async name => { try { - return ssm.addTagsToResource({ + return await ssm.addTagsToResource({ ResourceId: name, ResourceType: 'Parameter', Tags: [{ @@ -65,7 +65,7 @@ async function addTags(ssm: SSM, parameters: string[], keyName: string): Promise async function removeTags(ssm: SSM, parameters: string[], keyName: string): Promise { await Promise.all(parameters.map(async name => { try { - return ssm.removeTagsFromResource({ + return await ssm.removeTagsFromResource({ TagKeys: [keyName], ResourceType: 'Parameter', ResourceId: name, @@ -99,7 +99,7 @@ async function getParametersByPath(ssm: SSM, path: string, nextToken?: string): if (getParametersByPathResult.NextToken) { parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); } - return parameters; + return parameters; }); } From f9cbab7089958c21e7384eeeb1dda4af092b7578 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 3 Oct 2022 13:24:03 +0000 Subject: [PATCH 22/30] fixing version of aws-sdk --- packages/@aws-cdk/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/core/package.json b/packages/@aws-cdk/core/package.json index d40cc39af7502..6d54b58faab69 100644 --- a/packages/@aws-cdk/core/package.json +++ b/packages/@aws-cdk/core/package.json @@ -189,7 +189,7 @@ "@types/jest": "^27.5.2", "@types/lodash": "^4.14.185", "@types/minimatch": "^3.0.5", - "aws-sdk": "^2.848.0", + "aws-sdk": "^2.928.0", "@types/node": "^14.18.29", "@types/sinon": "^9.0.11", "fast-check": "^2.25.0", From 3a1c86bbbbecea6e4af518fdc90287593280329b Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 3 Oct 2022 14:30:26 +0000 Subject: [PATCH 23/30] fix test --- .../custom-resource-provider/export-writer-provider.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index 683fea1ccb908..bdb3ddb6608e5 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -146,7 +146,7 @@ describe('export writer provider', () => { S3Bucket: { 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', }, - S3Key: 'abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip', + S3Key: expect.any(String), }, Handler: '__entrypoint__.handler', MemorySize: 128, @@ -390,7 +390,7 @@ describe('export writer provider', () => { S3Bucket: { 'Fn::Sub': 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', }, - S3Key: 'abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip', + S3Key: expect.any(String), }, Handler: '__entrypoint__.handler', MemorySize: 128, From 38b67799c7d94f02fb44e5a3ba4ca500dfba328e Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 3 Oct 2022 14:53:19 +0000 Subject: [PATCH 24/30] fixing formatting in integ test --- .../__entrypoint__.js | 0 .../index.d.ts | 0 .../index.js | 127 ++++++++++++++++++ .../index.ts | 6 +- .../index.js | 127 ------------------ .../integ-cloudfront-stack.assets.json | 10 +- .../integ-cloudfront-stack.template.json | 2 +- .../manifest.json | 2 +- 8 files changed, 137 insertions(+), 137 deletions(-) rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915 => asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf}/__entrypoint__.js (100%) rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915 => asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915 => asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf}/index.ts (97%) delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/__entrypoint__.js rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.d.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.d.ts rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.d.ts diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js new file mode 100644 index 0000000000000..3c1ee7facb4df --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js @@ -0,0 +1,127 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties; + const imports = props.Imports; + const keyName = `cdk-strong-ref:${props.StackName}`; + const ssm = new aws_sdk_1.SSM({ region: props.Region }); + try { + switch (event.RequestType) { + case 'Create': + console.info('Tagging SSM Parameter imports'); + await addTags(ssm, imports, keyName); + return; + case 'Update': + const oldProps = event.OldResourceProperties; + const oldExports = oldProps.Imports; + const newExports = filterExports(imports, oldExports); + const paramsToDelete = filterExports(oldExports, imports); + console.info('Releasing unused SSM Parameter imports'); + if (Object.keys(paramsToDelete).length > 0) { + await removeTags(ssm, paramsToDelete, keyName); + } + console.info('Tagging new SSM Parameter imports'); + await addTags(ssm, newExports, keyName); + return; + case 'Delete': + console.info('Deleting all SSM Parameter exports'); + await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); + return; + default: + return; + } + } + catch (e) { + console.error('Error importing cross region stack exports: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Add tag to parameters for existing exports + */ +async function addTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return await ssm.addTagsToResource({ + ResourceId: name, + ResourceType: 'Parameter', + Tags: [{ + Key: keyName, + Value: 'true', + }], + }).promise(); + } + catch (e) { + throw new Error(`Error importing ${name}: ${e}`); + } + })); +} +/** + * Remove tags from parameters + */ +async function removeTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return await ssm.removeTagsFromResource({ + TagKeys: [keyName], + ResourceType: 'Parameter', + ResourceId: name, + }).promise(); + } + catch (e) { + switch (e.code) { + // if the parameter doesn't exist then there is nothing to release + case 'InvalidResourceId': + return; + default: + throw new Error(`Error releasing import ${name}: ${e}`); + } + } + })); +} +/** + * Get all parameters in a given path + * + * If the request fails for any reason it will fail the custom resource event. + * Since this is only run when the resource is deleted that is probably the behavior + * that is desired. + */ +async function getParametersByPath(ssm, path, nextToken) { + const parameters = []; + return ssm.getParametersByPath({ + Path: path, + NextToken: nextToken, + }).promise().then(async (getParametersByPathResult) => { + parameters.push(...getParametersByPathResult.Parameters ?? []); + if (getParametersByPathResult.NextToken) { + parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); + } + return parameters; + }); +} +/** + * Delete all parameters in a give path + */ +async function deleteParametersByPath(ssm, path) { + const allParams = await getParametersByPath(ssm, path); + const names = allParams.map(param => param.Name).filter(x => !!x); + await ssm.deleteParameters({ + Names: names, + }).promise(); +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function filterExports(source, filter) { + return source.filter(key => !filter.includes(key)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUV2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBYSxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ3hDLE1BQU0sT0FBTyxHQUFXLGtCQUFrQixLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7SUFFNUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO2dCQUM5QyxNQUFNLE9BQU8sQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFRO2dCQUNYLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQztnQkFDN0MsTUFBTSxVQUFVLEdBQWEsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDOUMsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFDO2dCQUN2RCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDMUMsTUFBTSxVQUFVLENBQUMs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LG1CQUFtQixDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUseUJBQXlCLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztTQUMvRjtRQUNELE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsS0FBSyxVQUFVLHNCQUFzQixDQUFDLEdBQVEsRUFBRSxJQUFZO0lBQzFELE1BQU0sU0FBUyxHQUFHLE1BQU0sbUJBQW1CLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3ZELE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBYSxDQUFDO0lBQzlFLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO1FBQ3pCLEtBQUssRUFBRSxLQUFLO0tBQ2IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ2YsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxhQUFhLENBQUMsTUFBZ0IsRUFBRSxNQUFnQjtJQUN2RCxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUNyRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyplc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnYXdzLXNkayc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGNvbnN0IHByb3BzID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICBjb25zdCBpbXBvcnRzOiBzdHJpbmdbXSA9IHByb3BzLkltcG9ydHM7XG4gIGNvbnN0IGtleU5hbWU6IHN0cmluZyA9IGBjZGstc3Ryb25nLXJlZjoke3Byb3BzLlN0YWNrTmFtZX1gO1xuXG4gIGNvbnN0IHNzbSA9IG5ldyBTU00oeyByZWdpb246IHByb3BzLlJlZ2lvbiB9KTtcbiAgdHJ5IHtcbiAgICBzd2l0Y2ggKGV2ZW50LlJlcXVlc3RUeXBlKSB7XG4gICAgICBjYXNlICdDcmVhdGUnOlxuICAgICAgICBjb25zb2xlLmluZm8oJ1RhZ2dpbmcgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGFkZFRhZ3Moc3NtLCBpbXBvcnRzLCBrZXlOYW1lKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHM6IHN0cmluZ1tdID0gb2xkUHJvcHMuSW1wb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGZpbHRlckV4cG9ydHMoaW1wb3J0cywgb2xkRXhwb3J0cyk7XG4gICAgICAgIGNvbnN0IHBhcmFtc1RvRGVsZXRlID0gZmlsdGVyRXhwb3J0cyhvbGRFeHBvcnRzLCBpbXBvcnRzKTtcbiAgICAgICAgY29uc29sZS5pbmZvKCdSZWxlYXNpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgaW1wb3J0cycpO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCByZW1vdmVUYWdzKHNzbSwgcGFyYW1zVG9EZWxldGUsIGtleU5hbWUpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBuZXcgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGFkZFRhZ3Moc3NtLCBuZXdFeHBvcnRzLCBrZXlOYW1lKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKCdEZWxldGluZyBhbGwgU1NNIFBhcmFtZXRlciBleHBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtLCBgL2Nkay9leHBvcnRzLyR7cHJvcHMuU3RhY2tOYW1lfS9gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGltcG9ydGluZyBjcm9zcyByZWdpb24gc3RhY2sgZXhwb3J0czogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBBZGQgdGFnIHRvIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gYWRkVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5hZGRUYWdzVG9SZXNvdXJjZSh7XG4gICAgICAgIFJlc291cmNlSWQ6IG5hbWUsXG4gICAgICAgIFJlc291cmNlVHlwZTogJ1BhcmFtZXRlcicsXG4gICAgICAgIFRhZ3M6IFt7XG4gICAgICAgICAgS2V5OiBrZXlOYW1lLFxuICAgICAgICAgIFZhbHVlOiAndHJ1ZScsXG4gICAgICAgIH1dLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgaW1wb3J0aW5nICR7bmFtZX06ICR7ZX1gKTtcbiAgICB9XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBSZW1vdmUgdGFncyBmcm9tIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVtb3ZlVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5yZW1vdmVUYWdzRnJvbVJlc291cmNlKHtcbiAgICAgICAgVGFnS2V5czogW2tleU5hbWVdLFxuICAgICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgICAgICBSZXNvdXJjZUlkOiBuYW1lLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHN3aXRjaCAoZS5jb2RlKSB7XG4gICAgICAgIC8vIGlmIHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdCB0aGVuIHRoZXJlIGlzIG5vdGhpbmcgdG8gcmVsZWFzZVxuICAgICAgICBjYXNlICdJbnZhbGlkUmVzb3VyY2VJZCc6XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgcmVsZWFzaW5nIGltcG9ydCAke25hbWV9OiAke2V9YCk7XG4gICAgICB9XG4gICAgfVxuICB9KSk7XG59XG5cbi8qKlxuICogR2V0IGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZW4gcGF0aFxuICpcbiAqIElmIHRoZSByZXF1ZXN0IGZhaWxzIGZvciBhbnkgcmVhc29uIGl0IHdpbGwgZmFpbCB0aGUgY3VzdG9tIHJlc291cmNlIGV2ZW50LlxuICogU2luY2UgdGhpcyBpcyBvbmx5IHJ1biB3aGVuIHRoZSByZXNvdXJjZSBpcyBkZWxldGVkIHRoYXQgaXMgcHJvYmFibHkgdGhlIGJlaGF2aW9yXG4gKiB0aGF0IGlzIGRlc2lyZWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldFBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZywgbmV4dFRva2VuPzogc3RyaW5nKTogUHJvbWlzZTxTU00uUGFyYW1ldGVyW10+IHtcbiAgY29uc3QgcGFyYW1ldGVyczogU1NNLlBhcmFtZXRlcltdID0gW107XG4gIHJldHVybiBzc20uZ2V0UGFyYW1ldGVyc0J5UGF0aCh7XG4gICAgUGF0aDogcGF0aCxcbiAgICBOZXh0VG9rZW46IG5leHRUb2tlbixcbiAgfSkucHJvbWlzZSgpLnRoZW4oYXN5bmMgZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdCA9PiB7XG4gICAgcGFyYW1ldGVycy5wdXNoKC4uLmdldFBhcmFtZXRlcnNCeVBhdGhSZXN1bHQuUGFyYW1ldGVycyA/PyBbXSk7XG4gICAgaWYgKGdldFBhcmFtZXRlcnNCeVBhdGhSZXN1bHQuTmV4dFRva2VuKSB7XG4gICAgICBwYXJhbWV0ZXJzLnB1c2goLi4uYXdhaXQgZ2V0UGFyYW1ldGVyc0J5UGF0aChzc20sIHBhdGgsIGdldFBhcmFtZXRlcnNCeVBhdGhSZXN1bHQuTmV4dFRva2VuKSk7XG4gICAgfVxuICAgIHJldHVybiBwYXJhbWV0ZXJzO1xuICB9KTtcbn1cblxuLyoqXG4gKiBEZWxldGUgYWxsIHBhcmFtZXRlcnMgaW4gYSBnaXZlIHBhdGhcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZGVsZXRlUGFyYW1ldGVyc0J5UGF0aChzc206IFNTTSwgcGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGFsbFBhcmFtcyA9IGF3YWl0IGdldFBhcmFtZXRlcnNCeVBhdGgoc3NtLCBwYXRoKTtcbiAgY29uc3QgbmFtZXMgPSBhbGxQYXJhbXMubWFwKHBhcmFtID0+IHBhcmFtLk5hbWUpLmZpbHRlcih4ID0+ICEheCkgYXMgc3RyaW5nW107XG4gIGF3YWl0IHNzbS5kZWxldGVQYXJhbWV0ZXJzKHtcbiAgICBOYW1lczogbmFtZXMsXG4gIH0pLnByb21pc2UoKTtcbn1cblxuLyoqXG4gKiBSZXR1cm4gb25seSB0aGUgaXRlbXMgZnJvbSBzb3VyY2UgdGhhdCBkbyBub3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICpcbiAqIEBwYXJhbSBzb3VyY2UgdGhlIHNvdXJjZSBvYmplY3QgdG8gcGVyZm9ybSB0aGUgZmlsdGVyIG9uXG4gKiBAcGFyYW0gZmlsdGVyIGZpbHRlciBvdXQgaXRlbXMgdGhhdCBleGlzdCBpbiB0aGlzIG9iamVjdFxuICovXG5mdW5jdGlvbiBmaWx0ZXJFeHBvcnRzKHNvdXJjZTogc3RyaW5nW10sIGZpbHRlcjogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBzb3VyY2UuZmlsdGVyKGtleSA9PiAhZmlsdGVyLmluY2x1ZGVzKGtleSkpO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.ts similarity index 97% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.ts rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.ts index 23ec4ad44a7f8..fd5e4422c2df2 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.ts @@ -45,7 +45,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent async function addTags(ssm: SSM, parameters: string[], keyName: string): Promise { await Promise.all(parameters.map(async name => { try { - return ssm.addTagsToResource({ + return await ssm.addTagsToResource({ ResourceId: name, ResourceType: 'Parameter', Tags: [{ @@ -65,7 +65,7 @@ async function addTags(ssm: SSM, parameters: string[], keyName: string): Promise async function removeTags(ssm: SSM, parameters: string[], keyName: string): Promise { await Promise.all(parameters.map(async name => { try { - return ssm.removeTagsFromResource({ + return await ssm.removeTagsFromResource({ TagKeys: [keyName], ResourceType: 'Parameter', ResourceId: name, @@ -99,7 +99,7 @@ async function getParametersByPath(ssm: SSM, path: string, nextToken?: string): if (getParametersByPathResult.NextToken) { parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); } - return parameters; + return parameters; }); } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js deleted file mode 100644 index c9a612ee4b4aa..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915/index.js +++ /dev/null @@ -1,127 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties; - const imports = props.Imports; - const keyName = `cdk-strong-ref:${props.StackName}`; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info('Tagging SSM Parameter imports'); - await addTags(ssm, imports, keyName); - return; - case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports = oldProps.Imports; - const newExports = filterExports(imports, oldExports); - const paramsToDelete = filterExports(oldExports, imports); - console.info('Releasing unused SSM Parameter imports'); - if (Object.keys(paramsToDelete).length > 0) { - await removeTags(ssm, paramsToDelete, keyName); - } - console.info('Tagging new SSM Parameter imports'); - await addTags(ssm, newExports, keyName); - return; - case 'Delete': - console.info('Deleting all SSM Parameter exports'); - await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); - return; - default: - return; - } - } - catch (e) { - console.error('Error importing cross region stack exports: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Add tag to parameters for existing exports - */ -async function addTags(ssm, parameters, keyName) { - await Promise.all(parameters.map(async (name) => { - try { - return ssm.addTagsToResource({ - ResourceId: name, - ResourceType: 'Parameter', - Tags: [{ - Key: keyName, - Value: 'true', - }], - }).promise(); - } - catch (e) { - throw new Error(`Error importing ${name}: ${e}`); - } - })); -} -/** - * Remove tags from parameters - */ -async function removeTags(ssm, parameters, keyName) { - await Promise.all(parameters.map(async (name) => { - try { - return ssm.removeTagsFromResource({ - TagKeys: [keyName], - ResourceType: 'Parameter', - ResourceId: name, - }).promise(); - } - catch (e) { - switch (e.code) { - // if the parameter doesn't exist then there is nothing to release - case 'InvalidResourceId': - return; - default: - throw new Error(`Error releasing import ${name}: ${e}`); - } - } - })); -} -/** - * Get all parameters in a given path - * - * If the request fails for any reason it will fail the custom resource event. - * Since this is only run when the resource is deleted that is probably the behavior - * that is desired. - */ -async function getParametersByPath(ssm, path, nextToken) { - const parameters = []; - return ssm.getParametersByPath({ - Path: path, - NextToken: nextToken, - }).promise().then(async (getParametersByPathResult) => { - parameters.push(...getParametersByPathResult.Parameters ?? []); - if (getParametersByPathResult.NextToken) { - parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); - } - return parameters; - }); -} -/** - * Delete all parameters in a give path - */ -async function deleteParametersByPath(ssm, path) { - const allParams = await getParametersByPath(ssm, path); - const names = allParams.map(param => param.Name).filter(x => !!x); - await ssm.deleteParameters({ - Names: names, - }).promise(); -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function filterExports(source, filter) { - return source.filter(key => !filter.includes(key)); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUV2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBYSxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ3hDLE1BQU0sT0FBTyxHQUFXLGtCQUFrQixLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7SUFFNUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO2dCQUM5QyxNQUFNLE9BQU8sQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFRO2dCQUNYLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQztnQkFDN0MsTUFBTSxVQUFVLEdBQWEsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDOUMsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFDO2dCQUN2RCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDMUMsTUFBTSxVQUFVLENBQUMsR0FBRyxFQUFFLGNBQWMsRUFBRSxPQUFPLENBQUMsQ0FBQztpQkFDaEQ7Z0JBQ0QsT0FBTyxDQUFDLElBQUksQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFDO2dCQUNsRCxNQUFNLE9BQU8sQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUN4QyxPQUFPO1lBQ1QsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsb0NBQW9DLENBQUMsQ0FBQztnQkFDbkQsTUFBTSxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsZ0JBQWdCLEtBQUssQ0FBQyxTQUFTLEdBQUcsQ0FBQyxDQUFDO2dCQUN0RSxPQUFPO1lBQ1Q7Z0JBQ0UsT0FBTztTQUNWO0tBQ0Y7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNWLE9BQU8sQ0FBQyxLQUFLLENBQUMsOENBQThDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDakUsTUFBTSxDQUFDLENBQUM7S0FDVDtBQUNILENBQUM7QUFuQ0QsMEJBbUNDO0FBQUEsQ0FBQztBQUVGOztHQUVHO0FBQ0gsS0FBSyxVQUFVLE9BQU8sQ0FBQyxHQUFRLEVBQUUsVUFBb0IsRUFBRSxPQUFlO0lBQ3BFLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBQyxJQUFJLEVBQUMsRUFBRTtRQUM1QyxJQUFJO1lBQ0YsT0FBTyxHQUFHLENBQUMsaUJBQWlCLENBQUM7Z0JBQzNCLFVBQVUsRUFBRSxJQUFJO2dCQUNoQixZQUFZLEVBQUUsV0FBVztnQkFDekIsSUFBSSxFQUFFLENBQUM7d0JBQ0wsR0FBRyxFQUFFLE9BQU87d0JBQ1osS0FBSyxFQUFFLE1BQU07cUJBQ2QsQ0FBQzthQUNILENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztTQUNkO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLElBQUksS0FBSyxDQUFDLG1CQUFtQixJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztTQUNsRDtJQUNILENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDTixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxLQUFLLFVBQVUsVUFBVSxDQUFDLEdBQVEsRUFBRSxVQUFvQixFQUFFLE9BQWU7SUFDdkUsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFDLElBQUksRUFBQyxFQUFFO1FBQzVDLElBQUk7WUFDRixPQUFPLEdBQUcsQ0FBQyxzQkFBc0IsQ0FBQztnQkFDaEMsT0FBTyxFQUFFLENBQUMsT0FBTyxDQUFDO2dCQUNsQixZQUFZLEVBQUUsV0FBVztnQkFDekIsVUFBVSxFQUFFLElBQUk7YUFDakIsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1NBQ2Q7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLFFBQVEsQ0FBQyxDQUFDLElBQUksRUFBRTtnQkFDZCxrRUFBa0U7Z0JBQ2xFLEtBQUssbUJBQW1CO29CQUN0QixPQUFPO2dCQUNUO29CQUNFLE1BQU0sSUFBSSxLQUFLLENBQUMsMEJBQTBCLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2FBQzNEO1NBQ0Y7SUFDSCxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ04sQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILEtBQUssVUFBVSxtQkFBbUIsQ0FBQyxHQUFRLEVBQUUsSUFBWSxFQUFFLFNBQWtCO0lBQzNFLE1BQU0sVUFBVSxHQUFvQixFQUFFLENBQUM7SUFDdkMsT0FBTyxHQUFHLENBQUMsbUJBQW1CLENBQUM7UUFDN0IsSUFBSSxFQUFFLElBQUk7UUFDVixTQUFTLEVBQUUsU0FBUztLQUNyQixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBQyx5QkFBeUIsRUFBQyxFQUFFO1FBQ2xELFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyx5QkFBeUIsQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUM7UUFDL0QsSUFBSSx5QkFBeUIsQ0FBQyxTQUFTLEVBQUU7WUFDdkMsVUFBVSxDQUFDLElBQUksQ0FBQyxHQUFHLE1BQU0sbUJBQW1CLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSx5QkFBeUIsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1NBQy9GO1FBQ0MsT0FBTyxVQUFVLENBQUM7SUFDdEIsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxLQUFLLFVBQVUsc0JBQXNCLENBQUMsR0FBUSxFQUFFLElBQVk7SUFDMUQsTUFBTSxTQUFTLEdBQUcsTUFBTSxtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDdkQsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFhLENBQUM7SUFDOUUsTUFBTSxHQUFHLENBQUMsZ0JBQWdCLENBQUM7UUFDekIsS0FBSyxFQUFFLEtBQUs7S0FDYixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7QUFDZixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLGFBQWEsQ0FBQyxNQUFnQixFQUFFLE1BQWdCO0lBQ3ZELE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQ3JELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGltcG9ydHM6IHN0cmluZ1tdID0gcHJvcHMuSW1wb3J0cztcbiAgY29uc3Qga2V5TmFtZTogc3RyaW5nID0gYGNkay1zdHJvbmctcmVmOiR7cHJvcHMuU3RhY2tOYW1lfWA7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgYXdhaXQgYWRkVGFncyhzc20sIGltcG9ydHMsIGtleU5hbWUpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogc3RyaW5nW10gPSBvbGRQcm9wcy5JbXBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhpbXBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgY29uc3QgcGFyYW1zVG9EZWxldGUgPSBmaWx0ZXJFeHBvcnRzKG9sZEV4cG9ydHMsIGltcG9ydHMpO1xuICAgICAgICBjb25zb2xlLmluZm8oJ1JlbGVhc2luZyB1bnVzZWQgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGlmIChPYmplY3Qua2V5cyhwYXJhbXNUb0RlbGV0ZSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGF3YWl0IHJlbW92ZVRhZ3Moc3NtLCBwYXJhbXNUb0RlbGV0ZSwga2V5TmFtZSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc29sZS5pbmZvKCdUYWdnaW5nIG5ldyBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgYXdhaXQgYWRkVGFncyhzc20sIG5ld0V4cG9ydHMsIGtleU5hbWUpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdEZWxldGUnOlxuICAgICAgICBjb25zb2xlLmluZm8oJ0RlbGV0aW5nIGFsbCBTU00gUGFyYW1ldGVyIGV4cG9ydHMnKTtcbiAgICAgICAgYXdhaXQgZGVsZXRlUGFyYW1ldGVyc0J5UGF0aChzc20sIGAvY2RrL2V4cG9ydHMvJHtwcm9wcy5TdGFja05hbWV9L2ApO1xuICAgICAgICByZXR1cm47XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm47XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc29sZS5lcnJvcignRXJyb3IgaW1wb3J0aW5nIGNyb3NzIHJlZ2lvbiBzdGFjayBleHBvcnRzOiAnLCBlKTtcbiAgICB0aHJvdyBlO1xuICB9XG59O1xuXG4vKipcbiAqIEFkZCB0YWcgdG8gcGFyYW1ldGVycyBmb3IgZXhpc3RpbmcgZXhwb3J0c1xuICovXG5hc3luYyBmdW5jdGlvbiBhZGRUYWdzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBzdHJpbmdbXSwga2V5TmFtZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKHBhcmFtZXRlcnMubWFwKGFzeW5jIG5hbWUgPT4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gc3NtLmFkZFRhZ3NUb1Jlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgICAgVGFnczogW3tcbiAgICAgICAgICBLZXk6IGtleU5hbWUsXG4gICAgICAgICAgVmFsdWU6ICd0cnVlJyxcbiAgICAgICAgfV0sXG4gICAgICB9KS5wcm9taXNlKCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciBpbXBvcnRpbmcgJHtuYW1lfTogJHtlfWApO1xuICAgIH1cbiAgfSkpO1xufVxuXG4vKipcbiAqIFJlbW92ZSB0YWdzIGZyb20gcGFyYW1ldGVyc1xuICovXG5hc3luYyBmdW5jdGlvbiByZW1vdmVUYWdzKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBzdHJpbmdbXSwga2V5TmFtZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKHBhcmFtZXRlcnMubWFwKGFzeW5jIG5hbWUgPT4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gc3NtLnJlbW92ZVRhZ3NGcm9tUmVzb3VyY2Uoe1xuICAgICAgICBUYWdLZXlzOiBba2V5TmFtZV0sXG4gICAgICAgIFJlc291cmNlVHlwZTogJ1BhcmFtZXRlcicsXG4gICAgICAgIFJlc291cmNlSWQ6IG5hbWUsXG4gICAgICB9KS5wcm9taXNlKCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgc3dpdGNoIChlLmNvZGUpIHtcbiAgICAgICAgLy8gaWYgdGhlIHBhcmFtZXRlciBkb2Vzbid0IGV4aXN0IHRoZW4gdGhlcmUgaXMgbm90aGluZyB0byByZWxlYXNlXG4gICAgICAgIGNhc2UgJ0ludmFsaWRSZXNvdXJjZUlkJzpcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciByZWxlYXNpbmcgaW1wb3J0ICR7bmFtZX06ICR7ZX1gKTtcbiAgICAgIH1cbiAgICB9XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBHZXQgYWxsIHBhcmFtZXRlcnMgaW4gYSBnaXZlbiBwYXRoXG4gKlxuICogSWYgdGhlIHJlcXVlc3QgZmFpbHMgZm9yIGFueSByZWFzb24gaXQgd2lsbCBmYWlsIHRoZSBjdXN0b20gcmVzb3VyY2UgZXZlbnQuXG4gKiBTaW5jZSB0aGlzIGlzIG9ubHkgcnVuIHdoZW4gdGhlIHJlc291cmNlIGlzIGRlbGV0ZWQgdGhhdCBpcyBwcm9iYWJseSB0aGUgYmVoYXZpb3JcbiAqIHRoYXQgaXMgZGVzaXJlZC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0UGFyYW1ldGVyc0J5UGF0aChzc206IFNTTSwgcGF0aDogc3RyaW5nLCBuZXh0VG9rZW4/OiBzdHJpbmcpOiBQcm9taXNlPFNTTS5QYXJhbWV0ZXJbXT4ge1xuICBjb25zdCBwYXJhbWV0ZXJzOiBTU00uUGFyYW1ldGVyW10gPSBbXTtcbiAgcmV0dXJuIHNzbS5nZXRQYXJhbWV0ZXJzQnlQYXRoKHtcbiAgICBQYXRoOiBwYXRoLFxuICAgIE5leHRUb2tlbjogbmV4dFRva2VuLFxuICB9KS5wcm9taXNlKCkudGhlbihhc3luYyBnZXRQYXJhbWV0ZXJzQnlQYXRoUmVzdWx0ID0+IHtcbiAgICBwYXJhbWV0ZXJzLnB1c2goLi4uZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdC5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICBpZiAoZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdC5OZXh0VG9rZW4pIHtcbiAgICAgIHBhcmFtZXRlcnMucHVzaCguLi5hd2FpdCBnZXRQYXJhbWV0ZXJzQnlQYXRoKHNzbSwgcGF0aCwgZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdC5OZXh0VG9rZW4pKTtcbiAgICB9XG4gICAgICByZXR1cm4gcGFyYW1ldGVycztcbiAgfSk7XG59XG5cbi8qKlxuICogRGVsZXRlIGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZSBwYXRoXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBhbGxQYXJhbXMgPSBhd2FpdCBnZXRQYXJhbWV0ZXJzQnlQYXRoKHNzbSwgcGF0aCk7XG4gIGNvbnN0IG5hbWVzID0gYWxsUGFyYW1zLm1hcChwYXJhbSA9PiBwYXJhbS5OYW1lKS5maWx0ZXIoeCA9PiAhIXgpIGFzIHN0cmluZ1tdO1xuICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IG5hbWVzLFxuICB9KS5wcm9taXNlKCk7XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IHN0cmluZ1tdLCBmaWx0ZXI6IHN0cmluZ1tdKTogc3RyaW5nW10ge1xuICByZXR1cm4gc291cmNlLmZpbHRlcihrZXkgPT4gIWZpbHRlci5pbmNsdWRlcyhrZXkpKTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json index 889e19b14d748..d23d133596e3b 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json @@ -1,21 +1,21 @@ { "version": "21.0.0", "files": { - "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915": { + "050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf": { "source": { - "path": "asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915", + "path": "asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf", "packaging": "zip" }, "destinations": { "12345678-us-east-2": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip", + "objectKey": "050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } }, - "c6b357f1b87eb64cf51942fb6defee308cb2c19a05b1780418ff0b3fbd74758e": { + "8fcffd645cc61b1c9eccdc465ce88708586341f42dc5a067cadd1a6d00a72a9f": { "source": { "path": "integ-cloudfront-stack.template.json", "packaging": "file" @@ -23,7 +23,7 @@ "destinations": { "12345678-us-east-2": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "c6b357f1b87eb64cf51942fb6defee308cb2c19a05b1780418ff0b3fbd74758e.json", + "objectKey": "8fcffd645cc61b1c9eccdc465ce88708586341f42dc5a067cadd1a6d00a72a9f.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json index 06bb2c28e3cde..d80474d0862d1 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json @@ -99,7 +99,7 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", - "S3Key": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip" + "S3Key": "050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json index 0c9e8cb0e02aa..071ec3deea392 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json @@ -88,7 +88,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/c6b357f1b87eb64cf51942fb6defee308cb2c19a05b1780418ff0b3fbd74758e.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/8fcffd645cc61b1c9eccdc465ce88708586341f42dc5a067cadd1a6d00a72a9f.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ From 7ffd80be43f4b3087699c39d16cfe0072a6a7e31 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 13 Oct 2022 18:09:28 +0000 Subject: [PATCH 25/30] updates based on review comments - moving feature flag to stack property - renaming some things --- .../integ.core-cross-region-references.ts | 6 +- .../integ.cloudfront-cross-region-cert.ts | 6 +- .../test/integ.pipeline-with-replication.ts | 6 +- packages/@aws-cdk/core/README.md | 18 +- .../core/adr/cross-region-stack-references.md | 270 ++++++++++++++++++ .../cross-region-ssm-reader-handler/index.ts | 41 ++- .../cross-region-ssm-writer-handler/index.ts | 26 +- .../export-reader-provider.ts | 21 +- .../export-writer-provider.ts | 27 +- .../cross-region-export-providers/types.ts | 49 ++++ packages/@aws-cdk/core/lib/nested-stack.ts | 1 + packages/@aws-cdk/core/lib/private/refs.ts | 9 +- packages/@aws-cdk/core/lib/resource.ts | 6 +- packages/@aws-cdk/core/lib/stack.ts | 18 ++ .../core/test/cross-environment-token.test.ts | 20 +- .../cross-region-ssm-reader-handler.test.ts | 80 +++--- .../cross-region-ssm-writer-handler.test.ts | 107 +++---- .../export-writer-provider.test.ts | 71 +++-- .../@aws-cdk/core/test/nested-stack.test.ts | 36 +-- packages/@aws-cdk/core/test/stack.test.ts | 149 +++++----- packages/@aws-cdk/cx-api/README.md | 18 -- packages/@aws-cdk/cx-api/lib/features.ts | 8 - 22 files changed, 681 insertions(+), 312 deletions(-) create mode 100644 packages/@aws-cdk/core/adr/cross-region-stack-references.md rename packages/@aws-cdk/core/lib/custom-resource-provider/{ => cross-region-export-providers}/cross-region-ssm-reader-handler/index.ts (74%) rename packages/@aws-cdk/core/lib/custom-resource-provider/{ => cross-region-export-providers}/cross-region-ssm-writer-handler/index.ts (77%) rename packages/@aws-cdk/core/lib/custom-resource-provider/{ => cross-region-export-providers}/export-reader-provider.ts (84%) rename packages/@aws-cdk/core/lib/custom-resource-provider/{ => cross-region-export-providers}/export-writer-provider.ts (87%) create mode 100644 packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts index d3bdb1bee9ef3..6565d2311f8dc 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts @@ -1,16 +1,12 @@ import { Queue, IQueue } from '@aws-cdk/aws-sqs'; import { StringParameter } from '@aws-cdk/aws-ssm'; import { App, Stack, StackProps, NestedStack } from '@aws-cdk/core'; -import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { IntegTest } from '@aws-cdk/integ-tests'; import { Construct } from 'constructs'; // GIVEN const app = new App({ treeMetadata: false, - postCliContext: { - [ENABLE_CROSS_REGION_REFERENCES]: true, - }, }); class ProducerStack extends Stack { @@ -21,6 +17,7 @@ class ProducerStack extends Stack { env: { region: 'us-east-1', }, + optInToCrossRegionReferences: true, }); const nested = new NestedStack(this, 'IntegNested'); this.queue = new Queue(this, 'IntegQueue'); @@ -38,6 +35,7 @@ class ConsumerStack extends Stack { env: { region: 'us-east-2', }, + optInToCrossRegionReferences: true, }); const nested = new NestedStack(this, 'IntegNested'); diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts index 132de7f38185e..2b3d73ac562bd 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts @@ -1,7 +1,6 @@ import * as acm from '@aws-cdk/aws-certificatemanager'; import * as route53 from '@aws-cdk/aws-route53'; import * as cdk from '@aws-cdk/core'; -import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { IntegTest } from '@aws-cdk/integ-tests'; import * as cloudfront from '../lib'; import { TestOrigin } from './test-origin'; @@ -16,15 +15,13 @@ if (!domainName) throw new Error('For this test you must provide your own Domain const app = new cdk.App({ treeMetadata: false, - postCliContext: { - [ENABLE_CROSS_REGION_REFERENCES]: true, - }, }); const acmStack = new cdk.Stack(app, 'integ-acm-stack', { env: { region: 'us-east-1', account, }, + optInToCrossRegionReferences: true, }); const cloudFrontStack = new cdk.Stack(app, 'integ-cloudfront-stack', { @@ -32,6 +29,7 @@ const cloudFrontStack = new cdk.Stack(app, 'integ-cloudfront-stack', { region: 'us-east-2', account, }, + optInToCrossRegionReferences: true, }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts index b7d18a64c0d2c..1e0447f6347ee 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts @@ -3,26 +3,24 @@ import { Pipeline, Artifact } from '@aws-cdk/aws-codepipeline'; import { Key } from '@aws-cdk/aws-kms'; import { Bucket } from '@aws-cdk/aws-s3'; import { App, Stack, RemovalPolicy } from '@aws-cdk/core'; -import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { IntegTest } from '@aws-cdk/integ-tests'; import { S3SourceAction, CodeBuildAction } from '../lib'; const app = new App({ treeMetadata: false, - postCliContext: { - [ENABLE_CROSS_REGION_REFERENCES]: true, - }, }); const stack1 = new Stack(app, 'integ-pipeline-producer-stack', { env: { region: 'us-east-1', }, + optInToCrossRegionReferences: true, }); const stack2 = new Stack(app, 'integ-pipeline-consumer-stack', { env: { region: 'us-east-2', }, + optInToCrossRegionReferences: true, }); diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 28f317e533963..f78bc0ce4d2a9 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -164,19 +164,29 @@ other. ## Accessing resources in a different stack and region -You can enable the feature flag `@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources` +You can enable the Stack property `optInToCrossRegionReferences` in order to access resources in a different stack _and_ region. With this feature flag enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and an ACM certificate in `us-east-1`. ```ts -const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); +const stack1 = new Stack(app, 'Stack1', { + env: { + region: 'us-east-1', + }, + optInToCrossRegionReferences: true, +}); const cert = new acm.Certificate(stack1, 'Cert', { domainName: '*.example.com', validation: acm.CertificateValidation.fromDns(route53.PublicHostedZone.fromHostedZoneId(stack1, 'Zone', 'Z0329774B51CGXTDQV3X')), }); -const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); +const stack2 = new Stack(app, 'Stack2', { + env: { + region: 'us-east-2', + }, + optInToCrossRegionReferences: true, +}); new cloudfront.Distribution(stack2, 'Distribution', { defaultBehavior: { origin: new origins.HttpOrigin('example.com'), @@ -197,6 +207,8 @@ In order to mimic strong references, a Custom Resource is also created in the co stack which marks the SSM parameters as being "imported". When a parameter has been successfully imported, the producing stack cannot update the value. +See the [adr](./adr/cross-region-stack-references.md) for more details on this feature. + ### Removing automatic cross-stack references The automatic references created by CDK when you use resources across stacks diff --git a/packages/@aws-cdk/core/adr/cross-region-stack-references.md b/packages/@aws-cdk/core/adr/cross-region-stack-references.md new file mode 100644 index 0000000000000..c513279e75d0c --- /dev/null +++ b/packages/@aws-cdk/core/adr/cross-region-stack-references.md @@ -0,0 +1,270 @@ +# Cross Region Stack References + +## Status + +accepted + +## Context + +The CDK allows for you to natively (in code) reference resources between stacks. For example: + +```ts +const bucket = new s3.Bucket(stack1, 'Bucket'); +const handler = new lambda.Function(stack2, 'Handler'); +bucket.grantRead(handler); +``` + +Here we have create an S3 bucket in one stack and natively referenced the bucket from a resource +in a different stack. This works because CDK knows that this is a cross stack reference and will create the +appropriate stack exports and imports. In this case it would create an `Export` in `stack1`. + +```json +{ + "Outputs": { + "ExportsOutputFnGetAttBucket83908E77Arn063C8555": { + "Value": { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "Export": { + "Name": "stack1:ExportsOutputFnGetAttBucket83908E77Arn063C8555" + } + } + } +} +``` + +And an "Import" in stack2 + +```json +{ + "Resources": { + "HandlerServiceRoleDefaultPolicyCBD0CC91": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::ImportValue": "stack1:ExportsOutputFnGetAttBucket83908E77Arn063C8555" + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::ImportValue": "stack1:ExportsOutputFnGetAttBucket83908E77Arn063C8555" + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "HandlerServiceRoleDefaultPolicyCBD0CC91", + "Roles": [ + { + "Ref": "HandlerServiceRoleFCDC14AE" + } + ] + } + } + } +} +``` + +If these stack exist in different regions this is no longer possible. This is due to +an underlying limitation with CloudFormation, namely that Stack Exports/Imports do not +work cross-region. There are some cases where cross region references are _required_ +by the AWS services themselves. A good example of this is AWS CloudFront. CloudFront is a global +service and you can create a CloudFront distribution via CloudFormation in any AWS Region. Other +resources that are used by CloudFront are required to be created in the `us-east-1` region. + +For example, lets say you have an application that you created in `us-east-2`. + +```ts +const appStack = new Stack(app, 'AppStack', { env: { region: 'us-east-2' } }); +const service = new ApplicationLoadBalancedFargateService(appStack, 'Service'); +``` + +If I want to add CloudFront to this application. I can add the distribution in the +same Stack, but if I also want to add a ACM Certificate (why wouldn't I?) it becomes +more difficult. In order to use a ACM Certificate with CloudFront, the certificate +must be created in `us-east-1` regardless of what region you create the CloudFront +distribution from. + +```ts +const appStack = new Stack(app, 'AppStack', { env: { region: 'us-east-2' } }); +const service = new ApplicationLoadBalancedFargateService(appStack, 'Service'); + +// this won't work!!! +const certificate = new acm.Certificate(appStack, 'Cert'); +const distribution = new Distribution(appStack, 'Distribution', { + defaultBehavior: { origin: new LoadBalancerV2Origin(service.loadBalancer) }, + certificate, +}); +``` + +To workaround this issue we have created things like the `DnsValidatedCertificate` construct +which uses custom resources to create the certificate in `us-east-1`. This requires us +to essentially maintain our own `Certificate` resource that maintains feature parity with the +official `AWS::CertificateManager::Certificate` resource. + +Another example is the `aws-cloudfront.experimental` `EdgeFunction` construct. This takes +a different approach to managing cross region resources. Instead of creating the resources with +a custom resource, we instead create a support stack in `us-east-1` which creates the lambda +function. We then use a custom resource to "lookup" the function arn in the Stack that creates the +CloudFront distribution. This is becoming our recommended pattern for creating cross-region +resources, so why not add an officially supported method for doing this. + +## Constraints + +The biggest constraint with implementing a solution is CloudFormation itself. A common +request from CDK users is for the CDK to support [weak +references](https://github.com/aws/aws-cdk-rfcs/issues/82). The reason we have not yet implemented +this feature is that there are good reasons as to why strong references exist and are the only +officially supported method. + +Let's walk through an example to illustrate. Suppose I had a Lambda function that referenced an S3 +bucket in some way (read data, write data, etc). CloudFormation will create a "strong" reference +between these two resources. + +```ts +const bucket = new s3.Bucket(stack1, 'Bucket', { + bucketName: 'mybucket', +}); +const handler = new lambda.Function(stack2, 'Handler', { + environment: { + BUCKET_NAME: bucket.bucketName, + }, +}); +bucket.grantRead(handler); +``` + +If I tried to update the bucket, for example changing the name from `mybucket` to `myNewNameBucket` +CloudFormation will fail the deployment for stack1 and prevent the bucket from being recreated. This +is because it _knows_ that `stack2` is using the bucket. If it allows the bucket to change and the +export to change you could end up in an unrecoverable state for `stack2`. + +```mermaid +sequenceDiagram + Note over Stack1,Stack2: Initial Deployment + activate Stack2 + Note over Stack1: Create export mybucket + Stack2->>+Stack1: Read export mybucket + Note over Stack2: Create Function + deactivate Stack2 + Note over Stack1,Stack2: Second Deployment + activate Stack2 + Note over Stack1: Delete export mybucket + Note over Stack1: Create export myNewNameBucket + Stack2->>+Stack1: Read export myNewNameBucket + deactivate Stack2 + Note over Stack2: Update Function Failed! + Stack2-->>+Stack2: Rollback + activate Stack2 + Stack2->>+Stack1: Read export mybucket + Note right of Stack1: Export doesn't exist! + deactivate Stack2 + Note over Stack2: Stack rollback failed! + Note over Stack1,Stack2: We're stuck!! +``` + +For the CDK to implement it's own concept of references it needs to take this into account. + +### Custom Resources + +This solution utilizes custom resources to manage outputs/imports, and custom resources come with +their own constraints. + +- Custom resources are only executed when the properties change. There is no way to have the + resource execute on every deploy. + +- Custom resources only know about the current update (via `ResourceProperties`) and the previous update + (via `OldResourceProperties`). Custom resources cannot keep track of all prior updates, unless + we were to implement some external state mechanism. + + +## Decision + +The CDK will natively support cross region stack references. + +```ts +const appStack = new Stack(app, 'AppStack', { env: { region: 'us-east-2' } }); +const service = new ApplicationLoadBalancedFargateService(appStack, 'Service'); + +// this will work!!! +const certificate = new acm.Certificate(appStack, 'Cert'); +const distribution = new Distribution(appStack, 'Distribution', { + defaultBehavior: { origin: new LoadBalancerV2Origin(service.loadBalancer) }, + certificate, +}); +``` + +Since it is not natively supported by CloudFormation we will use CloudFormation custom resources to +perform the output/import. This behavior will not be enabled by default and will be controlled by an +optional Stack property. + +```ts +new Stack(app, 'MyStack', { + optInToCrossRegionReferences: true, +}); +``` + +### Outputs + +In order to "output" the value from the producing stack, a custom resource will be created in the +producing stack which will create an SSM parameter with a generated name in the consuming region. +For example the name might be `/cdk/exports/stack2/stack1useast1CertRefCert5C9F`. + +To implement strong references the custom resources will be allowed to create new outputs, but will +only be allowed to update/delete existing outputs if the output has _not_ been imported. If it has +been imported the stack update will fail (similar to the behavior of native exports). See +[Imports](#imports) for how the import is performed. + +### Imports + +The consuming stack will then "import" the value via a SSM dynamic reference. This is possible +because we know the name of the SSM parameter that the producing stack creates. This will look +something like `{{resolve:ssm:/cdk/exports/stack2/stack1useast1CertRefCert5C9F}}`. + +A custom resource will also be created in the consuming stack that will be responsible for marking +the SSM parameter as having been "imported". It will do so by adding a tag to the parameter, +something like `aws-cdk:strong-ref=stack2`. If the value is no longer imported by the stack then the +tag will be removed. The producing stack will use the presence of the tag to determine whether or +not the output can be updated/deleted. + +Since the imports for a stack are exported as SSM parameters with the stack name as part of the name +prefix, when the importing stack is deleted it will clean up and remove any SSM parameters under +that prefix. + +## Alternatives + +This solution uses a push model where the producing stack "pushes" the output to the target region. +One alternative that was considered was to use a pull model where the consuming stack would "pull" +the output from the producing region. For example the producing stack could produce a normal +CloudFormation export and then the consuming stack would have a custom resource the reads the +exports. + +This alternative had several limitations: +1. No way to implement strong references. CloudFormation would not know the export is being used and + would allow it to be updated/deleted. +2. The consuming custom resource would need to run every time the stack is deployed. This would + require introducing a salt that would cause a template diff on every deploy (not ideal). + +## Consequences + +If we add support for cross region references we will need to support cross region references going +forward. We will not be tied to this implementation though. diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts similarity index 74% rename from packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts rename to packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts index fd5e4422c2df2..7c92fde2bf1c7 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-reader-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts @@ -1,13 +1,14 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; +import { ExportReaderCRProps } from '../types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const imports: string[] = props.Imports; - const keyName: string = `cdk-strong-ref:${props.StackName}`; + const props: ExportReaderCRProps = event.ResourceProperties.ReaderProps; + const imports: string[] = props.imports; + const keyName: string = `aws-cdk:strong-ref:${props.prefix}`; - const ssm = new SSM({ region: props.Region }); + const ssm = new SSM({ region: props.region }); try { switch (event.RequestType) { case 'Create': @@ -15,10 +16,10 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent await addTags(ssm, imports, keyName); return; case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports: string[] = oldProps.Imports; - const newExports = filterExports(imports, oldExports); - const paramsToDelete = filterExports(oldExports, imports); + const oldProps: ExportReaderCRProps = event.OldResourceProperties.ReaderProps; + const oldExports: string[] = oldProps.imports; + const newExports = except(imports, oldExports); + const paramsToDelete = except(oldExports, imports); console.info('Releasing unused SSM Parameter imports'); if (Object.keys(paramsToDelete).length > 0) { await removeTags(ssm, paramsToDelete, keyName); @@ -28,7 +29,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent return; case 'Delete': console.info('Deleting all SSM Parameter exports'); - await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); + await deleteParametersByPath(ssm, `/cdk/exports/${props.prefix}/`); return; default: return; @@ -89,18 +90,16 @@ async function removeTags(ssm: SSM, parameters: string[], keyName: string): Prom * Since this is only run when the resource is deleted that is probably the behavior * that is desired. */ -async function getParametersByPath(ssm: SSM, path: string, nextToken?: string): Promise { +async function getParametersByPath(ssm: SSM, path: string): Promise { const parameters: SSM.Parameter[] = []; - return ssm.getParametersByPath({ - Path: path, - NextToken: nextToken, - }).promise().then(async getParametersByPathResult => { - parameters.push(...getParametersByPathResult.Parameters ?? []); - if (getParametersByPathResult.NextToken) { - parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); - } - return parameters; - }); + let nextToken: string | undefined; + do { + const response = await ssm.getParametersByPath({ Path: path, NextToken: nextToken }).promise(); + parameters.push(...response.Parameters ?? []); + nextToken = response.NextToken; + + } while (nextToken); + return parameters; } /** @@ -120,6 +119,6 @@ async function deleteParametersByPath(ssm: SSM, path: string): Promise { * @param source the source object to perform the filter on * @param filter filter out items that exist in this object */ -function filterExports(source: string[], filter: string[]): string[] { +function except(source: string[], filter: string[]): string[] { return source.filter(key => !filter.includes(key)); } diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler/index.ts similarity index 77% rename from packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts rename to packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler/index.ts index 69866b354da99..8970f2b163664 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-ssm-writer-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler/index.ts @@ -1,26 +1,26 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; -type CrossRegionExports = { [exportName: string]: string }; +import { CrossRegionExports, ExportWriterCRProps } from '../types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const exports: CrossRegionExports = props.Exports; + const props: ExportWriterCRProps = event.ResourceProperties.WriterProps; + const exports = props.exports as CrossRegionExports; - const ssm = new SSM({ region: props.Region }); + const ssm = new SSM({ region: props.region }); try { switch (event.RequestType) { case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await throwIfAnyInUse(ssm, exports); await putParameters(ssm, exports); return; case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports: CrossRegionExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); + const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports as CrossRegionExports; + const newExports = except(exports, oldExports); await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await putParameters(ssm, newExports); return; case 'Delete': @@ -61,10 +61,10 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis }).promise(); result.TagList?.forEach(tag => { const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'cdk-strong-ref') { + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[1]) - : tagResults.set(name, new Set([tagParts[1]])); + ? tagResults.get(name)!.add(tagParts[2]) + : tagResults.set(name, new Set([tagParts[2]])); } }); @@ -93,7 +93,7 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis * @param source the source object to perform the filter on * @param filter filter out items that exist in this object */ -function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { +function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) .reduce((acc: CrossRegionExports, curr: string) => { diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts similarity index 84% rename from packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts rename to packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts index 546f52fc2c24e..a6389b28c0c1d 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/export-reader-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts @@ -1,12 +1,12 @@ import * as path from 'path'; import { Construct } from 'constructs'; -import { CustomResource } from '../custom-resource'; -import { Lazy } from '../lazy'; -import { Stack } from '../stack'; -import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; -import { CfnResource } from '../cfn-resource'; +import { CfnResource } from '../../cfn-resource'; +import { CustomResource } from '../../custom-resource'; +import { Lazy } from '../../lazy'; +import { Stack } from '../../stack'; +import { CustomResourceProvider, CustomResourceProviderRuntime } from '../custom-resource-provider'; +import { SSM_EXPORT_PATH_PREFIX, ExportReaderCRProps } from './types'; -export const SSM_EXPORT_PATH_PREFIX = 'cdk/exports/'; /** * Properties for an ExportReader @@ -55,13 +55,16 @@ export class ExportReader extends Construct { }], }); + const properties: ExportReaderCRProps = { + region: stack.region, + prefix: stack.stackName, + imports: Lazy.list({ produce: () => this.importParametersNames }), + }; this.customResource = new CustomResource(this, 'Resource', { resourceType: resourceType, serviceToken, properties: { - Region: stack.region, - StackName: stack.stackName, - Imports: Lazy.list({ produce: () => this.importParametersNames }), + ReaderProps: properties, }, }); } diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts similarity index 87% rename from packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts rename to packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts index d6281a66c3557..6b2fa661e4dff 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/export-writer-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts @@ -1,17 +1,15 @@ import * as path from 'path'; import { Construct } from 'constructs'; -import { CfnDynamicReference, CfnDynamicReferenceService } from '../cfn-dynamic-reference'; -import { CustomResource } from '../custom-resource'; -import { Lazy } from '../lazy'; -import { Intrinsic } from '../private/intrinsic'; -import { makeUniqueId } from '../private/uniqueid'; -import { Reference } from '../reference'; -import { Stack } from '../stack'; -import { CustomResourceProvider, CustomResourceProviderRuntime } from './custom-resource-provider'; +import { CfnDynamicReference, CfnDynamicReferenceService } from '../../cfn-dynamic-reference'; +import { CustomResource } from '../../custom-resource'; +import { Lazy } from '../../lazy'; +import { Intrinsic } from '../../private/intrinsic'; +import { makeUniqueId } from '../../private/uniqueid'; +import { Reference } from '../../reference'; +import { Stack } from '../../stack'; +import { CustomResourceProvider, CustomResourceProviderRuntime } from '../custom-resource-provider'; import { ExportReader } from './export-reader-provider'; - -type CrossRegionExports = { [exportName: string]: string }; -export const SSM_EXPORT_PATH_PREFIX = 'cdk/exports/'; +import { CrossRegionExports, SSM_EXPORT_PATH_PREFIX, ExportWriterCRProps } from './types'; /** * Properties for an ExportReader @@ -81,12 +79,15 @@ export class ExportWriter extends Construct { }], }); + const properties: ExportWriterCRProps = { + region: region, + exports: Lazy.any({ produce: () => this._references }), + }; new CustomResource(this, 'Resource', { resourceType: resourceType, serviceToken, properties: { - Region: region, - Exports: Lazy.any({ produce: () => this._references }), + WriterProps: properties, }, }); } diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts new file mode 100644 index 0000000000000..b097da1cc8999 --- /dev/null +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts @@ -0,0 +1,49 @@ +import { IResolvable } from '../../resolvable'; + +/** + * The SSM parameter prefix that will be used for + * all cross region exports + */ +export const SSM_EXPORT_PATH_PREFIX = 'cdk/exports/'; + +/** + * Map of exportName to export value + */ +export type CrossRegionExports = { [exportName: string]: string }; + +/** + * Properties for the CrossRegionExportReader Custom Resource + */ +export interface ExportReaderCRProps { + /** + * The region that this resource exists in + */ + readonly region: string; + + /** + * An additional prefix to use. This will be appended + * to SSM_EXPORT_PATH_PREFIX. + */ + readonly prefix: string; + + /** + * A list of imports used by this stack. + * Will be a list of parameter names + */ + readonly imports: string[]; +} + +/** + * Properties for the CrossRegionExportWriter custom resource + */ +export interface ExportWriterCRProps { + /** + * The region to export the value to + */ + readonly region: string; + + /** + * A list of values to export to the target region + */ + readonly exports: CrossRegionExports | IResolvable; +} diff --git a/packages/@aws-cdk/core/lib/nested-stack.ts b/packages/@aws-cdk/core/lib/nested-stack.ts index 3adb30e677800..df7bf4a10189b 100644 --- a/packages/@aws-cdk/core/lib/nested-stack.ts +++ b/packages/@aws-cdk/core/lib/nested-stack.ts @@ -120,6 +120,7 @@ export class NestedStack extends Stack { env: { account: parentStack.account, region: parentStack.region }, synthesizer: new NestedStackSynthesizer(parentStack.synthesizer), description: props.description, + optInToCrossRegionReferences: parentStack._crossRegionReferences, }); this._parentStack = parentStack; diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index ed8006a978c47..b61fc5f5654d2 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -7,8 +7,7 @@ import { IConstruct } from 'constructs'; import { CfnElement } from '../cfn-element'; import { CfnOutput } from '../cfn-output'; import { CfnParameter } from '../cfn-parameter'; -import { ExportWriter } from '../custom-resource-provider/export-writer-provider'; -import { FeatureFlags } from '../feature-flags'; +import { ExportWriter } from '../custom-resource-provider/cross-region-export-providers/export-writer-provider'; import { Names } from '../names'; import { Reference } from '../reference'; import { IResolvable } from '../resolvable'; @@ -67,11 +66,11 @@ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { // Stacks are in the same account, but different regions - if (producerRegion !== consumerRegion && !FeatureFlags.of(consumer).isEnabled(cxapi.ENABLE_CROSS_REGION_REFERENCES)) { + if (producerRegion !== consumerRegion && !consumer._crossRegionReferences) { throw new Error( `Stack "${consumer.node.path}" cannot consume a cross reference from stack "${producer.node.path}". ` + 'Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack. ' + - `Set ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true to enable cross region references`); + 'Set optInToCrossRegionReferences=true to enable cross region references'); } // ---------------------------------------------------------------------- @@ -110,7 +109,7 @@ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { // ---------------------------------------------------------------------- // Stacks are in the same account, but different regions - if (producerRegion !== consumerRegion && FeatureFlags.of(consumer).isEnabled(cxapi.ENABLE_CROSS_REGION_REFERENCES)) { + if (producerRegion !== consumerRegion && consumer._crossRegionReferences) { if (producerRegion === cxapi.UNKNOWN_REGION || consumerRegion === cxapi.UNKNOWN_REGION) { throw new Error( `Stack "${consumer.node.path}" cannot consume a cross reference from stack "${producer.node.path}". ` + diff --git a/packages/@aws-cdk/core/lib/resource.ts b/packages/@aws-cdk/core/lib/resource.ts index 319d4ac728def..2135f5ccf1083 100644 --- a/packages/@aws-cdk/core/lib/resource.ts +++ b/packages/@aws-cdk/core/lib/resource.ts @@ -1,7 +1,5 @@ -import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { ArnComponents, ArnFormat } from './arn'; import { CfnResource } from './cfn-resource'; -import { FeatureFlags } from './feature-flags'; import { IStringProducer, Lazy } from './lazy'; import { generatePhysicalName, isGeneratedWhenNeededMarker } from './private/physical-name-generator'; import { Reference } from './reference'; @@ -260,7 +258,7 @@ export abstract class Resource extends Construct implements IResource { if (this.stack.account !== consumingStack.account || (this.stack.region !== consumingStack.region && - !FeatureFlags.of(consumingStack).isEnabled(ENABLE_CROSS_REGION_REFERENCES))) { + !consumingStack._crossRegionReferences)) { this._enableCrossEnvironment(); return this.physicalName; } else { @@ -293,7 +291,7 @@ export abstract class Resource extends Construct implements IResource { const consumingStack = Stack.of(context.scope); if (this.stack.account !== consumingStack.account || (this.stack.region !== consumingStack.region && - !FeatureFlags.of(consumingStack).isEnabled(ENABLE_CROSS_REGION_REFERENCES))) { + !consumingStack._crossRegionReferences)) { this._enableCrossEnvironment(); return this.stack.formatArn(arnComponents); } else { diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 421b91fa3ad2a..7d6a8257a5d89 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -139,6 +139,16 @@ export interface StackProps { * 'aws:cdk:version-reporting' context key */ readonly analyticsReporting?: boolean; + + /** + * Enable this flag to allow native cross region stack references. + * + * Enabling this will create a CloudFormation custom resource + * in both the producing stack and consuming stack in order to perform the export/import + * + * @default false + */ + readonly optInToCrossRegionReferences?: boolean; } /** @@ -300,6 +310,13 @@ export class Stack extends Construct implements ITaggable { */ public readonly _versionReportingEnabled: boolean; + /** + * Whether cross region references are enabled for this stack + * + * @internal + */ + public readonly _crossRegionReferences: boolean; + /** * Logical ID generation strategy */ @@ -344,6 +361,7 @@ export class Stack extends Construct implements ITaggable { this._missingContext = new Array(); this._stackDependencies = { }; this.templateOptions = { }; + this._crossRegionReferences = !!props.optInToCrossRegionReferences; Object.defineProperty(this, STACK_SYMBOL, { value: true }); diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index b8bd59b032b8d..7113fa842d2d2 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -1,4 +1,3 @@ -import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; import { App, CfnOutput, CfnResource, PhysicalName, Resource, Stack } from '../lib'; import { toCloudFormation } from './util'; @@ -187,7 +186,7 @@ describe('cross environment', () => { /Cannot use resource 'Stack1\/MyResource' in a cross-environment fashion/); }); - test(`can reference a deploy-time physical name across regions, when ${ENABLE_CROSS_REGION_REFERENCES}=true`, () => { + test('can reference a deploy-time physical name across regions, when optInToCrossRegionReferences=true', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { @@ -195,14 +194,15 @@ describe('cross environment', () => { account: '123456789012', region: 'bermuda-triangle-1337', }, + optInToCrossRegionReferences: true, }); const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789012', region: 'bermuda-triangle-42', }, + optInToCrossRegionReferences: true, }); - stack2.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); // WHEN const myResource = new MyResource(stack1, 'MyResource'); @@ -219,12 +219,14 @@ describe('cross environment', () => { 'ExportsWriterbermudatriangle42E59594276156AC73': { 'DeletionPolicy': 'Delete', 'Properties': { - 'Exports': { - '/cdk/exports/Stack2/Stack1bermudatriangle1337RefMyResource6073B41F66B72887': { - 'Ref': 'MyResource6073B41F', + 'WriterProps': { + 'exports': { + '/cdk/exports/Stack2/Stack1bermudatriangle1337RefMyResource6073B41F66B72887': { + 'Ref': 'MyResource6073B41F', + }, }, + 'region': 'bermuda-triangle-42', }, - 'Region': 'bermuda-triangle-42', 'ServiceToken': { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -243,7 +245,7 @@ describe('cross environment', () => { }); }); - test(`cannot reference a deploy-time physical name across regions, when ${ENABLE_CROSS_REGION_REFERENCES}=false`, () => { + test('cannot reference a deploy-time physical name across regions, when optInToCrossRegionReferences=false', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { @@ -251,12 +253,14 @@ describe('cross environment', () => { account: '123456789012', region: 'bermuda-triangle-1337', }, + optInToCrossRegionReferences: true, }); const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789012', region: 'bermuda-triangle-42', }, + optInToCrossRegionReferences: false, }); // WHEN diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts index d026bfbb184a6..9785f957312e4 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts @@ -1,5 +1,5 @@ -import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-reader-handler'; -import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/export-reader-provider'; +import { handler } from '../../lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler'; +import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/cross-region-export-providers/types'; let mockDeleteParameters: jest.Mock ; let mockAddTagsToResource: jest.Mock; @@ -53,10 +53,12 @@ describe('cross-region-ssm-reader entrypoint', () => { const event = makeEvent({ RequestType: 'Create', ResourceProperties: { + ReaderProps: { + region: 'us-east-1', + prefix: 'MyStack', + imports: ['/cdk/exports/MyStack/MyExport'], + }, ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Imports: ['/cdk/exports/MyStack/MyExport'], }, }); @@ -68,7 +70,7 @@ describe('cross-region-ssm-reader entrypoint', () => { ResourceId: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, ResourceType: 'Parameter', Tags: [{ - Key: 'cdk-strong-ref:MyStack', + Key: 'aws-cdk:strong-ref:MyStack', Value: 'true', }], }); @@ -80,20 +82,25 @@ describe('cross-region-ssm-reader entrypoint', () => { const event = makeEvent({ RequestType: 'Update', OldResourceProperties: { + ReaderProps: { + region: 'us-east-1', + prefix: 'MyStack', + imports: [ + '/cdk/exports/MyStack/ExistingExport', + ], + }, ServiceToken: '', - StackName: 'MyStack', - Imports: [ - '/cdk/exports/MyStack/ExistingExport', - ], }, ResourceProperties: { + ReaderProps: { + r: 'us-east-1', + prefix: 'MyStack', + imports: [ + '/cdk/exports/MyStack/ExistingExport', + '/cdk/exports/MyStack/MyExport', + ], + }, ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Imports: [ - '/cdk/exports/MyStack/ExistingExport', - '/cdk/exports/MyStack/MyExport', - ], }, }); @@ -105,7 +112,7 @@ describe('cross-region-ssm-reader entrypoint', () => { ResourceId: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, ResourceType: 'Parameter', Tags: [{ - Key: 'cdk-strong-ref:MyStack', + Key: 'aws-cdk:strong-ref:MyStack', Value: 'true', }], }); @@ -119,19 +126,24 @@ describe('cross-region-ssm-reader entrypoint', () => { const event = makeEvent({ RequestType: 'Update', OldResourceProperties: { + ReaderProps: { + region: 'us-east-1', + prefix: 'MyStack', + imports: [ + '/cdk/exports/MyStack/RemovedExport', + ], + }, ServiceToken: '', - StackName: 'MyStack', - Imports: [ - '/cdk/exports/MyStack/RemovedExport', - ], }, ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Imports: [ - '/cdk/exports/MyStack/MyExport', - ], + ReaderProps: { + region: 'us-east-1', + prefix: 'MyStack', + imports: [ + '/cdk/exports/MyStack/MyExport', + ], + }, }, }); @@ -143,14 +155,14 @@ describe('cross-region-ssm-reader entrypoint', () => { ResourceId: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, ResourceType: 'Parameter', Tags: [{ - Key: 'cdk-strong-ref:MyStack', + Key: 'aws-cdk:strong-ref:MyStack', Value: 'true', }], }); expect(mockRemoveTagsFromResource).toHaveBeenCalledWith({ ResourceId: '/cdk/exports/MyStack/RemovedExport', ResourceType: 'Parameter', - TagKeys: ['cdk-strong-ref:MyStack'], + TagKeys: ['aws-cdk:strong-ref:MyStack'], }); expect(mockDeleteParameters).toHaveBeenCalledTimes(0); }); @@ -161,11 +173,13 @@ describe('cross-region-ssm-reader entrypoint', () => { RequestType: 'Delete', ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Imports: [ - '/cdk/exports/MyStack/RemovedExport', - ], + ReaderProps: { + region: 'us-east-1', + prefix: 'MyStack', + imports: [ + '/cdk/exports/MyStack/RemovedExport', + ], + }, }, }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts index 18ac8bbe33bd9..e78bb47018dc3 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -1,5 +1,5 @@ -import { handler } from '../../lib/custom-resource-provider/cross-region-ssm-writer-handler'; -import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/export-writer-provider'; +import { handler } from '../../lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler'; +import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/cross-region-export-providers/types'; let mockPutParameter: jest.Mock ; let mocklistTagsForResource: jest.Mock; @@ -43,10 +43,11 @@ describe('cross-region-ssm-writer throws', () => { RequestType: 'Create', ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, }); @@ -55,7 +56,7 @@ describe('cross-region-ssm-writer throws', () => { mocklistTagsForResource.mockImplementation(() => { return { TagList: [{ - Key: 'cdk-strong-ref:MyStack', + Key: 'aws-cdk:strong-ref:MyStack', Value: 'true', }], }; @@ -71,19 +72,21 @@ describe('cross-region-ssm-writer throws', () => { RequestType: 'Update', OldResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/AlreadyExists': 'Value', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'Value', + }, }, }, }); @@ -92,7 +95,7 @@ describe('cross-region-ssm-writer throws', () => { mocklistTagsForResource.mockImplementation(() => { return { TagList: [{ - Key: 'cdk-strong-ref:MyStack', + Key: 'aws-cdk:strong-ref:MyStack', Value: 'true', }], }; @@ -108,20 +111,22 @@ describe('cross-region-ssm-writer throws', () => { RequestType: 'Update', OldResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/AlreadyExists': 'Original', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'Original', + }, }, }, ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/AlreadyExists': 'NewValue', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'NewValue', + }, }, }, }); @@ -134,7 +139,7 @@ describe('cross-region-ssm-writer throws', () => { }); return { TagList: [{ - Key: 'cdk-strong-ref:MyStack', + Key: 'aws-cdk:strong-ref:MyStack', Value: 'true', }], }; @@ -152,10 +157,11 @@ describe('cross-region-ssm-writer entrypoint', () => { RequestType: 'Create', ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, }); @@ -179,10 +185,11 @@ describe('cross-region-ssm-writer entrypoint', () => { RequestType: 'Create', ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, }); @@ -209,18 +216,21 @@ describe('cross-region-ssm-writer entrypoint', () => { RequestType: 'Update', OldResourceProperties: { ServiceToken: '', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + }, }, }, ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', - '/cdk/exports/MyStack/MyExport': 'Value', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, }); @@ -244,10 +254,11 @@ describe('cross-region-ssm-writer entrypoint', () => { RequestType: 'Delete', ResourceProperties: { ServiceToken: '', - Region: 'us-east-1', - StackName: 'MyStack', - Exports: { - '/cdk/exports/MyStack/RemovedExport': 'RemovedValue', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/RemovedExport': 'RemovedValue', + }, }, }, }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index bdb3ddb6608e5..b0282b0f1e884 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -1,5 +1,5 @@ import { App, Stack, AssetStaging, CfnResource, NestedStack } from '../../lib'; -import { ExportWriter } from '../../lib/custom-resource-provider/export-writer-provider'; +import { ExportWriter } from '../../lib/custom-resource-provider/cross-region-export-providers/export-writer-provider'; import { toCloudFormation } from '../util'; @@ -90,21 +90,23 @@ describe('export writer provider', () => { ExportWriterA770449C: { DeletionPolicy: 'Delete', Properties: { - Region: 'us-east-1', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyResourceName': { + 'Fn::GetAtt': [ + 'MyResource', + 'arn', + ], + }, + }, + }, ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', 'Arn', ], }, - Exports: { - '/cdk/exports/MyResourceName': { - 'Fn::GetAtt': [ - 'MyResource', - 'arn', - ], - }, - }, }, Type: 'Custom::CrossRegionExportWriter', UpdateReplacePolicy: 'Delete', @@ -226,11 +228,14 @@ describe('export writer provider', () => { ExportsReader8B249524: { DeletionPolicy: 'Delete', Properties: { - Imports: [ - '/cdk/exports/MyResourceName', - ], - Region: { - Ref: 'AWS::Region', + ReaderProps: { + imports: [ + '/cdk/exports/MyResourceName', + ], + region: { + Ref: 'AWS::Region', + }, + prefix: 'Stack2', }, ServiceToken: { 'Fn::GetAtt': [ @@ -238,7 +243,6 @@ describe('export writer provider', () => { 'Arn', ], }, - StackName: 'Stack2', }, Type: 'Custom::CrossRegionExportReader', UpdateReplacePolicy: 'Delete', @@ -334,21 +338,24 @@ describe('export writer provider', () => { ExportWriterA770449C: { DeletionPolicy: 'Delete', Properties: { - Region: 'us-east-1', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyResourceName': { + 'Fn::GetAtt': [ + 'MyResource', + 'arn', + ], + }, + }, + + }, ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', 'Arn', ], }, - Exports: { - '/cdk/exports/MyResourceName': { - 'Fn::GetAtt': [ - 'MyResource', - 'arn', - ], - }, - }, }, Type: 'Custom::CrossRegionExportWriter', UpdateReplacePolicy: 'Delete', @@ -470,11 +477,14 @@ describe('export writer provider', () => { ExportsReader8B249524: { DeletionPolicy: 'Delete', Properties: { - Imports: [ - '/cdk/exports/MyResourceName', - ], - Region: { - Ref: 'AWS::Region', + ReaderProps: { + imports: [ + '/cdk/exports/MyResourceName', + ], + region: { + Ref: 'AWS::Region', + }, + prefix: 'Stack2', }, ServiceToken: { 'Fn::GetAtt': [ @@ -482,7 +492,6 @@ describe('export writer provider', () => { 'Arn', ], }, - StackName: 'Stack2', }, Type: 'Custom::CrossRegionExportReader', UpdateReplacePolicy: 'Delete', diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 3ce5c150fc4c3..8ca9d890e4764 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -1,5 +1,4 @@ import * as path from 'path'; -import { ENABLE_CROSS_REGION_REFERENCES } from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; import { readFileSync } from 'fs-extra'; import { @@ -36,7 +35,7 @@ describe('nested-stack', () => { expect(nestedStack.templateOptions.description).toEqual(description); }); - test(`can create cross region references when ${ENABLE_CROSS_REGION_REFERENCES}=true`, () => { + test('can create cross region references when optInToCrossRegionReferences=true', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { @@ -44,14 +43,15 @@ describe('nested-stack', () => { account: '123456789012', region: 'bermuda-triangle-1337', }, + optInToCrossRegionReferences: true, }); const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789012', region: 'bermuda-triangle-42', }, + optInToCrossRegionReferences: true, }); - stack2.node.setContext(ENABLE_CROSS_REGION_REFERENCES, true); const nestedStack = new NestedStack(stack1, 'Nested1'); const nestedStack2 = new NestedStack(stack2, 'Nested2'); @@ -83,17 +83,19 @@ describe('nested-stack', () => { ExportsReader8B249524: { DeletionPolicy: 'Delete', Properties: { - Imports: [ - '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E', - ], - Region: 'bermuda-triangle-42', + ReaderProps: { + imports: [ + '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E', + ], + region: 'bermuda-triangle-42', + prefix: 'Stack2', + }, ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', 'Arn', ], }, - StackName: 'Stack2', }, DependsOn: [ 'Nested2NestedStackNested2NestedStackResource877A1112', @@ -116,15 +118,17 @@ describe('nested-stack', () => { ExportsWriterbermudatriangle42E59594276156AC73: { DeletionPolicy: 'Delete', Properties: { - Exports: { - '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E': { - 'Fn::GetAtt': [ - 'Nested1NestedStackNested1NestedStackResourceCD0AD36B', - 'Outputs.Stack1Nested1Resource178AEB067Ref', - ], + WriterProps: { + exports: { + '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E': { + 'Fn::GetAtt': [ + 'Nested1NestedStackNested1NestedStackResourceCD0AD36B', + 'Outputs.Stack1Nested1Resource178AEB067Ref', + ], + }, }, + region: 'bermuda-triangle-42', }, - Region: 'bermuda-triangle-42', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -138,7 +142,7 @@ describe('nested-stack', () => { }); }); - test(`cannot create cross region references when ${ENABLE_CROSS_REGION_REFERENCES}=false`, () => { + test('cannot create cross region references when optInToCrossRegionReferences=false', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 27f76efa7c341..6f2fa0c2dfab0 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -462,15 +462,14 @@ describe('stack', () => { }); }); - test(`cross-region stack references, ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true`, () => { + test('cross-region stack references, optInToCrossRegionReferences=true', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); - stack2.node.setContext(cxapi.ENABLE_CROSS_REGION_REFERENCES, true); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, optInToCrossRegionReferences: true }); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { @@ -494,15 +493,17 @@ describe('stack', () => { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { - Exports: { - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'name', - ], + WriterProps: { + exports: { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, }, + region: 'us-east-2', }, - Region: 'us-east-2', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -529,7 +530,7 @@ describe('stack', () => { test('cross-region stack references throws error', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); @@ -546,22 +547,21 @@ describe('stack', () => { // THEN expect(() => { app.synth(); - }).toThrow(/Set @aws-cdk\/core:enableCrossRegionReferencesUsingCustomResources=true to enable cross region references/); + }).toThrow(/Set optInToCrossRegionReferences=true to enable cross region references/); }); - test(`cross region stack references with multiple stacks, ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true`, () => { + test('cross region stack references with multiple stacks, optInToCrossRegionReferences=true', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-east-1' } }); + const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); const exportResource3 = new CfnResource(stack3, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); - stack2.node.setContext(cxapi.ENABLE_CROSS_REGION_REFERENCES, true); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, optInToCrossRegionReferences: true }); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { @@ -625,19 +625,21 @@ describe('stack', () => { ExportsReader8B249524: { DeletionPolicy: 'Delete', Properties: { - Imports: [ - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F', - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1', - '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B', - ], - Region: 'us-east-2', + ReaderProps: { + imports: [ + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1', + '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B', + ], + region: 'us-east-2', + prefix: 'Stack2', + }, ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportReaderCustomResourceProviderHandler46647B68', 'Arn', ], }, - StackName: 'Stack2', }, Type: 'Custom::CrossRegionExportReader', UpdateReplacePolicy: 'Delete', @@ -661,15 +663,17 @@ describe('stack', () => { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { - Exports: { - '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B': { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other2', - ], + WriterProps: { + exports: { + '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other2', + ], + }, }, + region: 'us-east-2', }, - Region: 'us-east-2', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -689,21 +693,23 @@ describe('stack', () => { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { - Exports: { - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'name', - ], - }, - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1': { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other', - ], + WriterProps: { + exports: { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other', + ], + }, }, + region: 'us-east-2', }, - Region: 'us-east-2', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -716,19 +722,18 @@ describe('stack', () => { }); }); - test(`cross region stack references with multiple stacks and multiple regions, ${cxapi.ENABLE_CROSS_REGION_REFERENCES}=true`, () => { + test('cross region stack references with multiple stacks and multiple regions, optInToCrossRegionReferences=true', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-west-1' } }); + const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-west-1' }, optInToCrossRegionReferences: true }); const exportResource3 = new CfnResource(stack3, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); - stack2.node.setContext(cxapi.ENABLE_CROSS_REGION_REFERENCES, true); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, optInToCrossRegionReferences: true }); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { @@ -774,15 +779,17 @@ describe('stack', () => { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { - Exports: { - '/cdk/exports/Stack2/Stack3uswest1FnGetAttSomeResourceExportother2491B5DA7': { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other2', - ], + WriterProps: { + exports: { + '/cdk/exports/Stack2/Stack3uswest1FnGetAttSomeResourceExportother2491B5DA7': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other2', + ], + }, }, + region: 'us-east-2', }, - Region: 'us-east-2', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', @@ -802,21 +809,23 @@ describe('stack', () => { Type: 'Custom::CrossRegionExportWriter', DeletionPolicy: 'Delete', Properties: { - Exports: { - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'name', - ], - }, - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1': { - 'Fn::GetAtt': [ - 'SomeResourceExport', - 'other', - ], + WriterProps: { + exports: { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'name', + ], + }, + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1': { + 'Fn::GetAtt': [ + 'SomeResourceExport', + 'other', + ], + }, }, + region: 'us-east-2', }, - Region: 'us-east-2', ServiceToken: { 'Fn::GetAtt': [ 'CustomCrossRegionExportWriterCustomResourceProviderHandlerD8786E8A', diff --git a/packages/@aws-cdk/cx-api/README.md b/packages/@aws-cdk/cx-api/README.md index 8ee777c3832b8..0a15d225ee257 100644 --- a/packages/@aws-cdk/cx-api/README.md +++ b/packages/@aws-cdk/cx-api/README.md @@ -105,21 +105,3 @@ becomes: AWS: "arn:aws:iam::123456789876:root" ``` -* `@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources` - - -Enable this feature flag to allow native cross region stack references. This will use a CloudFormation -Custom Resource to perform the cross region export. - -This is disabled by default. - -_cdk.json_ - -```json -{ - "context": { - "@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources": true - } -} -``` - diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index 8c96dff076541..e991c1e285b45 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -352,14 +352,6 @@ export const APIGATEWAY_DISABLE_CLOUDWATCH_ROLE = '@aws-cdk/aws-apigateway:disab */ export const ENABLE_PARTITION_LITERALS = '@aws-cdk/core:enablePartitionLiterals'; -/** - * Enable this feature flag to allow native cross region stack references. This will use a CloudFormation - * Custom Resource to perform the cross region export. - * - * @default false - */ -export const ENABLE_CROSS_REGION_REFERENCES = '@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources'; - /** * Flag values that should apply for new projects * From 5fe1d7b967087414fefb265e9a03009d2d0a70bc Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 13 Oct 2022 19:40:09 +0000 Subject: [PATCH 26/30] updating integ-tests and readme --- .../__entrypoint__.js | 144 ++++++++++++++++++ .../index.d.ts | 0 .../index.js | 102 +++++++++++++ .../index.ts | 26 ++-- .../__entrypoint__.js | 118 -------------- .../index.js | 102 ------------- .../cross-region-consumer.assets.json | 10 +- .../cross-region-consumer.template.json | 20 +-- .../cross-region-producer.assets.json | 10 +- .../cross-region-producer.template.json | 30 ++-- .../manifest.json | 4 +- .../__entrypoint__.js | 118 -------------- .../index.js | 127 --------------- .../__entrypoint__.js | 144 ++++++++++++++++++ .../index.d.ts | 0 .../index.js | 102 +++++++++++++ .../index.ts | 26 ++-- .../__entrypoint__.js | 144 ++++++++++++++++++ .../index.d.ts | 0 .../index.js | 124 +++++++++++++++ .../index.ts | 41 +++-- .../__entrypoint__.js | 118 -------------- .../index.js | 102 ------------- .../integ-acm-stack.assets.json | 10 +- .../integ-acm-stack.template.json | 12 +- .../integ-cloudfront-stack.assets.json | 10 +- .../integ-cloudfront-stack.template.json | 14 +- .../manifest.json | 4 +- .../__entrypoint__.js | 144 ++++++++++++++++++ .../index.d.ts | 0 .../index.js | 102 +++++++++++++ .../index.ts | 26 ++-- .../__entrypoint__.js | 118 -------------- .../index.js | 102 ------------- .../integ-pipeline-consumer-stack.assets.json | 16 +- ...nteg-pipeline-consumer-stack.template.json | 18 ++- .../integ-pipeline-producer-stack.assets.json | 16 +- ...nteg-pipeline-producer-stack.template.json | 26 ++-- .../manifest.json | 4 +- packages/@aws-cdk/core/README.md | 3 +- packages/aws-cdk-lib/README.md | 19 ++- 41 files changed, 1191 insertions(+), 1065 deletions(-) create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js rename packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/{asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc => asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js rename packages/@aws-cdk/{aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc => aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03}/index.ts (77%) delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/__entrypoint__.js delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf => asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js rename packages/@aws-cdk/{aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc => aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03}/index.ts (77%) create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/__entrypoint__.js rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc => asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf => asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878}/index.ts (74%) delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc => asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03}/index.d.ts (100%) create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc => asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03}/index.ts (77%) delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js new file mode 100644 index 0000000000000..1e3a3093c1706 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js @@ -0,0 +1,144 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, exports.external.sendHttpRequest)(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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RUFBRSxFQUFFO1FBQ3hCLElBQUksUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDaEMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUN2QixPQUFPLElBQUksRUFBRTtZQUNYLElBQUk7Z0JBQ0YsT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2FBQ3hCO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsSUFBSSxRQUFRLEVBQUUsSUFBSSxDQUFDLEVBQUU7b0JBQ25CLE1BQU0sQ0FBQyxDQUFDO2lCQUNUO2dCQUNELE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDVDtTQUNGO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQWhCRCxrQ0FnQkM7QUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDN0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBodHRwcyBmcm9tICdodHRwcyc7XG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcblxuLy8gZm9yIHVuaXQgdGVzdHNcbmV4cG9ydCBjb25zdCBleHRlcm5hbCA9IHtcbiAgc2VuZEh0dHBSZXF1ZXN0OiBkZWZhdWx0U2VuZEh0dHBSZXF1ZXN0LFxuICBsb2c6IGRlZmF1bHRMb2csXG4gIGluY2x1ZGVTdGFja1RyYWNlczogdHJ1ZSxcbiAgdXNlckhhbmRsZXJJbmRleDogJy4vaW5kZXgnLFxufTtcblxuY29uc3QgQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpDUkVBVEVfRkFJTEVEJztcbmNvbnN0IE1JU1NJTkdfUEhZU0lDQUxfSURfTUFSS0VSID0gJ0FXU0NESzo6Q3VzdG9tUmVzb3VyY2VQcm92aWRlckZyYW1ld29yazo6TUlTU0lOR19QSFlTSUNBTF9JRCc7XG5cbmV4cG9ydCB0eXBlIFJlc3BvbnNlID0gQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIEhhbmRsZXJSZXNwb25zZTtcbmV4cG9ydCB0eXBlIEhhbmRsZXIgPSAoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSA9PiBQcm9taXNlPEhhbmRsZXJSZXNwb25zZSB8IHZvaWQ+O1xuZXhwb3J0IHR5cGUgSGFuZGxlclJlc3BvbnNlID0gdW5kZWZpbmVkIHwge1xuICBEYXRhPzogYW55O1xuICBQaHlzaWNhbFJlc291cmNlSWQ/OiBzdHJpbmc7XG4gIFJlYXNvbj86IHN0cmluZztcbiAgTm9FY2hvPzogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50LCBjb250ZXh0OiBBV1NMYW1iZGEuQ29udGV4dCkge1xuICBjb25zdCBzYW5pdGl6ZWRFdmVudCA9IHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9O1xuICBleHRlcm5hbC5sb2coSlNPTi5zdHJpbmdpZnkoc2FuaXRpemVkRXZlbnQsIHVuZGVmaW5lZCwgMikpO1xuXG4gIC8vIGlnbm9yZSBERUxFVEUgZXZlbnQgd2hlbiB0aGUgcGh5c2ljYWwgcmVzb3VyY2UgSUQgaXMgdGhlIG1hcmtlciB0aGF0XG4gIC8vIGluZGljYXRlcyB0aGF0IHRoaXMgREVMRVRFIGlzIGEgc3Vic2VxdWVudCBERUxFVEUgdG8gYSBmYWlsZWQgQ1JFQVRFXG4gIC8vIG9wZXJhdGlvbi5cbiAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgPT09IENSRUFURV9GQUlMRURfUEhZU0lDQUxfSURfTUFSS0VSKSB7XG4gICAgZXh0ZXJuYWwubG9nKCdpZ25vcmluZyBERUxFVEUgZXZlbnQgY2F1c2VkIGJ5IGEgZmFpbGVkIENSRUFURSBldmVudCcpO1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgZXZlbnQpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gaW52b2tlIHRoZSB1c2VyIGhhbmRsZXIuIHRoaXMgaXMgaW50ZW50aW9uYWxseSBpbnNpZGUgdGhlIHRyeS1jYXRjaCB0b1xuICAgIC8vIGVuc3VyZSB0aGF0IGlmIHRoZXJlIGlzIGFuIGVycm9yIGl0J3MgcmVwb3J0ZWQgYXMgYSBmYWlsdXJlIHRvXG4gICAgLy8gY2xvdWRmb3JtYXRpb24gKG90aGVyd2lzZSBjZm4gd2FpdHMpLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgdXNlckhhbmRsZXI6IEhhbmRsZXIgPSByZXF1aXJlKGV4dGVybmFsLnVzZXJIYW5kbGVySW5kZXgpLmhhbmRsZXI7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdXNlckhhbmRsZXIoc2FuaXRpemVkRXZlbnQsIGNvbnRleHQpO1xuXG4gICAgLy8gdmFsaWRhdGUgdXNlciByZXNwb25zZSBhbmQgY3JlYXRlIHRoZSBjb21iaW5lZCBldmVudFxuICAgIGNvbnN0IHJlc3BvbnNlRXZlbnQgPSByZW5kZXJSZXNwb25zZShldmVudCwgcmVzdWx0KTtcblxuICAgIC8vIHN1Ym1pdCB0byBjZm4gYXMgc3VjY2Vzc1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgcmVzcG9uc2VFdmVudCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zdCByZXNwOiBSZXNwb25zZSA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgUmVhc29uOiBleHRlcm5hbC5pbmNsdWRlU3RhY2tUcmFjZXMgPyBlLnN0YWNrIDogZS5tZXNzYWdlLFxuICAgIH07XG5cbiAgICBpZiAoIXJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgICAvLyBzcGVjaWFsIGNhc2U6IGlmIENSRUFURSBmYWlscywgd2hpY2ggdXN1YWxseSBpbXBsaWVzLCB3ZSB1c3VhbGx5IGRvbid0XG4gICAgICAvLyBoYXZlIGEgcGh5c2ljYWwgcmVzb3VyY2UgaWQuIGluIHRoaXMgY2FzZSwgdGhlIHN1YnNlcXVlbnQgREVMRVRFXG4gICAgICAvLyBvcGVyYXRpb24gZG9lcyBub3QgaGF2ZSBhbnkgbWVhbmluZywgYW5kIHdpbGwgbGlrZWx5IGZhaWwgYXMgd2VsbC4gdG9cbiAgICAgIC8vIGFkZHJlc3MgdGhpcywgd2UgdXNlIGEgbWFya2VyIHNvIHRoZSBwcm92aWRlciBmcmFtZXdvcmsgY2FuIHNpbXBseVxuICAgICAgLy8gaWdub3JlIHRoZSBzdWJzZXF1ZW50IERFTEVURS5cbiAgICAgIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScpIHtcbiAgICAgICAgZXh0ZXJuYWwubG9nKCdDUkVBVEUgZmFpbGVkLCByZXNwb25kaW5nIHdpdGggYSBtYXJrZXIgcGh5c2ljYWwgcmVzb3VyY2UgaWQgc28gdGhhdCB0aGUgc3Vic2VxdWVudCBERUxFVEUgd2lsbCBiZSBpZ25vcmVkJyk7XG4gICAgICAgIHJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkID0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBvdGhlcndpc2UsIGlmIFBoeXNpY2FsUmVzb3VyY2VJZCBpcyBub3Qgc3BlY2lmaWVkLCBzb21ldGhpbmcgaXNcbiAgICAgICAgLy8gdGVycmlibHkgd3JvbmcgYmVjYXVzZSBhbGwgb3RoZXIgZXZlbnRzIHNob3VsZCBoYXZlIGFuIElELlxuICAgICAgICBleHRlcm5hbC5sb2coYEVSUk9SOiBNYWxmb3JtZWQgZXZlbnQuIFwiUGh5c2ljYWxSZXNvdXJjZUlkXCIgaXMgcmVxdWlyZWQ6ICR7SlNPTi5zdHJpbmdpZnkoZXZlbnQpfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRoaXMgaXMgYW4gYWN0dWFsIGVycm9yLCBmYWlsIHRoZSBhY3Rpdml0eSBhbHRvZ2V0aGVyIGFuZCBleGlzdC5cbiAgICBhd2FpdCBzdWJtaXRSZXNwb25zZSgnRkFJTEVEJywgcmVzcCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVuZGVyUmVzcG9uc2UoXG4gIGNmblJlcXVlc3Q6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQgJiB7IFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZyB9LFxuICBoYW5kbGVyUmVzcG9uc2U6IHZvaWQgfCBIYW5kbGVyUmVzcG9uc2UgPSB7IH0pOiBSZXNwb25zZSB7XG5cbiAgLy8gaWYgcGh5c2ljYWwgSUQgaXMgbm90IHJldHVybmVkLCB3ZSBoYXZlIHNvbWUgZGVmYXVsdHMgZm9yIHlvdSBiYXNlZFxuICAvLyBvbiB0aGUgcmVxdWVzdCB0eXBlLlxuICBjb25zdCBwaHlzaWNhbFJlc291cmNlSWQgPSBoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUmVxdWVzdElkO1xuXG4gIC8vIGlmIHdlIGFyZSBpbiBERUxFVEUgYW5kIHBoeXNpY2FsIElEIHdhcyBjaGFuZ2VkLCBpdCdzIGFuIGVycm9yLlxuICBpZiAoY2ZuUmVxdWVzdC5SZXF1ZXN0VHlwZSA9PT0gJ0RlbGV0ZScgJiYgcGh5c2ljYWxSZXNvdXJjZUlkICE9PSBjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgREVMRVRFOiBjYW5ub3QgY2hhbmdlIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBmcm9tIFwiJHtjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZH1cIiB0byBcIiR7aGFuZGxlclJlc3BvbnNlLlBoeXNpY2FsUmVzb3VyY2VJZH1cIiBkdXJpbmcgZGVsZXRpb25gKTtcbiAgfVxuXG4gIC8vIG1lcmdlIHJlcXVlc3QgZXZlbnQgYW5kIHJlc3VsdCBldmVudCAocmVzdWx0IHByZXZhaWxzKS5cbiAgcmV0dXJuIHtcbiAgICAuLi5jZm5SZXF1ZXN0LFxuICAgIC4uLmhhbmRsZXJSZXNwb25zZSxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IHBoeXNpY2FsUmVzb3VyY2VJZCxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3VibWl0UmVzcG9uc2Uoc3RhdHVzOiAnU1VDQ0VTUycgfCAnRkFJTEVEJywgZXZlbnQ6IFJlc3BvbnNlKSB7XG4gIGNvbnN0IGpzb246IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlUmVzcG9uc2UgPSB7XG4gICAgU3RhdHVzOiBzdGF0dXMsXG4gICAgUmVhc29uOiBldmVudC5SZWFzb24gPz8gc3RhdHVzLFxuICAgIFN0YWNrSWQ6IGV2ZW50LlN0YWNrSWQsXG4gICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgfHwgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIsXG4gICAgTG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgIE5vRWNobzogZXZlbnQuTm9FY2hvLFxuICAgIERhdGE6IGV2ZW50LkRhdGEsXG4gIH07XG5cbiAgZXh0ZXJuYWwubG9nKCdzdWJtaXQgcmVzcG9uc2UgdG8gY2xvdWRmb3JtYXRpb24nLCBqc29uKTtcblxuICBjb25zdCByZXNwb25zZUJvZHkgPSBKU09OLnN0cmluZ2lmeShqc29uKTtcbiAgY29uc3QgcGFyc2VkVXJsID0gdXJsLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgY29uc3QgcmVxID0ge1xuICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgcGF0aDogcGFyc2VkVXJsLnBhdGgsXG4gICAgbWV0aG9kOiAnUFVUJyxcbiAgICBoZWFkZXJzOiB7ICdjb250ZW50LXR5cGUnOiAnJywgJ2NvbnRlbnQtbGVuZ3RoJzogcmVzcG9uc2VCb2R5Lmxlbmd0aCB9LFxuICB9O1xuXG4gIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHtcbiAgICBhdHRlbXB0czogNSxcbiAgICBzbGVlcDogMTAwMCxcbiAgfTtcbiAgYXdhaXQgd2l0aFJldHJpZXMocmV0cnlPcHRpb25zLCBleHRlcm5hbC5zZW5kSHR0cFJlcXVlc3QpKHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV0cnlPcHRpb25zIHtcbiAgLyoqIEhvdyBtYW55IHJldHJpZXMgKHdpbGwgYXQgbGVhc3QgdHJ5IG9uY2UpICovXG4gIHJlYWRvbmx5IGF0dGVtcHRzOiBudW1iZXI7XG4gIC8qKiBTbGVlcCBiYXNlLCBpbiBtcyAqL1xuICByZWFkb25seSBzbGVlcDogbnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJldHJpZXM8QSBleHRlbmRzIEFycmF5PGFueT4sIEI+KG9wdGlvbnM6IFJldHJ5T3B0aW9ucywgZm46ICguLi54czogQSkgPT4gUHJvbWlzZTxCPik6ICguLi54czogQSkgPT4gUHJvbWlzZTxCPiB7XG4gIHJldHVybiBhc3luYyAoLi4ueHM6IEEpID0+IHtcbiAgICBsZXQgYXR0ZW1wdHMgPSBvcHRpb25zLmF0dGVtcHRzO1xuICAgIGxldCBtcyA9IG9wdGlvbnMuc2xlZXA7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBmbiguLi54cyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChhdHRlbXB0cy0tIDw9IDApIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG4gICAgICAgIG1zICo9IDI7XG4gICAgICB9XG4gICAgfVxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgob2spID0+IHNldFRpbWVvdXQob2ssIG1zKSk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js new file mode 100644 index 0000000000000..f432d8df83b5e --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js @@ -0,0 +1,102 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.WriterProps; + const exports = props.exports; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports; + const newExports = except(exports, oldExports); + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // consuming stack will delete parameters + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { + tagResults.has(name) + ? tagResults.get(name).add(tagParts[2]) + : tagResults.set(name, new Set([tagParts[2]])); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function except(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sZUFBZSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFD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QUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLE1BQU0sQ0FBQyxNQUEwQixFQUFFLE1BQTBCO0lBQ3BFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7U0FDdkIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzNFLE1BQU0sQ0FBQyxDQUFDLEdBQXVCLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDaEQsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNYLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IENyb3NzUmVnaW9uRXhwb3J0cywgRXhwb3J0V3JpdGVyQ1JQcm9wcyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gIGNvbnN0IGV4cG9ydHMgPSBwcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcblxuICBjb25zdCBzc20gPSBuZXcgU1NNKHsgcmVnaW9uOiBwcm9wcy5yZWdpb24gfSk7XG4gIHRyeSB7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHMgPSBvbGRQcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGV4Y2VwdChleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgICAgdGFnUmVzdWx0cy5oYXMobmFtZSlcbiAgICAgICAgICAgID8gdGFnUmVzdWx0cy5nZXQobmFtZSkhLmFkZCh0YWdQYXJ0c1syXSlcbiAgICAgICAgICAgIDogdGFnUmVzdWx0cy5zZXQobmFtZSwgbmV3IFNldChbdGFnUGFydHNbMl1dKSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gYW4gSW52YWxpZFJlc291cmNlSWQgbWVhbnMgdGhhdCB0aGUgcGFyYW1ldGVyIGRvZXNuJ3QgZXhpc3RcbiAgICAgIC8vIHdoaWNoIHdlIHNob3VsZCBpZ25vcmUgc2luY2UgdGhhdCBtZWFucyBpdCdzIG5vdCBpbiB1c2VcbiAgICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhyb3cgZTtcbiAgICB9XG5cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiBvbmx5IHRoZSBpdGVtcyBmcm9tIHNvdXJjZSB0aGF0IGRvIG5vdCBleGlzdCBpbiB0aGUgZmlsdGVyXG4gKlxuICogQHBhcmFtIHNvdXJjZSB0aGUgc291cmNlIG9iamVjdCB0byBwZXJmb3JtIHRoZSBmaWx0ZXIgb25cbiAqIEBwYXJhbSBmaWx0ZXIgZmlsdGVyIG91dCBpdGVtcyB0aGF0IGV4aXN0IGluIHRoaXMgb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGV4Y2VwdChzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts similarity index 77% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts index 69866b354da99..8970f2b163664 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts @@ -1,26 +1,26 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; -type CrossRegionExports = { [exportName: string]: string }; +import { CrossRegionExports, ExportWriterCRProps } from '../types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const exports: CrossRegionExports = props.Exports; + const props: ExportWriterCRProps = event.ResourceProperties.WriterProps; + const exports = props.exports as CrossRegionExports; - const ssm = new SSM({ region: props.Region }); + const ssm = new SSM({ region: props.region }); try { switch (event.RequestType) { case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await throwIfAnyInUse(ssm, exports); await putParameters(ssm, exports); return; case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports: CrossRegionExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); + const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports as CrossRegionExports; + const newExports = except(exports, oldExports); await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await putParameters(ssm, newExports); return; case 'Delete': @@ -61,10 +61,10 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis }).promise(); result.TagList?.forEach(tag => { const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'cdk-strong-ref') { + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[1]) - : tagResults.set(name, new Set([tagParts[1]])); + ? tagResults.get(name)!.add(tagParts[2]) + : tagResults.set(name, new Set([tagParts[2]])); } }); @@ -93,7 +93,7 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis * @param source the source object to perform the filter on * @param filter filter out items that exist in this object */ -function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { +function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) .reduce((acc: CrossRegionExports, curr: string) => { diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js deleted file mode 100644 index 9df94382cc74e..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js +++ /dev/null @@ -1,118 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = exports.external = void 0; -const https = require("https"); -const url = require("url"); -// for unit tests -exports.external = { - sendHttpRequest: defaultSendHttpRequest, - log: defaultLog, - includeStackTraces: true, - userHandlerIndex: './index', -}; -const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; -const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; -async function handler(event, context) { - const sanitizedEvent = { ...event, ResponseURL: '...' }; - exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); - // ignore DELETE event when the physical resource ID is the marker that - // indicates that this DELETE is a subsequent DELETE to a failed CREATE - // operation. - if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { - exports.external.log('ignoring DELETE event caused by a failed CREATE event'); - await submitResponse('SUCCESS', event); - return; - } - try { - // invoke the user handler. this is intentionally inside the try-catch to - // ensure that if there is an error it's reported as a failure to - // cloudformation (otherwise cfn waits). - // eslint-disable-next-line @typescript-eslint/no-require-imports - const userHandler = require(exports.external.userHandlerIndex).handler; - const result = await userHandler(sanitizedEvent, context); - // validate user response and create the combined event - const responseEvent = renderResponse(event, result); - // submit to cfn as success - await submitResponse('SUCCESS', responseEvent); - } - catch (e) { - const resp = { - ...event, - Reason: exports.external.includeStackTraces ? e.stack : e.message, - }; - if (!resp.PhysicalResourceId) { - // special case: if CREATE fails, which usually implies, we usually don't - // have a physical resource id. in this case, the subsequent DELETE - // operation does not have any meaning, and will likely fail as well. to - // address this, we use a marker so the provider framework can simply - // ignore the subsequent DELETE. - if (event.RequestType === 'Create') { - exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); - resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; - } - else { - // otherwise, if PhysicalResourceId is not specified, something is - // terribly wrong because all other events should have an ID. - exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); - } - } - // this is an actual error, fail the activity altogether and exist. - await submitResponse('FAILED', resp); - } -} -exports.handler = handler; -function renderResponse(cfnRequest, handlerResponse = {}) { - // if physical ID is not returned, we have some defaults for you based - // on the request type. - const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; - // if we are in DELETE and physical ID was changed, it's an error. - if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { - throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); - } - // merge request event and result event (result prevails). - return { - ...cfnRequest, - ...handlerResponse, - PhysicalResourceId: physicalResourceId, - }; -} -async function submitResponse(status, event) { - const json = { - Status: status, - Reason: event.Reason ?? status, - StackId: event.StackId, - RequestId: event.RequestId, - PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, - LogicalResourceId: event.LogicalResourceId, - NoEcho: event.NoEcho, - Data: event.Data, - }; - exports.external.log('submit response to cloudformation', json); - const responseBody = JSON.stringify(json); - const parsedUrl = url.parse(event.ResponseURL); - const req = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { 'content-type': '', 'content-length': responseBody.length }, - }; - await exports.external.sendHttpRequest(req, responseBody); -} -async function defaultSendHttpRequest(options, responseBody) { - return new Promise((resolve, reject) => { - try { - const request = https.request(options, _ => resolve()); - request.on('error', reject); - request.write(responseBody); - request.end(); - } - catch (e) { - reject(e); - } - }); -} -function defaultLog(fmt, ...params) { - // eslint-disable-next-line no-console - console.log(fmt, ...params); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js deleted file mode 100644 index a487b651f8d38..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties; - const exports = props.Exports; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm, parameters) { - const tagResults = new Map(); - await Promise.all(Object.keys(parameters).map(async (name) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'cdk-strong-ref') { - tagResults.has(name) - ? tagResults.get(name).add(tagParts[1]) - : tagResults.set(name, new Set([tagParts[1]])); - } - }); - } - catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - })); - if (tagResults.size > 0) { - const message = Object.entries(tagResults) - .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function filterExports(source, filter) { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNwQyxNQUFNLGFBQWEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2xDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDO2dCQUM3QyxNQUFNLFVBQVUsR0FBdUIsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDeEQsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN2QyxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxhQUFhLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFR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SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztTQUN2QixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDM0UsTUFBTSxDQUFDLENBQUMsR0FBdUIsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUNoRCxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ1gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSBvbGRQcm9wcy5FeHBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdjZGstc3Ryb25nLXJlZicpIHtcbiAgICAgICAgICB0YWdSZXN1bHRzLmhhcyhuYW1lKVxuICAgICAgICAgICAgPyB0YWdSZXN1bHRzLmdldChuYW1lKSEuYWRkKHRhZ1BhcnRzWzFdKVxuICAgICAgICAgICAgOiB0YWdSZXN1bHRzLnNldChuYW1lLCBuZXcgU2V0KFt0YWdQYXJ0c1sxXV0pKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBhbiBJbnZhbGlkUmVzb3VyY2VJZCBtZWFucyB0aGF0IHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdFxuICAgICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgICAgaWYgKGUuY29kZSA9PT0gJ0ludmFsaWRSZXNvdXJjZUlkJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cblxuICB9KSk7XG5cbiAgaWYgKHRhZ1Jlc3VsdHMuc2l6ZSA+IDApIHtcbiAgICBjb25zdCBtZXNzYWdlOiBzdHJpbmcgPSBPYmplY3QuZW50cmllcyh0YWdSZXN1bHRzKVxuICAgICAgLm1hcCgocmVzdWx0OiBbc3RyaW5nLCBzdHJpbmdbXV0pID0+IGAke3Jlc3VsdFswXX0gaXMgaW4gdXNlIGJ5IHN0YWNrKHMpICR7cmVzdWx0WzFdLmpvaW4oJyAnKX1gKVxuICAgICAgLmpvaW4oJ1xcbicpO1xuICAgIHRocm93IG5ldyBFcnJvcihgRXhwb3J0cyBjYW5ub3QgYmUgdXBkYXRlZDogXFxuJHttZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json index 4bff56202abef..5c15519036c1a 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -1,15 +1,15 @@ { "version": "21.0.0", "files": { - "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915": { + "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878": { "source": { - "path": "asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915", + "path": "asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878", "packaging": "zip" }, "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip", + "objectKey": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } @@ -29,7 +29,7 @@ } } }, - "f81d7674cf434dae5287040174f1f3f91843c4d8385d3b6b322b6ffbe47ae1f7": { + "e1c8d2a77a2765a4e4cfdf07208097c34980e050bb50fe5d07702ff157e29adc": { "source": { "path": "cross-region-consumer.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "f81d7674cf434dae5287040174f1f3f91843c4d8385d3b6b322b6ffbe47ae1f7.json", + "objectKey": "e1c8d2a77a2765a4e4cfdf07208097c34980e050bb50fe5d07702ff157e29adc.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json index 2ab0e7b2fbc18..011f4851d3e41 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -48,14 +48,16 @@ "Arn" ] }, - "Region": "us-east-2", - "StackName": "cross-region-consumer", - "Imports": [ - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E", - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" - ] + "ReaderProps": { + "region": "us-east-2", + "prefix": "cross-region-consumer", + "imports": [ + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" + ] + } }, "DependsOn": [ "IntegNestedNestedStackIntegNestedNestedStackResource168C5881" @@ -124,7 +126,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "S3Key": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip" + "S3Key": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json index f9d782c1a9ba4..458a24567cf12 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -1,15 +1,15 @@ { "version": "21.0.0", "files": { - "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc": { + "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03": { "source": { - "path": "asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc", + "path": "asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip", + "objectKey": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } @@ -29,7 +29,7 @@ } } }, - "390176c29eb237a2593e7747bd0a888561316b8ef0e3f6bc79cab1169443e61d": { + "641a63e906e5a90f7362126c1648579dec5c836db6e43276dadbf3da7627b08c": { "source": { "path": "cross-region-producer.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "390176c29eb237a2593e7747bd0a888561316b8ef0e3f6bc79cab1169443e61d.json", + "objectKey": "641a63e906e5a90f7362126c1648579dec5c836db6e43276dadbf3da7627b08c.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json index a9373164e5cd8..c0193c9cf9d32 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -37,19 +37,21 @@ "Arn" ] }, - "Region": "us-east-2", - "Exports": { - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B": { - "Fn::GetAtt": [ - "IntegQueue3A18718A", - "QueueName" - ] - }, - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E": { - "Fn::GetAtt": [ - "IntegNestedNestedStackIntegNestedNestedStackResource168C5881", - "Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" - ] + "WriterProps": { + "region": "us-east-2", + "exports": { + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B": { + "Fn::GetAtt": [ + "IntegQueue3A18718A", + "QueueName" + ] + }, + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E": { + "Fn::GetAtt": [ + "IntegNestedNestedStackIntegNestedNestedStackResource168C5881", + "Outputs.crossregionproducerIntegNestedNestedIntegQueueD686DB69QueueName" + ] + } } } }, @@ -115,7 +117,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip" + "S3Key": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index f76eb244585da..89d81d8421b3d 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/390176c29eb237a2593e7747bd0a888561316b8ef0e3f6bc79cab1169443e61d.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/641a63e906e5a90f7362126c1648579dec5c836db6e43276dadbf3da7627b08c.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -106,7 +106,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/f81d7674cf434dae5287040174f1f3f91843c4d8385d3b6b322b6ffbe47ae1f7.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/e1c8d2a77a2765a4e4cfdf07208097c34980e050bb50fe5d07702ff157e29adc.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/__entrypoint__.js deleted file mode 100644 index 9df94382cc74e..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/__entrypoint__.js +++ /dev/null @@ -1,118 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = exports.external = void 0; -const https = require("https"); -const url = require("url"); -// for unit tests -exports.external = { - sendHttpRequest: defaultSendHttpRequest, - log: defaultLog, - includeStackTraces: true, - userHandlerIndex: './index', -}; -const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; -const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; -async function handler(event, context) { - const sanitizedEvent = { ...event, ResponseURL: '...' }; - exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); - // ignore DELETE event when the physical resource ID is the marker that - // indicates that this DELETE is a subsequent DELETE to a failed CREATE - // operation. - if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { - exports.external.log('ignoring DELETE event caused by a failed CREATE event'); - await submitResponse('SUCCESS', event); - return; - } - try { - // invoke the user handler. this is intentionally inside the try-catch to - // ensure that if there is an error it's reported as a failure to - // cloudformation (otherwise cfn waits). - // eslint-disable-next-line @typescript-eslint/no-require-imports - const userHandler = require(exports.external.userHandlerIndex).handler; - const result = await userHandler(sanitizedEvent, context); - // validate user response and create the combined event - const responseEvent = renderResponse(event, result); - // submit to cfn as success - await submitResponse('SUCCESS', responseEvent); - } - catch (e) { - const resp = { - ...event, - Reason: exports.external.includeStackTraces ? e.stack : e.message, - }; - if (!resp.PhysicalResourceId) { - // special case: if CREATE fails, which usually implies, we usually don't - // have a physical resource id. in this case, the subsequent DELETE - // operation does not have any meaning, and will likely fail as well. to - // address this, we use a marker so the provider framework can simply - // ignore the subsequent DELETE. - if (event.RequestType === 'Create') { - exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); - resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; - } - else { - // otherwise, if PhysicalResourceId is not specified, something is - // terribly wrong because all other events should have an ID. - exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); - } - } - // this is an actual error, fail the activity altogether and exist. - await submitResponse('FAILED', resp); - } -} -exports.handler = handler; -function renderResponse(cfnRequest, handlerResponse = {}) { - // if physical ID is not returned, we have some defaults for you based - // on the request type. - const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; - // if we are in DELETE and physical ID was changed, it's an error. - if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { - throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); - } - // merge request event and result event (result prevails). - return { - ...cfnRequest, - ...handlerResponse, - PhysicalResourceId: physicalResourceId, - }; -} -async function submitResponse(status, event) { - const json = { - Status: status, - Reason: event.Reason ?? status, - StackId: event.StackId, - RequestId: event.RequestId, - PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, - LogicalResourceId: event.LogicalResourceId, - NoEcho: event.NoEcho, - Data: event.Data, - }; - exports.external.log('submit response to cloudformation', json); - const responseBody = JSON.stringify(json); - const parsedUrl = url.parse(event.ResponseURL); - const req = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { 'content-type': '', 'content-length': responseBody.length }, - }; - await exports.external.sendHttpRequest(req, responseBody); -} -async function defaultSendHttpRequest(options, responseBody) { - return new Promise((resolve, reject) => { - try { - const request = https.request(options, _ => resolve()); - request.on('error', reject); - request.write(responseBody); - request.end(); - } - catch (e) { - reject(e); - } - }); -} -function defaultLog(fmt, ...params) { - // eslint-disable-next-line no-console - console.log(fmt, ...params); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js deleted file mode 100644 index 3c1ee7facb4df..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.js +++ /dev/null @@ -1,127 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties; - const imports = props.Imports; - const keyName = `cdk-strong-ref:${props.StackName}`; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info('Tagging SSM Parameter imports'); - await addTags(ssm, imports, keyName); - return; - case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports = oldProps.Imports; - const newExports = filterExports(imports, oldExports); - const paramsToDelete = filterExports(oldExports, imports); - console.info('Releasing unused SSM Parameter imports'); - if (Object.keys(paramsToDelete).length > 0) { - await removeTags(ssm, paramsToDelete, keyName); - } - console.info('Tagging new SSM Parameter imports'); - await addTags(ssm, newExports, keyName); - return; - case 'Delete': - console.info('Deleting all SSM Parameter exports'); - await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); - return; - default: - return; - } - } - catch (e) { - console.error('Error importing cross region stack exports: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Add tag to parameters for existing exports - */ -async function addTags(ssm, parameters, keyName) { - await Promise.all(parameters.map(async (name) => { - try { - return await ssm.addTagsToResource({ - ResourceId: name, - ResourceType: 'Parameter', - Tags: [{ - Key: keyName, - Value: 'true', - }], - }).promise(); - } - catch (e) { - throw new Error(`Error importing ${name}: ${e}`); - } - })); -} -/** - * Remove tags from parameters - */ -async function removeTags(ssm, parameters, keyName) { - await Promise.all(parameters.map(async (name) => { - try { - return await ssm.removeTagsFromResource({ - TagKeys: [keyName], - ResourceType: 'Parameter', - ResourceId: name, - }).promise(); - } - catch (e) { - switch (e.code) { - // if the parameter doesn't exist then there is nothing to release - case 'InvalidResourceId': - return; - default: - throw new Error(`Error releasing import ${name}: ${e}`); - } - } - })); -} -/** - * Get all parameters in a given path - * - * If the request fails for any reason it will fail the custom resource event. - * Since this is only run when the resource is deleted that is probably the behavior - * that is desired. - */ -async function getParametersByPath(ssm, path, nextToken) { - const parameters = []; - return ssm.getParametersByPath({ - Path: path, - NextToken: nextToken, - }).promise().then(async (getParametersByPathResult) => { - parameters.push(...getParametersByPathResult.Parameters ?? []); - if (getParametersByPathResult.NextToken) { - parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); - } - return parameters; - }); -} -/** - * Delete all parameters in a give path - */ -async function deleteParametersByPath(ssm, path) { - const allParams = await getParametersByPath(ssm, path); - const names = allParams.map(param => param.Name).filter(x => !!x); - await ssm.deleteParameters({ - Names: names, - }).promise(); -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function filterExports(source, filter) { - return source.filter(key => !filter.includes(key)); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUV2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBYSxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ3hDLE1BQU0sT0FBTyxHQUFXLGtCQUFrQixLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7SUFFNUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO2dCQUM5QyxNQUFNLE9BQU8sQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFRO2dCQUNYLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQztnQkFDN0MsTUFBTSxVQUFVLEdBQWEsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDOUMsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxjQUFjLEdBQUcsYUFBYSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDMUQsT0FBTyxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFDO2dCQUN2RCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDMUMsTUFBTSxVQUFVLENBQUMsR0FBRyxFQUFFLGNBQWMsRUFBRSxPQUFPLENBQUMsQ0FBQztpQkFDaEQ7Z0JBQ0QsT0FBTyxDQUFDLElBQUksQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFDO2dCQUNsRCxNQUFNLE9BQU8sQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUN4QyxPQUFPO1lBQ1QsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsb0NBQW9DLENBQUMsQ0FBQztnQkFDbkQsTUFBTSxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsZ0JBQWdCLEtBQUssQ0FBQyxTQUFTLEdBQUcsQ0FBQyxDQUFDO2dCQUN0RSxPQUFPO1lBQ1Q7Z0JBQ0UsT0FBTztTQUNWO0tBQ0Y7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNWLE9BQU8sQ0FBQyxLQUFLLENBQUMsOENBQThDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDakUsTUFBTSxDQUFDLENBQUM7S0FDVDtBQUNILENBQUM7QUFuQ0QsMEJBbUNDO0FBQUEsQ0FBQztBQUVGOztHQUVHO0FBQ0gsS0FBSyxVQUFVLE9BQU8sQ0FBQyxHQUFRLEVBQUUsVUFBb0IsRUFBRSxPQUFlO0lBQ3BFLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBQyxJQUFJLEVBQUMsRUFBRTtRQUM1QyxJQUFJO1lBQ0YsT0FBTyxNQUFNLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQztnQkFDakMsVUFBVSxFQUFFLElBQUk7Z0JBQ2hCLFlBQVksRUFBRSxXQUFXO2dCQUN6QixJQUFJLEVBQUUsQ0FBQzt3QkFDTCxHQUFHLEVBQUUsT0FBTzt3QkFDWixLQUFLLEVBQUUsTUFBTTtxQkFDZCxDQUFDO2FBQ0gsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1NBQ2Q7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLE1BQU0sSUFBSSxLQUFLLENBQUMsbUJBQW1CLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1NBQ2xEO0lBQ0gsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNOLENBQUM7QUFFRDs7R0FFRztBQUNILEtBQUssVUFBVSxVQUFVLENBQUMsR0FBUSxFQUFFLFVBQW9CLEVBQUUsT0FBZTtJQUN2RSxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUMsSUFBSSxFQUFDLEVBQUU7UUFDNUMsSUFBSTtZQUNGLE9BQU8sTUFBTSxHQUFHLENBQUMsc0JBQXNCLENBQUM7Z0JBQ3RDLE9BQU8sRUFBRSxDQUFDLE9BQU8sQ0FBQztnQkFDbEIsWUFBWSxFQUFFLFdBQVc7Z0JBQ3pCLFVBQVUsRUFBRSxJQUFJO2FBQ2pCLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztTQUNkO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixRQUFRLENBQUMsQ0FBQyxJQUFJLEVBQUU7Z0JBQ2Qsa0VBQWtFO2dCQUNsRSxLQUFLLG1CQUFtQjtvQkFDdEIsT0FBTztnQkFDVDtvQkFDRSxNQUFNLElBQUksS0FBSyxDQUFDLDBCQUEwQixJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQzthQUMzRDtTQUNGO0lBQ0gsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNOLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxLQUFLLFVBQVUsbUJBQW1CLENBQUMsR0FBUSxFQUFFLElBQVksRUFBRSxTQUFrQjtJQUMzRSxNQUFNLFVBQVUsR0FBb0IsRUFBRSxDQUFDO0lBQ3ZDLE9BQU8sR0FBRyxDQUFDLG1CQUFtQixDQUFDO1FBQzdCLElBQUksRUFBRSxJQUFJO1FBQ1YsU0FBUyxFQUFFLFNBQVM7S0FDckIsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUMseUJBQXlCLEVBQUMsRUFBRTtRQUNsRCxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQUcseUJBQXlCLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQy9ELElBQUkseUJBQXlCLENBQUMsU0FBUyxFQUFFO1lBQ3ZDLFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLG1CQUFtQixDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUseUJBQXlCLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztTQUMvRjtRQUNELE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsS0FBSyxVQUFVLHNCQUFzQixDQUFDLEdBQVEsRUFBRSxJQUFZO0lBQzFELE1BQU0sU0FBUyxHQUFHLE1BQU0sbUJBQW1CLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3ZELE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBYSxDQUFDO0lBQzlFLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO1FBQ3pCLEtBQUssRUFBRSxLQUFLO0tBQ2IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ2YsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxhQUFhLENBQUMsTUFBZ0IsRUFBRSxNQUFnQjtJQUN2RCxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUNyRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyplc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnYXdzLXNkayc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGNvbnN0IHByb3BzID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICBjb25zdCBpbXBvcnRzOiBzdHJpbmdbXSA9IHByb3BzLkltcG9ydHM7XG4gIGNvbnN0IGtleU5hbWU6IHN0cmluZyA9IGBjZGstc3Ryb25nLXJlZjoke3Byb3BzLlN0YWNrTmFtZX1gO1xuXG4gIGNvbnN0IHNzbSA9IG5ldyBTU00oeyByZWdpb246IHByb3BzLlJlZ2lvbiB9KTtcbiAgdHJ5IHtcbiAgICBzd2l0Y2ggKGV2ZW50LlJlcXVlc3RUeXBlKSB7XG4gICAgICBjYXNlICdDcmVhdGUnOlxuICAgICAgICBjb25zb2xlLmluZm8oJ1RhZ2dpbmcgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGFkZFRhZ3Moc3NtLCBpbXBvcnRzLCBrZXlOYW1lKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHM6IHN0cmluZ1tdID0gb2xkUHJvcHMuSW1wb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGZpbHRlckV4cG9ydHMoaW1wb3J0cywgb2xkRXhwb3J0cyk7XG4gICAgICAgIGNvbnN0IHBhcmFtc1RvRGVsZXRlID0gZmlsdGVyRXhwb3J0cyhvbGRFeHBvcnRzLCBpbXBvcnRzKTtcbiAgICAgICAgY29uc29sZS5pbmZvKCdSZWxlYXNpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgaW1wb3J0cycpO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCByZW1vdmVUYWdzKHNzbSwgcGFyYW1zVG9EZWxldGUsIGtleU5hbWUpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBuZXcgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGFkZFRhZ3Moc3NtLCBuZXdFeHBvcnRzLCBrZXlOYW1lKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKCdEZWxldGluZyBhbGwgU1NNIFBhcmFtZXRlciBleHBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtLCBgL2Nkay9leHBvcnRzLyR7cHJvcHMuU3RhY2tOYW1lfS9gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGltcG9ydGluZyBjcm9zcyByZWdpb24gc3RhY2sgZXhwb3J0czogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBBZGQgdGFnIHRvIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gYWRkVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5hZGRUYWdzVG9SZXNvdXJjZSh7XG4gICAgICAgIFJlc291cmNlSWQ6IG5hbWUsXG4gICAgICAgIFJlc291cmNlVHlwZTogJ1BhcmFtZXRlcicsXG4gICAgICAgIFRhZ3M6IFt7XG4gICAgICAgICAgS2V5OiBrZXlOYW1lLFxuICAgICAgICAgIFZhbHVlOiAndHJ1ZScsXG4gICAgICAgIH1dLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgaW1wb3J0aW5nICR7bmFtZX06ICR7ZX1gKTtcbiAgICB9XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBSZW1vdmUgdGFncyBmcm9tIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVtb3ZlVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5yZW1vdmVUYWdzRnJvbVJlc291cmNlKHtcbiAgICAgICAgVGFnS2V5czogW2tleU5hbWVdLFxuICAgICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgICAgICBSZXNvdXJjZUlkOiBuYW1lLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHN3aXRjaCAoZS5jb2RlKSB7XG4gICAgICAgIC8vIGlmIHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdCB0aGVuIHRoZXJlIGlzIG5vdGhpbmcgdG8gcmVsZWFzZVxuICAgICAgICBjYXNlICdJbnZhbGlkUmVzb3VyY2VJZCc6XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgcmVsZWFzaW5nIGltcG9ydCAke25hbWV9OiAke2V9YCk7XG4gICAgICB9XG4gICAgfVxuICB9KSk7XG59XG5cbi8qKlxuICogR2V0IGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZW4gcGF0aFxuICpcbiAqIElmIHRoZSByZXF1ZXN0IGZhaWxzIGZvciBhbnkgcmVhc29uIGl0IHdpbGwgZmFpbCB0aGUgY3VzdG9tIHJlc291cmNlIGV2ZW50LlxuICogU2luY2UgdGhpcyBpcyBvbmx5IHJ1biB3aGVuIHRoZSByZXNvdXJjZSBpcyBkZWxldGVkIHRoYXQgaXMgcHJvYmFibHkgdGhlIGJlaGF2aW9yXG4gKiB0aGF0IGlzIGRlc2lyZWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldFBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZywgbmV4dFRva2VuPzogc3RyaW5nKTogUHJvbWlzZTxTU00uUGFyYW1ldGVyW10+IHtcbiAgY29uc3QgcGFyYW1ldGVyczogU1NNLlBhcmFtZXRlcltdID0gW107XG4gIHJldHVybiBzc20uZ2V0UGFyYW1ldGVyc0J5UGF0aCh7XG4gICAgUGF0aDogcGF0aCxcbiAgICBOZXh0VG9rZW46IG5leHRUb2tlbixcbiAgfSkucHJvbWlzZSgpLnRoZW4oYXN5bmMgZ2V0UGFyYW1ldGVyc0J5UGF0aFJlc3VsdCA9PiB7XG4gICAgcGFyYW1ldGVycy5wdXNoKC4uLmdldFBhcmFtZXRlcnNCeVBhdGhSZXN1bHQuUGFyYW1ldGVycyA/PyBbXSk7XG4gICAgaWYgKGdldFBhcmFtZXRlcnNCeVBhdGhSZXN1bHQuTmV4dFRva2VuKSB7XG4gICAgICBwYXJhbWV0ZXJzLnB1c2goLi4uYXdhaXQgZ2V0UGFyYW1ldGVyc0J5UGF0aChzc20sIHBhdGgsIGdldFBhcmFtZXRlcnNCeVBhdGhSZXN1bHQuTmV4dFRva2VuKSk7XG4gICAgfVxuICAgIHJldHVybiBwYXJhbWV0ZXJzO1xuICB9KTtcbn1cblxuLyoqXG4gKiBEZWxldGUgYWxsIHBhcmFtZXRlcnMgaW4gYSBnaXZlIHBhdGhcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZGVsZXRlUGFyYW1ldGVyc0J5UGF0aChzc206IFNTTSwgcGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGFsbFBhcmFtcyA9IGF3YWl0IGdldFBhcmFtZXRlcnNCeVBhdGgoc3NtLCBwYXRoKTtcbiAgY29uc3QgbmFtZXMgPSBhbGxQYXJhbXMubWFwKHBhcmFtID0+IHBhcmFtLk5hbWUpLmZpbHRlcih4ID0+ICEheCkgYXMgc3RyaW5nW107XG4gIGF3YWl0IHNzbS5kZWxldGVQYXJhbWV0ZXJzKHtcbiAgICBOYW1lczogbmFtZXMsXG4gIH0pLnByb21pc2UoKTtcbn1cblxuLyoqXG4gKiBSZXR1cm4gb25seSB0aGUgaXRlbXMgZnJvbSBzb3VyY2UgdGhhdCBkbyBub3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICpcbiAqIEBwYXJhbSBzb3VyY2UgdGhlIHNvdXJjZSBvYmplY3QgdG8gcGVyZm9ybSB0aGUgZmlsdGVyIG9uXG4gKiBAcGFyYW0gZmlsdGVyIGZpbHRlciBvdXQgaXRlbXMgdGhhdCBleGlzdCBpbiB0aGlzIG9iamVjdFxuICovXG5mdW5jdGlvbiBmaWx0ZXJFeHBvcnRzKHNvdXJjZTogc3RyaW5nW10sIGZpbHRlcjogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBzb3VyY2UuZmlsdGVyKGtleSA9PiAhZmlsdGVyLmluY2x1ZGVzKGtleSkpO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js new file mode 100644 index 0000000000000..1e3a3093c1706 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js @@ -0,0 +1,144 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, exports.external.sendHttpRequest)(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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RUFBRSxFQUFFO1FBQ3hCLElBQUksUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDaEMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUN2QixPQUFPLElBQUksRUFBRTtZQUNYLElBQUk7Z0JBQ0YsT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2FBQ3hCO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsSUFBSSxRQUFRLEVBQUUsSUFBSSxDQUFDLEVBQUU7b0JBQ25CLE1BQU0sQ0FBQyxDQUFDO2lCQUNUO2dCQUNELE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDVDtTQUNGO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQWhCRCxrQ0FnQkM7QUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDN0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBodHRwcyBmcm9tICdodHRwcyc7XG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcblxuLy8gZm9yIHVuaXQgdGVzdHNcbmV4cG9ydCBjb25zdCBleHRlcm5hbCA9IHtcbiAgc2VuZEh0dHBSZXF1ZXN0OiBkZWZhdWx0U2VuZEh0dHBSZXF1ZXN0LFxuICBsb2c6IGRlZmF1bHRMb2csXG4gIGluY2x1ZGVTdGFja1RyYWNlczogdHJ1ZSxcbiAgdXNlckhhbmRsZXJJbmRleDogJy4vaW5kZXgnLFxufTtcblxuY29uc3QgQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpDUkVBVEVfRkFJTEVEJztcbmNvbnN0IE1JU1NJTkdfUEhZU0lDQUxfSURfTUFSS0VSID0gJ0FXU0NESzo6Q3VzdG9tUmVzb3VyY2VQcm92aWRlckZyYW1ld29yazo6TUlTU0lOR19QSFlTSUNBTF9JRCc7XG5cbmV4cG9ydCB0eXBlIFJlc3BvbnNlID0gQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIEhhbmRsZXJSZXNwb25zZTtcbmV4cG9ydCB0eXBlIEhhbmRsZXIgPSAoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSA9PiBQcm9taXNlPEhhbmRsZXJSZXNwb25zZSB8IHZvaWQ+O1xuZXhwb3J0IHR5cGUgSGFuZGxlclJlc3BvbnNlID0gdW5kZWZpbmVkIHwge1xuICBEYXRhPzogYW55O1xuICBQaHlzaWNhbFJlc291cmNlSWQ/OiBzdHJpbmc7XG4gIFJlYXNvbj86IHN0cmluZztcbiAgTm9FY2hvPzogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50LCBjb250ZXh0OiBBV1NMYW1iZGEuQ29udGV4dCkge1xuICBjb25zdCBzYW5pdGl6ZWRFdmVudCA9IHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9O1xuICBleHRlcm5hbC5sb2coSlNPTi5zdHJpbmdpZnkoc2FuaXRpemVkRXZlbnQsIHVuZGVmaW5lZCwgMikpO1xuXG4gIC8vIGlnbm9yZSBERUxFVEUgZXZlbnQgd2hlbiB0aGUgcGh5c2ljYWwgcmVzb3VyY2UgSUQgaXMgdGhlIG1hcmtlciB0aGF0XG4gIC8vIGluZGljYXRlcyB0aGF0IHRoaXMgREVMRVRFIGlzIGEgc3Vic2VxdWVudCBERUxFVEUgdG8gYSBmYWlsZWQgQ1JFQVRFXG4gIC8vIG9wZXJhdGlvbi5cbiAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgPT09IENSRUFURV9GQUlMRURfUEhZU0lDQUxfSURfTUFSS0VSKSB7XG4gICAgZXh0ZXJuYWwubG9nKCdpZ25vcmluZyBERUxFVEUgZXZlbnQgY2F1c2VkIGJ5IGEgZmFpbGVkIENSRUFURSBldmVudCcpO1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgZXZlbnQpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gaW52b2tlIHRoZSB1c2VyIGhhbmRsZXIuIHRoaXMgaXMgaW50ZW50aW9uYWxseSBpbnNpZGUgdGhlIHRyeS1jYXRjaCB0b1xuICAgIC8vIGVuc3VyZSB0aGF0IGlmIHRoZXJlIGlzIGFuIGVycm9yIGl0J3MgcmVwb3J0ZWQgYXMgYSBmYWlsdXJlIHRvXG4gICAgLy8gY2xvdWRmb3JtYXRpb24gKG90aGVyd2lzZSBjZm4gd2FpdHMpLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgdXNlckhhbmRsZXI6IEhhbmRsZXIgPSByZXF1aXJlKGV4dGVybmFsLnVzZXJIYW5kbGVySW5kZXgpLmhhbmRsZXI7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdXNlckhhbmRsZXIoc2FuaXRpemVkRXZlbnQsIGNvbnRleHQpO1xuXG4gICAgLy8gdmFsaWRhdGUgdXNlciByZXNwb25zZSBhbmQgY3JlYXRlIHRoZSBjb21iaW5lZCBldmVudFxuICAgIGNvbnN0IHJlc3BvbnNlRXZlbnQgPSByZW5kZXJSZXNwb25zZShldmVudCwgcmVzdWx0KTtcblxuICAgIC8vIHN1Ym1pdCB0byBjZm4gYXMgc3VjY2Vzc1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgcmVzcG9uc2VFdmVudCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zdCByZXNwOiBSZXNwb25zZSA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgUmVhc29uOiBleHRlcm5hbC5pbmNsdWRlU3RhY2tUcmFjZXMgPyBlLnN0YWNrIDogZS5tZXNzYWdlLFxuICAgIH07XG5cbiAgICBpZiAoIXJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgICAvLyBzcGVjaWFsIGNhc2U6IGlmIENSRUFURSBmYWlscywgd2hpY2ggdXN1YWxseSBpbXBsaWVzLCB3ZSB1c3VhbGx5IGRvbid0XG4gICAgICAvLyBoYXZlIGEgcGh5c2ljYWwgcmVzb3VyY2UgaWQuIGluIHRoaXMgY2FzZSwgdGhlIHN1YnNlcXVlbnQgREVMRVRFXG4gICAgICAvLyBvcGVyYXRpb24gZG9lcyBub3QgaGF2ZSBhbnkgbWVhbmluZywgYW5kIHdpbGwgbGlrZWx5IGZhaWwgYXMgd2VsbC4gdG9cbiAgICAgIC8vIGFkZHJlc3MgdGhpcywgd2UgdXNlIGEgbWFya2VyIHNvIHRoZSBwcm92aWRlciBmcmFtZXdvcmsgY2FuIHNpbXBseVxuICAgICAgLy8gaWdub3JlIHRoZSBzdWJzZXF1ZW50IERFTEVURS5cbiAgICAgIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScpIHtcbiAgICAgICAgZXh0ZXJuYWwubG9nKCdDUkVBVEUgZmFpbGVkLCByZXNwb25kaW5nIHdpdGggYSBtYXJrZXIgcGh5c2ljYWwgcmVzb3VyY2UgaWQgc28gdGhhdCB0aGUgc3Vic2VxdWVudCBERUxFVEUgd2lsbCBiZSBpZ25vcmVkJyk7XG4gICAgICAgIHJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkID0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBvdGhlcndpc2UsIGlmIFBoeXNpY2FsUmVzb3VyY2VJZCBpcyBub3Qgc3BlY2lmaWVkLCBzb21ldGhpbmcgaXNcbiAgICAgICAgLy8gdGVycmlibHkgd3JvbmcgYmVjYXVzZSBhbGwgb3RoZXIgZXZlbnRzIHNob3VsZCBoYXZlIGFuIElELlxuICAgICAgICBleHRlcm5hbC5sb2coYEVSUk9SOiBNYWxmb3JtZWQgZXZlbnQuIFwiUGh5c2ljYWxSZXNvdXJjZUlkXCIgaXMgcmVxdWlyZWQ6ICR7SlNPTi5zdHJpbmdpZnkoZXZlbnQpfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRoaXMgaXMgYW4gYWN0dWFsIGVycm9yLCBmYWlsIHRoZSBhY3Rpdml0eSBhbHRvZ2V0aGVyIGFuZCBleGlzdC5cbiAgICBhd2FpdCBzdWJtaXRSZXNwb25zZSgnRkFJTEVEJywgcmVzcCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVuZGVyUmVzcG9uc2UoXG4gIGNmblJlcXVlc3Q6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQgJiB7IFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZyB9LFxuICBoYW5kbGVyUmVzcG9uc2U6IHZvaWQgfCBIYW5kbGVyUmVzcG9uc2UgPSB7IH0pOiBSZXNwb25zZSB7XG5cbiAgLy8gaWYgcGh5c2ljYWwgSUQgaXMgbm90IHJldHVybmVkLCB3ZSBoYXZlIHNvbWUgZGVmYXVsdHMgZm9yIHlvdSBiYXNlZFxuICAvLyBvbiB0aGUgcmVxdWVzdCB0eXBlLlxuICBjb25zdCBwaHlzaWNhbFJlc291cmNlSWQgPSBoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUmVxdWVzdElkO1xuXG4gIC8vIGlmIHdlIGFyZSBpbiBERUxFVEUgYW5kIHBoeXNpY2FsIElEIHdhcyBjaGFuZ2VkLCBpdCdzIGFuIGVycm9yLlxuICBpZiAoY2ZuUmVxdWVzdC5SZXF1ZXN0VHlwZSA9PT0gJ0RlbGV0ZScgJiYgcGh5c2ljYWxSZXNvdXJjZUlkICE9PSBjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgREVMRVRFOiBjYW5ub3QgY2hhbmdlIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBmcm9tIFwiJHtjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZH1cIiB0byBcIiR7aGFuZGxlclJlc3BvbnNlLlBoeXNpY2FsUmVzb3VyY2VJZH1cIiBkdXJpbmcgZGVsZXRpb25gKTtcbiAgfVxuXG4gIC8vIG1lcmdlIHJlcXVlc3QgZXZlbnQgYW5kIHJlc3VsdCBldmVudCAocmVzdWx0IHByZXZhaWxzKS5cbiAgcmV0dXJuIHtcbiAgICAuLi5jZm5SZXF1ZXN0LFxuICAgIC4uLmhhbmRsZXJSZXNwb25zZSxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IHBoeXNpY2FsUmVzb3VyY2VJZCxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3VibWl0UmVzcG9uc2Uoc3RhdHVzOiAnU1VDQ0VTUycgfCAnRkFJTEVEJywgZXZlbnQ6IFJlc3BvbnNlKSB7XG4gIGNvbnN0IGpzb246IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlUmVzcG9uc2UgPSB7XG4gICAgU3RhdHVzOiBzdGF0dXMsXG4gICAgUmVhc29uOiBldmVudC5SZWFzb24gPz8gc3RhdHVzLFxuICAgIFN0YWNrSWQ6IGV2ZW50LlN0YWNrSWQsXG4gICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgfHwgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIsXG4gICAgTG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgIE5vRWNobzogZXZlbnQuTm9FY2hvLFxuICAgIERhdGE6IGV2ZW50LkRhdGEsXG4gIH07XG5cbiAgZXh0ZXJuYWwubG9nKCdzdWJtaXQgcmVzcG9uc2UgdG8gY2xvdWRmb3JtYXRpb24nLCBqc29uKTtcblxuICBjb25zdCByZXNwb25zZUJvZHkgPSBKU09OLnN0cmluZ2lmeShqc29uKTtcbiAgY29uc3QgcGFyc2VkVXJsID0gdXJsLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgY29uc3QgcmVxID0ge1xuICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgcGF0aDogcGFyc2VkVXJsLnBhdGgsXG4gICAgbWV0aG9kOiAnUFVUJyxcbiAgICBoZWFkZXJzOiB7ICdjb250ZW50LXR5cGUnOiAnJywgJ2NvbnRlbnQtbGVuZ3RoJzogcmVzcG9uc2VCb2R5Lmxlbmd0aCB9LFxuICB9O1xuXG4gIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHtcbiAgICBhdHRlbXB0czogNSxcbiAgICBzbGVlcDogMTAwMCxcbiAgfTtcbiAgYXdhaXQgd2l0aFJldHJpZXMocmV0cnlPcHRpb25zLCBleHRlcm5hbC5zZW5kSHR0cFJlcXVlc3QpKHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV0cnlPcHRpb25zIHtcbiAgLyoqIEhvdyBtYW55IHJldHJpZXMgKHdpbGwgYXQgbGVhc3QgdHJ5IG9uY2UpICovXG4gIHJlYWRvbmx5IGF0dGVtcHRzOiBudW1iZXI7XG4gIC8qKiBTbGVlcCBiYXNlLCBpbiBtcyAqL1xuICByZWFkb25seSBzbGVlcDogbnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJldHJpZXM8QSBleHRlbmRzIEFycmF5PGFueT4sIEI+KG9wdGlvbnM6IFJldHJ5T3B0aW9ucywgZm46ICguLi54czogQSkgPT4gUHJvbWlzZTxCPik6ICguLi54czogQSkgPT4gUHJvbWlzZTxCPiB7XG4gIHJldHVybiBhc3luYyAoLi4ueHM6IEEpID0+IHtcbiAgICBsZXQgYXR0ZW1wdHMgPSBvcHRpb25zLmF0dGVtcHRzO1xuICAgIGxldCBtcyA9IG9wdGlvbnMuc2xlZXA7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBmbiguLi54cyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChhdHRlbXB0cy0tIDw9IDApIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG4gICAgICAgIG1zICo9IDI7XG4gICAgICB9XG4gICAgfVxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgob2spID0+IHNldFRpbWVvdXQob2ssIG1zKSk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.d.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.d.ts rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js new file mode 100644 index 0000000000000..f432d8df83b5e --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js @@ -0,0 +1,102 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.WriterProps; + const exports = props.exports; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports; + const newExports = except(exports, oldExports); + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // consuming stack will delete parameters + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { + tagResults.has(name) + ? tagResults.get(name).add(tagParts[2]) + : tagResults.set(name, new Set([tagParts[2]])); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function except(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sZUFBZSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDckMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCx5Q0FBeUM7Z0JBQ3pDLE9BQU87WUFDVDtnQkFDRSxPQUFPO1NBQ1Y7S0FDRjtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsT0FBTyxDQUFDLEtBQUssQ0FBQywwQkFBMEIsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM3QyxNQUFNLENBQUMsQ0FBQztLQUNUO0FBQ0gsQ0FBQztBQTlCRCwwQkE4QkM7QUFBQSxDQUFDO0FBRUY7O0dBRUc7QUFDSCxLQUFLLFVBQVUsYUFBYSxDQUFDLEdBQVEsRUFBRSxVQUE4QjtJQUNuRSxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRTtRQUN6RSxPQUFPLEdBQUcsQ0FBQyxZQUFZLENBQUM7WUFDdEIsSUFBSSxFQUFFLElBQUk7WUFDVixLQUFLLEVBQUUsS0FBSztZQUNaLElBQUksRUFBRSxRQUFRO1NBQ2YsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2YsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNOLENBQUM7QUFFRDs7R0FFRztBQUNILEtBQUssVUFBVSxlQUFlLENBQUMsR0FBUSxFQUFFLFVBQThCO0lBQ3JFLE1BQU0sVUFBVSxHQUE2QixJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ3ZELE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDbkUsSUFBSTtZQUNGLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLG1CQUFtQixDQUFDO2dCQUMzQyxVQUFVLEVBQUUsSUFBSTtnQkFDaEIsWUFBWSxFQUFFLFdBQVc7YUFDMUIsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsTUFBTSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUU7Z0JBQzVCLE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNwQyxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxTQUFTLElBQUksUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLFlBQVksRUFBRTtvQkFDN0QsVUFBVSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUM7d0JBQ2xCLENBQUMsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBRSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBQ3hDLENBQUMsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztpQkFDbEQ7WUFDSCxDQUFDLENBQUMsQ0FBQztTQUVKO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDViw4REFBOEQ7WUFDOUQsMERBQTBEO1lBQzFELElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxtQkFBbUIsRUFBRTtnQkFDbEMsT0FBTzthQUNSO1lBQ0QsTUFBTSxDQUFDLENBQUM7U0FDVDtJQUVILENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFSixJQUFJLFVBQVUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFO1FBQ3ZCLE1BQU0sT0FBTyxHQUFXLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDO2FBQy9DLEdBQUcsQ0FBQyxDQUFDLE1BQTBCLEVBQUUsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQywwQkFBMEIsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2FBQ2hHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0NBQWdDLE9BQU8sRUFBRSxDQUFDLENBQUM7S0FDNUQ7QUFDSCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLE1BQU0sQ0FBQyxNQUEwQixFQUFFLE1BQTBCO0lBQ3BFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7U0FDdkIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzNFLE1BQU0sQ0FBQyxDQUFDLEdBQXVCLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDaEQsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNYLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IENyb3NzUmVnaW9uRXhwb3J0cywgRXhwb3J0V3JpdGVyQ1JQcm9wcyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gIGNvbnN0IGV4cG9ydHMgPSBwcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcblxuICBjb25zdCBzc20gPSBuZXcgU1NNKHsgcmVnaW9uOiBwcm9wcy5yZWdpb24gfSk7XG4gIHRyeSB7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHMgPSBvbGRQcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGV4Y2VwdChleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgICAgdGFnUmVzdWx0cy5oYXMobmFtZSlcbiAgICAgICAgICAgID8gdGFnUmVzdWx0cy5nZXQobmFtZSkhLmFkZCh0YWdQYXJ0c1syXSlcbiAgICAgICAgICAgIDogdGFnUmVzdWx0cy5zZXQobmFtZSwgbmV3IFNldChbdGFnUGFydHNbMl1dKSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gYW4gSW52YWxpZFJlc291cmNlSWQgbWVhbnMgdGhhdCB0aGUgcGFyYW1ldGVyIGRvZXNuJ3QgZXhpc3RcbiAgICAgIC8vIHdoaWNoIHdlIHNob3VsZCBpZ25vcmUgc2luY2UgdGhhdCBtZWFucyBpdCdzIG5vdCBpbiB1c2VcbiAgICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhyb3cgZTtcbiAgICB9XG5cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiBvbmx5IHRoZSBpdGVtcyBmcm9tIHNvdXJjZSB0aGF0IGRvIG5vdCBleGlzdCBpbiB0aGUgZmlsdGVyXG4gKlxuICogQHBhcmFtIHNvdXJjZSB0aGUgc291cmNlIG9iamVjdCB0byBwZXJmb3JtIHRoZSBmaWx0ZXIgb25cbiAqIEBwYXJhbSBmaWx0ZXIgZmlsdGVyIG91dCBpdGVtcyB0aGF0IGV4aXN0IGluIHRoaXMgb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGV4Y2VwdChzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts similarity index 77% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts index 69866b354da99..8970f2b163664 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts @@ -1,26 +1,26 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; -type CrossRegionExports = { [exportName: string]: string }; +import { CrossRegionExports, ExportWriterCRProps } from '../types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const exports: CrossRegionExports = props.Exports; + const props: ExportWriterCRProps = event.ResourceProperties.WriterProps; + const exports = props.exports as CrossRegionExports; - const ssm = new SSM({ region: props.Region }); + const ssm = new SSM({ region: props.region }); try { switch (event.RequestType) { case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await throwIfAnyInUse(ssm, exports); await putParameters(ssm, exports); return; case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports: CrossRegionExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); + const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports as CrossRegionExports; + const newExports = except(exports, oldExports); await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await putParameters(ssm, newExports); return; case 'Delete': @@ -61,10 +61,10 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis }).promise(); result.TagList?.forEach(tag => { const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'cdk-strong-ref') { + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[1]) - : tagResults.set(name, new Set([tagParts[1]])); + ? tagResults.get(name)!.add(tagParts[2]) + : tagResults.set(name, new Set([tagParts[2]])); } }); @@ -93,7 +93,7 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis * @param source the source object to perform the filter on * @param filter filter out items that exist in this object */ -function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { +function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) .reduce((acc: CrossRegionExports, curr: string) => { diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/__entrypoint__.js new file mode 100644 index 0000000000000..1e3a3093c1706 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/__entrypoint__.js @@ -0,0 +1,144 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, exports.external.sendHttpRequest)(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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RUFBRSxFQUFFO1FBQ3hCLElBQUksUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDaEMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUN2QixPQUFPLElBQUksRUFBRTtZQUNYLElBQUk7Z0JBQ0YsT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2FBQ3hCO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsSUFBSSxRQUFRLEVBQUUsSUFBSSxDQUFDLEVBQUU7b0JBQ25CLE1BQU0sQ0FBQyxDQUFDO2lCQUNUO2dCQUNELE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDVDtTQUNGO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQWhCRCxrQ0FnQkM7QUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDN0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBodHRwcyBmcm9tICdodHRwcyc7XG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcblxuLy8gZm9yIHVuaXQgdGVzdHNcbmV4cG9ydCBjb25zdCBleHRlcm5hbCA9IHtcbiAgc2VuZEh0dHBSZXF1ZXN0OiBkZWZhdWx0U2VuZEh0dHBSZXF1ZXN0LFxuICBsb2c6IGRlZmF1bHRMb2csXG4gIGluY2x1ZGVTdGFja1RyYWNlczogdHJ1ZSxcbiAgdXNlckhhbmRsZXJJbmRleDogJy4vaW5kZXgnLFxufTtcblxuY29uc3QgQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpDUkVBVEVfRkFJTEVEJztcbmNvbnN0IE1JU1NJTkdfUEhZU0lDQUxfSURfTUFSS0VSID0gJ0FXU0NESzo6Q3VzdG9tUmVzb3VyY2VQcm92aWRlckZyYW1ld29yazo6TUlTU0lOR19QSFlTSUNBTF9JRCc7XG5cbmV4cG9ydCB0eXBlIFJlc3BvbnNlID0gQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIEhhbmRsZXJSZXNwb25zZTtcbmV4cG9ydCB0eXBlIEhhbmRsZXIgPSAoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSA9PiBQcm9taXNlPEhhbmRsZXJSZXNwb25zZSB8IHZvaWQ+O1xuZXhwb3J0IHR5cGUgSGFuZGxlclJlc3BvbnNlID0gdW5kZWZpbmVkIHwge1xuICBEYXRhPzogYW55O1xuICBQaHlzaWNhbFJlc291cmNlSWQ/OiBzdHJpbmc7XG4gIFJlYXNvbj86IHN0cmluZztcbiAgTm9FY2hvPzogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50LCBjb250ZXh0OiBBV1NMYW1iZGEuQ29udGV4dCkge1xuICBjb25zdCBzYW5pdGl6ZWRFdmVudCA9IHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9O1xuICBleHRlcm5hbC5sb2coSlNPTi5zdHJpbmdpZnkoc2FuaXRpemVkRXZlbnQsIHVuZGVmaW5lZCwgMikpO1xuXG4gIC8vIGlnbm9yZSBERUxFVEUgZXZlbnQgd2hlbiB0aGUgcGh5c2ljYWwgcmVzb3VyY2UgSUQgaXMgdGhlIG1hcmtlciB0aGF0XG4gIC8vIGluZGljYXRlcyB0aGF0IHRoaXMgREVMRVRFIGlzIGEgc3Vic2VxdWVudCBERUxFVEUgdG8gYSBmYWlsZWQgQ1JFQVRFXG4gIC8vIG9wZXJhdGlvbi5cbiAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgPT09IENSRUFURV9GQUlMRURfUEhZU0lDQUxfSURfTUFSS0VSKSB7XG4gICAgZXh0ZXJuYWwubG9nKCdpZ25vcmluZyBERUxFVEUgZXZlbnQgY2F1c2VkIGJ5IGEgZmFpbGVkIENSRUFURSBldmVudCcpO1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgZXZlbnQpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gaW52b2tlIHRoZSB1c2VyIGhhbmRsZXIuIHRoaXMgaXMgaW50ZW50aW9uYWxseSBpbnNpZGUgdGhlIHRyeS1jYXRjaCB0b1xuICAgIC8vIGVuc3VyZSB0aGF0IGlmIHRoZXJlIGlzIGFuIGVycm9yIGl0J3MgcmVwb3J0ZWQgYXMgYSBmYWlsdXJlIHRvXG4gICAgLy8gY2xvdWRmb3JtYXRpb24gKG90aGVyd2lzZSBjZm4gd2FpdHMpLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgdXNlckhhbmRsZXI6IEhhbmRsZXIgPSByZXF1aXJlKGV4dGVybmFsLnVzZXJIYW5kbGVySW5kZXgpLmhhbmRsZXI7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdXNlckhhbmRsZXIoc2FuaXRpemVkRXZlbnQsIGNvbnRleHQpO1xuXG4gICAgLy8gdmFsaWRhdGUgdXNlciByZXNwb25zZSBhbmQgY3JlYXRlIHRoZSBjb21iaW5lZCBldmVudFxuICAgIGNvbnN0IHJlc3BvbnNlRXZlbnQgPSByZW5kZXJSZXNwb25zZShldmVudCwgcmVzdWx0KTtcblxuICAgIC8vIHN1Ym1pdCB0byBjZm4gYXMgc3VjY2Vzc1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgcmVzcG9uc2VFdmVudCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zdCByZXNwOiBSZXNwb25zZSA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgUmVhc29uOiBleHRlcm5hbC5pbmNsdWRlU3RhY2tUcmFjZXMgPyBlLnN0YWNrIDogZS5tZXNzYWdlLFxuICAgIH07XG5cbiAgICBpZiAoIXJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgICAvLyBzcGVjaWFsIGNhc2U6IGlmIENSRUFURSBmYWlscywgd2hpY2ggdXN1YWxseSBpbXBsaWVzLCB3ZSB1c3VhbGx5IGRvbid0XG4gICAgICAvLyBoYXZlIGEgcGh5c2ljYWwgcmVzb3VyY2UgaWQuIGluIHRoaXMgY2FzZSwgdGhlIHN1YnNlcXVlbnQgREVMRVRFXG4gICAgICAvLyBvcGVyYXRpb24gZG9lcyBub3QgaGF2ZSBhbnkgbWVhbmluZywgYW5kIHdpbGwgbGlrZWx5IGZhaWwgYXMgd2VsbC4gdG9cbiAgICAgIC8vIGFkZHJlc3MgdGhpcywgd2UgdXNlIGEgbWFya2VyIHNvIHRoZSBwcm92aWRlciBmcmFtZXdvcmsgY2FuIHNpbXBseVxuICAgICAgLy8gaWdub3JlIHRoZSBzdWJzZXF1ZW50IERFTEVURS5cbiAgICAgIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScpIHtcbiAgICAgICAgZXh0ZXJuYWwubG9nKCdDUkVBVEUgZmFpbGVkLCByZXNwb25kaW5nIHdpdGggYSBtYXJrZXIgcGh5c2ljYWwgcmVzb3VyY2UgaWQgc28gdGhhdCB0aGUgc3Vic2VxdWVudCBERUxFVEUgd2lsbCBiZSBpZ25vcmVkJyk7XG4gICAgICAgIHJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkID0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBvdGhlcndpc2UsIGlmIFBoeXNpY2FsUmVzb3VyY2VJZCBpcyBub3Qgc3BlY2lmaWVkLCBzb21ldGhpbmcgaXNcbiAgICAgICAgLy8gdGVycmlibHkgd3JvbmcgYmVjYXVzZSBhbGwgb3RoZXIgZXZlbnRzIHNob3VsZCBoYXZlIGFuIElELlxuICAgICAgICBleHRlcm5hbC5sb2coYEVSUk9SOiBNYWxmb3JtZWQgZXZlbnQuIFwiUGh5c2ljYWxSZXNvdXJjZUlkXCIgaXMgcmVxdWlyZWQ6ICR7SlNPTi5zdHJpbmdpZnkoZXZlbnQpfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRoaXMgaXMgYW4gYWN0dWFsIGVycm9yLCBmYWlsIHRoZSBhY3Rpdml0eSBhbHRvZ2V0aGVyIGFuZCBleGlzdC5cbiAgICBhd2FpdCBzdWJtaXRSZXNwb25zZSgnRkFJTEVEJywgcmVzcCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVuZGVyUmVzcG9uc2UoXG4gIGNmblJlcXVlc3Q6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQgJiB7IFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZyB9LFxuICBoYW5kbGVyUmVzcG9uc2U6IHZvaWQgfCBIYW5kbGVyUmVzcG9uc2UgPSB7IH0pOiBSZXNwb25zZSB7XG5cbiAgLy8gaWYgcGh5c2ljYWwgSUQgaXMgbm90IHJldHVybmVkLCB3ZSBoYXZlIHNvbWUgZGVmYXVsdHMgZm9yIHlvdSBiYXNlZFxuICAvLyBvbiB0aGUgcmVxdWVzdCB0eXBlLlxuICBjb25zdCBwaHlzaWNhbFJlc291cmNlSWQgPSBoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUmVxdWVzdElkO1xuXG4gIC8vIGlmIHdlIGFyZSBpbiBERUxFVEUgYW5kIHBoeXNpY2FsIElEIHdhcyBjaGFuZ2VkLCBpdCdzIGFuIGVycm9yLlxuICBpZiAoY2ZuUmVxdWVzdC5SZXF1ZXN0VHlwZSA9PT0gJ0RlbGV0ZScgJiYgcGh5c2ljYWxSZXNvdXJjZUlkICE9PSBjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgREVMRVRFOiBjYW5ub3QgY2hhbmdlIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBmcm9tIFwiJHtjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZH1cIiB0byBcIiR7aGFuZGxlclJlc3BvbnNlLlBoeXNpY2FsUmVzb3VyY2VJZH1cIiBkdXJpbmcgZGVsZXRpb25gKTtcbiAgfVxuXG4gIC8vIG1lcmdlIHJlcXVlc3QgZXZlbnQgYW5kIHJlc3VsdCBldmVudCAocmVzdWx0IHByZXZhaWxzKS5cbiAgcmV0dXJuIHtcbiAgICAuLi5jZm5SZXF1ZXN0LFxuICAgIC4uLmhhbmRsZXJSZXNwb25zZSxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IHBoeXNpY2FsUmVzb3VyY2VJZCxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3VibWl0UmVzcG9uc2Uoc3RhdHVzOiAnU1VDQ0VTUycgfCAnRkFJTEVEJywgZXZlbnQ6IFJlc3BvbnNlKSB7XG4gIGNvbnN0IGpzb246IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlUmVzcG9uc2UgPSB7XG4gICAgU3RhdHVzOiBzdGF0dXMsXG4gICAgUmVhc29uOiBldmVudC5SZWFzb24gPz8gc3RhdHVzLFxuICAgIFN0YWNrSWQ6IGV2ZW50LlN0YWNrSWQsXG4gICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgfHwgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIsXG4gICAgTG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgIE5vRWNobzogZXZlbnQuTm9FY2hvLFxuICAgIERhdGE6IGV2ZW50LkRhdGEsXG4gIH07XG5cbiAgZXh0ZXJuYWwubG9nKCdzdWJtaXQgcmVzcG9uc2UgdG8gY2xvdWRmb3JtYXRpb24nLCBqc29uKTtcblxuICBjb25zdCByZXNwb25zZUJvZHkgPSBKU09OLnN0cmluZ2lmeShqc29uKTtcbiAgY29uc3QgcGFyc2VkVXJsID0gdXJsLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgY29uc3QgcmVxID0ge1xuICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgcGF0aDogcGFyc2VkVXJsLnBhdGgsXG4gICAgbWV0aG9kOiAnUFVUJyxcbiAgICBoZWFkZXJzOiB7ICdjb250ZW50LXR5cGUnOiAnJywgJ2NvbnRlbnQtbGVuZ3RoJzogcmVzcG9uc2VCb2R5Lmxlbmd0aCB9LFxuICB9O1xuXG4gIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHtcbiAgICBhdHRlbXB0czogNSxcbiAgICBzbGVlcDogMTAwMCxcbiAgfTtcbiAgYXdhaXQgd2l0aFJldHJpZXMocmV0cnlPcHRpb25zLCBleHRlcm5hbC5zZW5kSHR0cFJlcXVlc3QpKHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV0cnlPcHRpb25zIHtcbiAgLyoqIEhvdyBtYW55IHJldHJpZXMgKHdpbGwgYXQgbGVhc3QgdHJ5IG9uY2UpICovXG4gIHJlYWRvbmx5IGF0dGVtcHRzOiBudW1iZXI7XG4gIC8qKiBTbGVlcCBiYXNlLCBpbiBtcyAqL1xuICByZWFkb25seSBzbGVlcDogbnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJldHJpZXM8QSBleHRlbmRzIEFycmF5PGFueT4sIEI+KG9wdGlvbnM6IFJldHJ5T3B0aW9ucywgZm46ICguLi54czogQSkgPT4gUHJvbWlzZTxCPik6ICguLi54czogQSkgPT4gUHJvbWlzZTxCPiB7XG4gIHJldHVybiBhc3luYyAoLi4ueHM6IEEpID0+IHtcbiAgICBsZXQgYXR0ZW1wdHMgPSBvcHRpb25zLmF0dGVtcHRzO1xuICAgIGxldCBtcyA9IG9wdGlvbnMuc2xlZXA7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBmbiguLi54cyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChhdHRlbXB0cy0tIDw9IDApIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG4gICAgICAgIG1zICo9IDI7XG4gICAgICB9XG4gICAgfVxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgob2spID0+IHNldFRpbWVvdXQob2ssIG1zKSk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.d.ts diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js new file mode 100644 index 0000000000000..0cc6cf063d1c7 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js @@ -0,0 +1,124 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.ReaderProps; + const imports = props.imports; + const keyName = `aws-cdk:strong-ref:${props.prefix}`; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info('Tagging SSM Parameter imports'); + await addTags(ssm, imports, keyName); + return; + case 'Update': + const oldProps = event.OldResourceProperties.ReaderProps; + const oldExports = oldProps.imports; + const newExports = except(imports, oldExports); + const paramsToDelete = except(oldExports, imports); + console.info('Releasing unused SSM Parameter imports'); + if (Object.keys(paramsToDelete).length > 0) { + await removeTags(ssm, paramsToDelete, keyName); + } + console.info('Tagging new SSM Parameter imports'); + await addTags(ssm, newExports, keyName); + return; + case 'Delete': + console.info('Deleting all SSM Parameter exports'); + await deleteParametersByPath(ssm, `/cdk/exports/${props.prefix}/`); + return; + default: + return; + } + } + catch (e) { + console.error('Error importing cross region stack exports: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Add tag to parameters for existing exports + */ +async function addTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return await ssm.addTagsToResource({ + ResourceId: name, + ResourceType: 'Parameter', + Tags: [{ + Key: keyName, + Value: 'true', + }], + }).promise(); + } + catch (e) { + throw new Error(`Error importing ${name}: ${e}`); + } + })); +} +/** + * Remove tags from parameters + */ +async function removeTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return await ssm.removeTagsFromResource({ + TagKeys: [keyName], + ResourceType: 'Parameter', + ResourceId: name, + }).promise(); + } + catch (e) { + switch (e.code) { + // if the parameter doesn't exist then there is nothing to release + case 'InvalidResourceId': + return; + default: + throw new Error(`Error releasing import ${name}: ${e}`); + } + } + })); +} +/** + * Get all parameters in a given path + * + * If the request fails for any reason it will fail the custom resource event. + * Since this is only run when the resource is deleted that is probably the behavior + * that is desired. + */ +async function getParametersByPath(ssm, path) { + const parameters = []; + let nextToken; + do { + const response = await ssm.getParametersByPath({ Path: path, NextToken: nextToken }).promise(); + parameters.push(...response.Parameters ?? []); + nextToken = response.NextToken; + } while (nextToken); + return parameters; +} +/** + * Delete all parameters in a give path + */ +async function deleteParametersByPath(ssm, path) { + const allParams = await getParametersByPath(ssm, path); + const names = allParams.map(param => param.Name).filter(x => !!x); + await ssm.deleteParameters({ + Names: names, + }).promise(); +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function except(source, filter) { + return source.filter(key => !filter.includes(key)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFhLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFDeEMsTUFBTSxPQUFPLEdBQVcsc0JBQXNCLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUU3RCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLCtCQUErQixDQUFDLENBQUM7Z0JBQzlDLE1BQU0sT0FBTyxDQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3JDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQXdCLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxXQUFXLENBQUM7Z0JBQzlFLE1BQU0sVUFBVSxHQUFhLFFBQVEsQ0FBQyxPQUFPLENBQUM7Z0JBQzlDLE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sY0FBYyxHQUFHLE1BQU0sQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ25ELE9BQU8sQ0FBQyxJQUFJLENBQUMsd0NBQXdDLENBQUMsQ0FBQztnQkFDdkQsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7b0JBQzFDLE1BQU0sVUFBVSxDQUFDLEdBQUcsRUFBRSxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7aUJBQ2hEO2dCQUNELE9BQU8sQ0FBQyxJQUFJLENBQUMsbUNBQW1DLENBQUMsQ0FBQztnQkFDbEQsTUFBTSxPQUFPLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDeEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLG9DQUFvQyxDQUFDLENBQUM7Z0JBQ25ELE1BQU0sc0JBQXNCLENBQUMsR0FBRyxFQUFFLGdCQUFnQixLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztnQkFDbkUsT0FBTztZQUNUO2dCQUNFLE9BQU87U0FDVjtLQUNGO0lBQUMsT0FBTyxDQUFDLEVBQUU7UUFDVixPQUFPLENBQUMsS0FBSyxDQUFDLDhDQUE4QyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2pFLE1BQU0sQ0FBQyxDQUFDO0tBQ1Q7QUFDSCxDQUFDO0FBbkNELDBCQW1DQztBQUFBLENBQUM7QUFFRjs7R0FFRztBQUNILEtBQUssVUFBVSxPQUFPLENBQUMsR0FBUSxFQUFFLFVBQW9CLEVBQUUsT0FBZTtJQUNwRSxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUMsSUFBSSxFQUFDLEVBQUU7UUFDNUMsSUFBSTtZQUNGLE9BQU8sTUFBTSxHQUFHLENBQUMsaUJBQWlCLENBQUM7Z0JBQ2pDLFVBQVUsRUFBRSxJQUFJO2dCQUNoQixZQUFZLEVBQUUsV0FBVztnQkFDekIsSUFBSSxFQUFFLENBQUM7d0JBQ0wsR0FBRyxFQUFFLE9BQU87d0JBQ1osS0FBSyxFQUFFLE1BQU07cUJBQ2QsQ0FBQzthQUNILENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztTQUNkO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLElBQUksS0FBSyxDQUFDLG1CQUFtQixJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztTQUNsRDtJQUNILENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDTixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxLQUFLLFVBQVUsVUFBVSxDQUFDLEdBQVEsRUFBRSxVQUFvQixFQUFFLE9BQWU7SUFDdkUsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFDLElBQUksRUFBQyxFQUFFO1FBQzVDLElBQUk7WUFDRixPQUFPLE1BQU0sR0FBRyxDQUFDLHNCQUFzQixDQUFDO2dCQUN0QyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUM7Z0JBQ2xCLFlBQVksRUFBRSxXQUFXO2dCQUN6QixVQUFVLEVBQUUsSUFBSTthQUNqQixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7U0FDZDtRQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ1YsUUFBUSxDQUFDLENBQUMsSUFBSSxFQUFFO2dCQUNkLGtFQUFrRTtnQkFDbEUsS0FBSyxtQkFBbUI7b0JBQ3RCLE9BQU87Z0JBQ1Q7b0JBQ0UsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsSUFBSSxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDM0Q7U0FDRjtJQUNILENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDTixDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsS0FBSyxVQUFVLG1CQUFtQixDQUFDLEdBQVEsRUFBRSxJQUFZO0lBQ3ZELE1BQU0sVUFBVSxHQUFvQixFQUFFLENBQUM7SUFDdkMsSUFBSSxTQUE2QixDQUFDO0lBQ2xDLEdBQUc7UUFDRCxNQUFNLFFBQVEsR0FBRyxNQUFNLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDL0YsVUFBVSxDQUFDLElBQUksQ0FBQyxHQUFHLFFBQVEsQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUM7UUFDOUMsU0FBUyxHQUFHLFFBQVEsQ0FBQyxTQUFTLENBQUM7S0FFaEMsUUFBUSxTQUFTLEVBQUU7SUFDcEIsT0FBTyxVQUFVLENBQUM7QUFDcEIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsS0FBSyxVQUFVLHNCQUFzQixDQUFDLEdBQVEsRUFBRSxJQUFZO0lBQzFELE1BQU0sU0FBUyxHQUFHLE1BQU0sbUJBQW1CLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3ZELE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBYSxDQUFDO0lBQzlFLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO1FBQ3pCLEtBQUssRUFBRSxLQUFLO0tBQ2IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ2YsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxNQUFNLENBQUMsTUFBZ0IsRUFBRSxNQUFnQjtJQUNoRCxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUNyRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyplc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgeyBFeHBvcnRSZWFkZXJDUlByb3BzIH0gZnJvbSAnLi4vdHlwZXMnO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaGFuZGxlcihldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCkge1xuICBjb25zdCBwcm9wczogRXhwb3J0UmVhZGVyQ1JQcm9wcyA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5SZWFkZXJQcm9wcztcbiAgY29uc3QgaW1wb3J0czogc3RyaW5nW10gPSBwcm9wcy5pbXBvcnRzO1xuICBjb25zdCBrZXlOYW1lOiBzdHJpbmcgPSBgYXdzLWNkazpzdHJvbmctcmVmOiR7cHJvcHMucHJlZml4fWA7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMucmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgYXdhaXQgYWRkVGFncyhzc20sIGltcG9ydHMsIGtleU5hbWUpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wczogRXhwb3J0UmVhZGVyQ1JQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcy5SZWFkZXJQcm9wcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogc3RyaW5nW10gPSBvbGRQcm9wcy5pbXBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZXhjZXB0KGltcG9ydHMsIG9sZEV4cG9ydHMpO1xuICAgICAgICBjb25zdCBwYXJhbXNUb0RlbGV0ZSA9IGV4Y2VwdChvbGRFeHBvcnRzLCBpbXBvcnRzKTtcbiAgICAgICAgY29uc29sZS5pbmZvKCdSZWxlYXNpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgaW1wb3J0cycpO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCByZW1vdmVUYWdzKHNzbSwgcGFyYW1zVG9EZWxldGUsIGtleU5hbWUpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBuZXcgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGFkZFRhZ3Moc3NtLCBuZXdFeHBvcnRzLCBrZXlOYW1lKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKCdEZWxldGluZyBhbGwgU1NNIFBhcmFtZXRlciBleHBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtLCBgL2Nkay9leHBvcnRzLyR7cHJvcHMucHJlZml4fS9gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGltcG9ydGluZyBjcm9zcyByZWdpb24gc3RhY2sgZXhwb3J0czogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBBZGQgdGFnIHRvIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gYWRkVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5hZGRUYWdzVG9SZXNvdXJjZSh7XG4gICAgICAgIFJlc291cmNlSWQ6IG5hbWUsXG4gICAgICAgIFJlc291cmNlVHlwZTogJ1BhcmFtZXRlcicsXG4gICAgICAgIFRhZ3M6IFt7XG4gICAgICAgICAgS2V5OiBrZXlOYW1lLFxuICAgICAgICAgIFZhbHVlOiAndHJ1ZScsXG4gICAgICAgIH1dLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgaW1wb3J0aW5nICR7bmFtZX06ICR7ZX1gKTtcbiAgICB9XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBSZW1vdmUgdGFncyBmcm9tIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVtb3ZlVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5yZW1vdmVUYWdzRnJvbVJlc291cmNlKHtcbiAgICAgICAgVGFnS2V5czogW2tleU5hbWVdLFxuICAgICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgICAgICBSZXNvdXJjZUlkOiBuYW1lLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHN3aXRjaCAoZS5jb2RlKSB7XG4gICAgICAgIC8vIGlmIHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdCB0aGVuIHRoZXJlIGlzIG5vdGhpbmcgdG8gcmVsZWFzZVxuICAgICAgICBjYXNlICdJbnZhbGlkUmVzb3VyY2VJZCc6XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgcmVsZWFzaW5nIGltcG9ydCAke25hbWV9OiAke2V9YCk7XG4gICAgICB9XG4gICAgfVxuICB9KSk7XG59XG5cbi8qKlxuICogR2V0IGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZW4gcGF0aFxuICpcbiAqIElmIHRoZSByZXF1ZXN0IGZhaWxzIGZvciBhbnkgcmVhc29uIGl0IHdpbGwgZmFpbCB0aGUgY3VzdG9tIHJlc291cmNlIGV2ZW50LlxuICogU2luY2UgdGhpcyBpcyBvbmx5IHJ1biB3aGVuIHRoZSByZXNvdXJjZSBpcyBkZWxldGVkIHRoYXQgaXMgcHJvYmFibHkgdGhlIGJlaGF2aW9yXG4gKiB0aGF0IGlzIGRlc2lyZWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldFBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZyk6IFByb21pc2U8U1NNLlBhcmFtZXRlcltdPiB7XG4gIGNvbnN0IHBhcmFtZXRlcnM6IFNTTS5QYXJhbWV0ZXJbXSA9IFtdO1xuICBsZXQgbmV4dFRva2VuOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGRvIHtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHNzbS5nZXRQYXJhbWV0ZXJzQnlQYXRoKHsgUGF0aDogcGF0aCwgTmV4dFRva2VuOiBuZXh0VG9rZW4gfSkucHJvbWlzZSgpO1xuICAgIHBhcmFtZXRlcnMucHVzaCguLi5yZXNwb25zZS5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICBuZXh0VG9rZW4gPSByZXNwb25zZS5OZXh0VG9rZW47XG5cbiAgfSB3aGlsZSAobmV4dFRva2VuKTtcbiAgcmV0dXJuIHBhcmFtZXRlcnM7XG59XG5cbi8qKlxuICogRGVsZXRlIGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZSBwYXRoXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBhbGxQYXJhbXMgPSBhd2FpdCBnZXRQYXJhbWV0ZXJzQnlQYXRoKHNzbSwgcGF0aCk7XG4gIGNvbnN0IG5hbWVzID0gYWxsUGFyYW1zLm1hcChwYXJhbSA9PiBwYXJhbS5OYW1lKS5maWx0ZXIoeCA9PiAhIXgpIGFzIHN0cmluZ1tdO1xuICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IG5hbWVzLFxuICB9KS5wcm9taXNlKCk7XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZXhjZXB0KHNvdXJjZTogc3RyaW5nW10sIGZpbHRlcjogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBzb3VyY2UuZmlsdGVyKGtleSA9PiAhZmlsdGVyLmluY2x1ZGVzKGtleSkpO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.ts similarity index 74% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.ts rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.ts index fd5e4422c2df2..7c92fde2bf1c7 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf/index.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.ts @@ -1,13 +1,14 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; +import { ExportReaderCRProps } from '../types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const imports: string[] = props.Imports; - const keyName: string = `cdk-strong-ref:${props.StackName}`; + const props: ExportReaderCRProps = event.ResourceProperties.ReaderProps; + const imports: string[] = props.imports; + const keyName: string = `aws-cdk:strong-ref:${props.prefix}`; - const ssm = new SSM({ region: props.Region }); + const ssm = new SSM({ region: props.region }); try { switch (event.RequestType) { case 'Create': @@ -15,10 +16,10 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent await addTags(ssm, imports, keyName); return; case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports: string[] = oldProps.Imports; - const newExports = filterExports(imports, oldExports); - const paramsToDelete = filterExports(oldExports, imports); + const oldProps: ExportReaderCRProps = event.OldResourceProperties.ReaderProps; + const oldExports: string[] = oldProps.imports; + const newExports = except(imports, oldExports); + const paramsToDelete = except(oldExports, imports); console.info('Releasing unused SSM Parameter imports'); if (Object.keys(paramsToDelete).length > 0) { await removeTags(ssm, paramsToDelete, keyName); @@ -28,7 +29,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent return; case 'Delete': console.info('Deleting all SSM Parameter exports'); - await deleteParametersByPath(ssm, `/cdk/exports/${props.StackName}/`); + await deleteParametersByPath(ssm, `/cdk/exports/${props.prefix}/`); return; default: return; @@ -89,18 +90,16 @@ async function removeTags(ssm: SSM, parameters: string[], keyName: string): Prom * Since this is only run when the resource is deleted that is probably the behavior * that is desired. */ -async function getParametersByPath(ssm: SSM, path: string, nextToken?: string): Promise { +async function getParametersByPath(ssm: SSM, path: string): Promise { const parameters: SSM.Parameter[] = []; - return ssm.getParametersByPath({ - Path: path, - NextToken: nextToken, - }).promise().then(async getParametersByPathResult => { - parameters.push(...getParametersByPathResult.Parameters ?? []); - if (getParametersByPathResult.NextToken) { - parameters.push(...await getParametersByPath(ssm, path, getParametersByPathResult.NextToken)); - } - return parameters; - }); + let nextToken: string | undefined; + do { + const response = await ssm.getParametersByPath({ Path: path, NextToken: nextToken }).promise(); + parameters.push(...response.Parameters ?? []); + nextToken = response.NextToken; + + } while (nextToken); + return parameters; } /** @@ -120,6 +119,6 @@ async function deleteParametersByPath(ssm: SSM, path: string): Promise { * @param source the source object to perform the filter on * @param filter filter out items that exist in this object */ -function filterExports(source: string[], filter: string[]): string[] { +function except(source: string[], filter: string[]): string[] { return source.filter(key => !filter.includes(key)); } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js deleted file mode 100644 index 9df94382cc74e..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js +++ /dev/null @@ -1,118 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = exports.external = void 0; -const https = require("https"); -const url = require("url"); -// for unit tests -exports.external = { - sendHttpRequest: defaultSendHttpRequest, - log: defaultLog, - includeStackTraces: true, - userHandlerIndex: './index', -}; -const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; -const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; -async function handler(event, context) { - const sanitizedEvent = { ...event, ResponseURL: '...' }; - exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); - // ignore DELETE event when the physical resource ID is the marker that - // indicates that this DELETE is a subsequent DELETE to a failed CREATE - // operation. - if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { - exports.external.log('ignoring DELETE event caused by a failed CREATE event'); - await submitResponse('SUCCESS', event); - return; - } - try { - // invoke the user handler. this is intentionally inside the try-catch to - // ensure that if there is an error it's reported as a failure to - // cloudformation (otherwise cfn waits). - // eslint-disable-next-line @typescript-eslint/no-require-imports - const userHandler = require(exports.external.userHandlerIndex).handler; - const result = await userHandler(sanitizedEvent, context); - // validate user response and create the combined event - const responseEvent = renderResponse(event, result); - // submit to cfn as success - await submitResponse('SUCCESS', responseEvent); - } - catch (e) { - const resp = { - ...event, - Reason: exports.external.includeStackTraces ? e.stack : e.message, - }; - if (!resp.PhysicalResourceId) { - // special case: if CREATE fails, which usually implies, we usually don't - // have a physical resource id. in this case, the subsequent DELETE - // operation does not have any meaning, and will likely fail as well. to - // address this, we use a marker so the provider framework can simply - // ignore the subsequent DELETE. - if (event.RequestType === 'Create') { - exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); - resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; - } - else { - // otherwise, if PhysicalResourceId is not specified, something is - // terribly wrong because all other events should have an ID. - exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); - } - } - // this is an actual error, fail the activity altogether and exist. - await submitResponse('FAILED', resp); - } -} -exports.handler = handler; -function renderResponse(cfnRequest, handlerResponse = {}) { - // if physical ID is not returned, we have some defaults for you based - // on the request type. - const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; - // if we are in DELETE and physical ID was changed, it's an error. - if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { - throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); - } - // merge request event and result event (result prevails). - return { - ...cfnRequest, - ...handlerResponse, - PhysicalResourceId: physicalResourceId, - }; -} -async function submitResponse(status, event) { - const json = { - Status: status, - Reason: event.Reason ?? status, - StackId: event.StackId, - RequestId: event.RequestId, - PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, - LogicalResourceId: event.LogicalResourceId, - NoEcho: event.NoEcho, - Data: event.Data, - }; - exports.external.log('submit response to cloudformation', json); - const responseBody = JSON.stringify(json); - const parsedUrl = url.parse(event.ResponseURL); - const req = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { 'content-type': '', 'content-length': responseBody.length }, - }; - await exports.external.sendHttpRequest(req, responseBody); -} -async function defaultSendHttpRequest(options, responseBody) { - return new Promise((resolve, reject) => { - try { - const request = https.request(options, _ => resolve()); - request.on('error', reject); - request.write(responseBody); - request.end(); - } - catch (e) { - reject(e); - } - }); -} -function defaultLog(fmt, ...params) { - // eslint-disable-next-line no-console - console.log(fmt, ...params); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js deleted file mode 100644 index a487b651f8d38..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties; - const exports = props.Exports; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm, parameters) { - const tagResults = new Map(); - await Promise.all(Object.keys(parameters).map(async (name) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'cdk-strong-ref') { - tagResults.has(name) - ? tagResults.get(name).add(tagParts[1]) - : tagResults.set(name, new Set([tagParts[1]])); - } - }); - } - catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - })); - if (tagResults.size > 0) { - const message = Object.entries(tagResults) - .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function filterExports(source, filter) { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNwQyxNQUFNLGFBQWEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2xDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDO2dCQUM3QyxNQUFNLFVBQVUsR0FBdUIsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDeEQsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN2QyxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxhQUFhLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFR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SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztTQUN2QixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDM0UsTUFBTSxDQUFDLENBQUMsR0FBdUIsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUNoRCxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ1gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSBvbGRQcm9wcy5FeHBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdjZGstc3Ryb25nLXJlZicpIHtcbiAgICAgICAgICB0YWdSZXN1bHRzLmhhcyhuYW1lKVxuICAgICAgICAgICAgPyB0YWdSZXN1bHRzLmdldChuYW1lKSEuYWRkKHRhZ1BhcnRzWzFdKVxuICAgICAgICAgICAgOiB0YWdSZXN1bHRzLnNldChuYW1lLCBuZXcgU2V0KFt0YWdQYXJ0c1sxXV0pKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBhbiBJbnZhbGlkUmVzb3VyY2VJZCBtZWFucyB0aGF0IHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdFxuICAgICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgICAgaWYgKGUuY29kZSA9PT0gJ0ludmFsaWRSZXNvdXJjZUlkJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cblxuICB9KSk7XG5cbiAgaWYgKHRhZ1Jlc3VsdHMuc2l6ZSA+IDApIHtcbiAgICBjb25zdCBtZXNzYWdlOiBzdHJpbmcgPSBPYmplY3QuZW50cmllcyh0YWdSZXN1bHRzKVxuICAgICAgLm1hcCgocmVzdWx0OiBbc3RyaW5nLCBzdHJpbmdbXV0pID0+IGAke3Jlc3VsdFswXX0gaXMgaW4gdXNlIGJ5IHN0YWNrKHMpICR7cmVzdWx0WzFdLmpvaW4oJyAnKX1gKVxuICAgICAgLmpvaW4oJ1xcbicpO1xuICAgIHRocm93IG5ldyBFcnJvcihgRXhwb3J0cyBjYW5ub3QgYmUgdXBkYXRlZDogXFxuJHttZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json index 33b3b650da092..b96a348a60978 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json @@ -1,21 +1,21 @@ { "version": "21.0.0", "files": { - "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc": { + "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03": { "source": { - "path": "asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc", + "path": "asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03", "packaging": "zip" }, "destinations": { "12345678-us-east-1": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", - "objectKey": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip", + "objectKey": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" } } }, - "4419fd51b8084fccb4113ddda58c48d294c907c5ed5a69b01405d0bdf4b7aee2": { + "8f45c6dc78189ec64aedbbe51f14c1ab502625ff42f76ea4b3133f6518d09ab4": { "source": { "path": "integ-acm-stack.template.json", "packaging": "file" @@ -23,7 +23,7 @@ "destinations": { "12345678-us-east-1": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", - "objectKey": "4419fd51b8084fccb4113ddda58c48d294c907c5ed5a69b01405d0bdf4b7aee2.json", + "objectKey": "8f45c6dc78189ec64aedbbe51f14c1ab502625ff42f76ea4b3133f6518d09ab4.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json index 3bea1a4fa002e..38c7ef05ce839 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json @@ -22,10 +22,12 @@ "Arn" ] }, - "Region": "us-east-2", - "Exports": { - "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2": { - "Ref": "Cert5C9FAEC1" + "WriterProps": { + "region": "us-east-2", + "exports": { + "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2": { + "Ref": "Cert5C9FAEC1" + } } } }, @@ -78,7 +80,7 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-1", - "S3Key": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip" + "S3Key": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json index d23d133596e3b..ee98d77e6ccd9 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json @@ -1,21 +1,21 @@ { "version": "21.0.0", "files": { - "050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf": { + "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878": { "source": { - "path": "asset.050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf", + "path": "asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878", "packaging": "zip" }, "destinations": { "12345678-us-east-2": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf.zip", + "objectKey": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } }, - "8fcffd645cc61b1c9eccdc465ce88708586341f42dc5a067cadd1a6d00a72a9f": { + "a34d8c9d8e266042eca500b0479cd35e6f3a1b782b3c37fd090a83ac30f2f24a": { "source": { "path": "integ-cloudfront-stack.template.json", "packaging": "file" @@ -23,7 +23,7 @@ "destinations": { "12345678-us-east-2": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "8fcffd645cc61b1c9eccdc465ce88708586341f42dc5a067cadd1a6d00a72a9f.json", + "objectKey": "a34d8c9d8e266042eca500b0479cd35e6f3a1b782b3c37fd090a83ac30f2f24a.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json index d80474d0862d1..c77430560de99 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json @@ -42,11 +42,13 @@ "Arn" ] }, - "Region": "us-east-2", - "StackName": "integ-cloudfront-stack", - "Imports": [ - "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2" - ] + "ReaderProps": { + "region": "us-east-2", + "prefix": "integ-cloudfront-stack", + "imports": [ + "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2" + ] + } }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -99,7 +101,7 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", - "S3Key": "050cb1cdb12429c67ac88971dbcf1ed3a84d4bc4ba17ac3f7abd95d2ae75f7cf.zip" + "S3Key": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json index 071ec3deea392..92386d70e6b6a 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/4419fd51b8084fccb4113ddda58c48d294c907c5ed5a69b01405d0bdf4b7aee2.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/8f45c6dc78189ec64aedbbe51f14c1ab502625ff42f76ea4b3133f6518d09ab4.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -88,7 +88,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/8fcffd645cc61b1c9eccdc465ce88708586341f42dc5a067cadd1a6d00a72a9f.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/a34d8c9d8e266042eca500b0479cd35e6f3a1b782b3c37fd090a83ac30f2f24a.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js new file mode 100644 index 0000000000000..1e3a3093c1706 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js @@ -0,0 +1,144 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, exports.external.sendHttpRequest)(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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RUFBRSxFQUFFO1FBQ3hCLElBQUksUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDaEMsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQztRQUN2QixPQUFPLElBQUksRUFBRTtZQUNYLElBQUk7Z0JBQ0YsT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2FBQ3hCO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsSUFBSSxRQUFRLEVBQUUsSUFBSSxDQUFDLEVBQUU7b0JBQ25CLE1BQU0sQ0FBQyxDQUFDO2lCQUNUO2dCQUNELE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDVDtTQUNGO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQWhCRCxrQ0FnQkM7QUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDN0IsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQ2pELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBodHRwcyBmcm9tICdodHRwcyc7XG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcblxuLy8gZm9yIHVuaXQgdGVzdHNcbmV4cG9ydCBjb25zdCBleHRlcm5hbCA9IHtcbiAgc2VuZEh0dHBSZXF1ZXN0OiBkZWZhdWx0U2VuZEh0dHBSZXF1ZXN0LFxuICBsb2c6IGRlZmF1bHRMb2csXG4gIGluY2x1ZGVTdGFja1RyYWNlczogdHJ1ZSxcbiAgdXNlckhhbmRsZXJJbmRleDogJy4vaW5kZXgnLFxufTtcblxuY29uc3QgQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpDUkVBVEVfRkFJTEVEJztcbmNvbnN0IE1JU1NJTkdfUEhZU0lDQUxfSURfTUFSS0VSID0gJ0FXU0NESzo6Q3VzdG9tUmVzb3VyY2VQcm92aWRlckZyYW1ld29yazo6TUlTU0lOR19QSFlTSUNBTF9JRCc7XG5cbmV4cG9ydCB0eXBlIFJlc3BvbnNlID0gQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIEhhbmRsZXJSZXNwb25zZTtcbmV4cG9ydCB0eXBlIEhhbmRsZXIgPSAoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSA9PiBQcm9taXNlPEhhbmRsZXJSZXNwb25zZSB8IHZvaWQ+O1xuZXhwb3J0IHR5cGUgSGFuZGxlclJlc3BvbnNlID0gdW5kZWZpbmVkIHwge1xuICBEYXRhPzogYW55O1xuICBQaHlzaWNhbFJlc291cmNlSWQ/OiBzdHJpbmc7XG4gIFJlYXNvbj86IHN0cmluZztcbiAgTm9FY2hvPzogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50LCBjb250ZXh0OiBBV1NMYW1iZGEuQ29udGV4dCkge1xuICBjb25zdCBzYW5pdGl6ZWRFdmVudCA9IHsgLi4uZXZlbnQsIFJlc3BvbnNlVVJMOiAnLi4uJyB9O1xuICBleHRlcm5hbC5sb2coSlNPTi5zdHJpbmdpZnkoc2FuaXRpemVkRXZlbnQsIHVuZGVmaW5lZCwgMikpO1xuXG4gIC8vIGlnbm9yZSBERUxFVEUgZXZlbnQgd2hlbiB0aGUgcGh5c2ljYWwgcmVzb3VyY2UgSUQgaXMgdGhlIG1hcmtlciB0aGF0XG4gIC8vIGluZGljYXRlcyB0aGF0IHRoaXMgREVMRVRFIGlzIGEgc3Vic2VxdWVudCBERUxFVEUgdG8gYSBmYWlsZWQgQ1JFQVRFXG4gIC8vIG9wZXJhdGlvbi5cbiAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgPT09IENSRUFURV9GQUlMRURfUEhZU0lDQUxfSURfTUFSS0VSKSB7XG4gICAgZXh0ZXJuYWwubG9nKCdpZ25vcmluZyBERUxFVEUgZXZlbnQgY2F1c2VkIGJ5IGEgZmFpbGVkIENSRUFURSBldmVudCcpO1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgZXZlbnQpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gaW52b2tlIHRoZSB1c2VyIGhhbmRsZXIuIHRoaXMgaXMgaW50ZW50aW9uYWxseSBpbnNpZGUgdGhlIHRyeS1jYXRjaCB0b1xuICAgIC8vIGVuc3VyZSB0aGF0IGlmIHRoZXJlIGlzIGFuIGVycm9yIGl0J3MgcmVwb3J0ZWQgYXMgYSBmYWlsdXJlIHRvXG4gICAgLy8gY2xvdWRmb3JtYXRpb24gKG90aGVyd2lzZSBjZm4gd2FpdHMpLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgY29uc3QgdXNlckhhbmRsZXI6IEhhbmRsZXIgPSByZXF1aXJlKGV4dGVybmFsLnVzZXJIYW5kbGVySW5kZXgpLmhhbmRsZXI7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdXNlckhhbmRsZXIoc2FuaXRpemVkRXZlbnQsIGNvbnRleHQpO1xuXG4gICAgLy8gdmFsaWRhdGUgdXNlciByZXNwb25zZSBhbmQgY3JlYXRlIHRoZSBjb21iaW5lZCBldmVudFxuICAgIGNvbnN0IHJlc3BvbnNlRXZlbnQgPSByZW5kZXJSZXNwb25zZShldmVudCwgcmVzdWx0KTtcblxuICAgIC8vIHN1Ym1pdCB0byBjZm4gYXMgc3VjY2Vzc1xuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdTVUNDRVNTJywgcmVzcG9uc2VFdmVudCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zdCByZXNwOiBSZXNwb25zZSA9IHtcbiAgICAgIC4uLmV2ZW50LFxuICAgICAgUmVhc29uOiBleHRlcm5hbC5pbmNsdWRlU3RhY2tUcmFjZXMgPyBlLnN0YWNrIDogZS5tZXNzYWdlLFxuICAgIH07XG5cbiAgICBpZiAoIXJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgICAvLyBzcGVjaWFsIGNhc2U6IGlmIENSRUFURSBmYWlscywgd2hpY2ggdXN1YWxseSBpbXBsaWVzLCB3ZSB1c3VhbGx5IGRvbid0XG4gICAgICAvLyBoYXZlIGEgcGh5c2ljYWwgcmVzb3VyY2UgaWQuIGluIHRoaXMgY2FzZSwgdGhlIHN1YnNlcXVlbnQgREVMRVRFXG4gICAgICAvLyBvcGVyYXRpb24gZG9lcyBub3QgaGF2ZSBhbnkgbWVhbmluZywgYW5kIHdpbGwgbGlrZWx5IGZhaWwgYXMgd2VsbC4gdG9cbiAgICAgIC8vIGFkZHJlc3MgdGhpcywgd2UgdXNlIGEgbWFya2VyIHNvIHRoZSBwcm92aWRlciBmcmFtZXdvcmsgY2FuIHNpbXBseVxuICAgICAgLy8gaWdub3JlIHRoZSBzdWJzZXF1ZW50IERFTEVURS5cbiAgICAgIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScpIHtcbiAgICAgICAgZXh0ZXJuYWwubG9nKCdDUkVBVEUgZmFpbGVkLCByZXNwb25kaW5nIHdpdGggYSBtYXJrZXIgcGh5c2ljYWwgcmVzb3VyY2UgaWQgc28gdGhhdCB0aGUgc3Vic2VxdWVudCBERUxFVEUgd2lsbCBiZSBpZ25vcmVkJyk7XG4gICAgICAgIHJlc3AuUGh5c2ljYWxSZXNvdXJjZUlkID0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBvdGhlcndpc2UsIGlmIFBoeXNpY2FsUmVzb3VyY2VJZCBpcyBub3Qgc3BlY2lmaWVkLCBzb21ldGhpbmcgaXNcbiAgICAgICAgLy8gdGVycmlibHkgd3JvbmcgYmVjYXVzZSBhbGwgb3RoZXIgZXZlbnRzIHNob3VsZCBoYXZlIGFuIElELlxuICAgICAgICBleHRlcm5hbC5sb2coYEVSUk9SOiBNYWxmb3JtZWQgZXZlbnQuIFwiUGh5c2ljYWxSZXNvdXJjZUlkXCIgaXMgcmVxdWlyZWQ6ICR7SlNPTi5zdHJpbmdpZnkoZXZlbnQpfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHRoaXMgaXMgYW4gYWN0dWFsIGVycm9yLCBmYWlsIHRoZSBhY3Rpdml0eSBhbHRvZ2V0aGVyIGFuZCBleGlzdC5cbiAgICBhd2FpdCBzdWJtaXRSZXNwb25zZSgnRkFJTEVEJywgcmVzcCk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVuZGVyUmVzcG9uc2UoXG4gIGNmblJlcXVlc3Q6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQgJiB7IFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZyB9LFxuICBoYW5kbGVyUmVzcG9uc2U6IHZvaWQgfCBIYW5kbGVyUmVzcG9uc2UgPSB7IH0pOiBSZXNwb25zZSB7XG5cbiAgLy8gaWYgcGh5c2ljYWwgSUQgaXMgbm90IHJldHVybmVkLCB3ZSBoYXZlIHNvbWUgZGVmYXVsdHMgZm9yIHlvdSBiYXNlZFxuICAvLyBvbiB0aGUgcmVxdWVzdCB0eXBlLlxuICBjb25zdCBwaHlzaWNhbFJlc291cmNlSWQgPSBoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkID8/IGNmblJlcXVlc3QuUmVxdWVzdElkO1xuXG4gIC8vIGlmIHdlIGFyZSBpbiBERUxFVEUgYW5kIHBoeXNpY2FsIElEIHdhcyBjaGFuZ2VkLCBpdCdzIGFuIGVycm9yLlxuICBpZiAoY2ZuUmVxdWVzdC5SZXF1ZXN0VHlwZSA9PT0gJ0RlbGV0ZScgJiYgcGh5c2ljYWxSZXNvdXJjZUlkICE9PSBjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgREVMRVRFOiBjYW5ub3QgY2hhbmdlIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBmcm9tIFwiJHtjZm5SZXF1ZXN0LlBoeXNpY2FsUmVzb3VyY2VJZH1cIiB0byBcIiR7aGFuZGxlclJlc3BvbnNlLlBoeXNpY2FsUmVzb3VyY2VJZH1cIiBkdXJpbmcgZGVsZXRpb25gKTtcbiAgfVxuXG4gIC8vIG1lcmdlIHJlcXVlc3QgZXZlbnQgYW5kIHJlc3VsdCBldmVudCAocmVzdWx0IHByZXZhaWxzKS5cbiAgcmV0dXJuIHtcbiAgICAuLi5jZm5SZXF1ZXN0LFxuICAgIC4uLmhhbmRsZXJSZXNwb25zZSxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IHBoeXNpY2FsUmVzb3VyY2VJZCxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc3VibWl0UmVzcG9uc2Uoc3RhdHVzOiAnU1VDQ0VTUycgfCAnRkFJTEVEJywgZXZlbnQ6IFJlc3BvbnNlKSB7XG4gIGNvbnN0IGpzb246IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlUmVzcG9uc2UgPSB7XG4gICAgU3RhdHVzOiBzdGF0dXMsXG4gICAgUmVhc29uOiBldmVudC5SZWFzb24gPz8gc3RhdHVzLFxuICAgIFN0YWNrSWQ6IGV2ZW50LlN0YWNrSWQsXG4gICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBldmVudC5QaHlzaWNhbFJlc291cmNlSWQgfHwgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIsXG4gICAgTG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgIE5vRWNobzogZXZlbnQuTm9FY2hvLFxuICAgIERhdGE6IGV2ZW50LkRhdGEsXG4gIH07XG5cbiAgZXh0ZXJuYWwubG9nKCdzdWJtaXQgcmVzcG9uc2UgdG8gY2xvdWRmb3JtYXRpb24nLCBqc29uKTtcblxuICBjb25zdCByZXNwb25zZUJvZHkgPSBKU09OLnN0cmluZ2lmeShqc29uKTtcbiAgY29uc3QgcGFyc2VkVXJsID0gdXJsLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgY29uc3QgcmVxID0ge1xuICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgcGF0aDogcGFyc2VkVXJsLnBhdGgsXG4gICAgbWV0aG9kOiAnUFVUJyxcbiAgICBoZWFkZXJzOiB7ICdjb250ZW50LXR5cGUnOiAnJywgJ2NvbnRlbnQtbGVuZ3RoJzogcmVzcG9uc2VCb2R5Lmxlbmd0aCB9LFxuICB9O1xuXG4gIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHtcbiAgICBhdHRlbXB0czogNSxcbiAgICBzbGVlcDogMTAwMCxcbiAgfTtcbiAgYXdhaXQgd2l0aFJldHJpZXMocmV0cnlPcHRpb25zLCBleHRlcm5hbC5zZW5kSHR0cFJlcXVlc3QpKHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV0cnlPcHRpb25zIHtcbiAgLyoqIEhvdyBtYW55IHJldHJpZXMgKHdpbGwgYXQgbGVhc3QgdHJ5IG9uY2UpICovXG4gIHJlYWRvbmx5IGF0dGVtcHRzOiBudW1iZXI7XG4gIC8qKiBTbGVlcCBiYXNlLCBpbiBtcyAqL1xuICByZWFkb25seSBzbGVlcDogbnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJldHJpZXM8QSBleHRlbmRzIEFycmF5PGFueT4sIEI+KG9wdGlvbnM6IFJldHJ5T3B0aW9ucywgZm46ICguLi54czogQSkgPT4gUHJvbWlzZTxCPik6ICguLi54czogQSkgPT4gUHJvbWlzZTxCPiB7XG4gIHJldHVybiBhc3luYyAoLi4ueHM6IEEpID0+IHtcbiAgICBsZXQgYXR0ZW1wdHMgPSBvcHRpb25zLmF0dGVtcHRzO1xuICAgIGxldCBtcyA9IG9wdGlvbnMuc2xlZXA7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBhd2FpdCBmbiguLi54cyk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChhdHRlbXB0cy0tIDw9IDApIHtcbiAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHNsZWVwKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIG1zKSk7XG4gICAgICAgIG1zICo9IDI7XG4gICAgICB9XG4gICAgfVxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgob2spID0+IHNldFRpbWVvdXQob2ssIG1zKSk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts similarity index 100% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.d.ts rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js new file mode 100644 index 0000000000000..f432d8df83b5e --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js @@ -0,0 +1,102 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.WriterProps; + const exports = props.exports; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports; + const newExports = except(exports, oldExports); + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // consuming stack will delete parameters + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + try { + const result = await ssm.listTagsForResource({ + ResourceId: name, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { + tagResults.has(name) + ? tagResults.get(name).add(tagParts[2]) + : tagResults.set(name, new Set([tagParts[2]])); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return; + } + throw e; + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function except(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sZUFBZSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDckMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCx5Q0FBeUM7Z0JBQ3pDLE9BQU87WUFDVDtnQkFDRSxPQUFPO1NBQ1Y7S0FDRjtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsT0FBTyxDQUFDLEtBQUssQ0FBQywwQkFBMEIsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM3QyxNQUFNLENBQUMsQ0FBQztLQUNUO0FBQ0gsQ0FBQztBQTlCRCwwQkE4QkM7QUFBQSxDQUFDO0FBRUY7O0dBRUc7QUFDSCxLQUFLLFVBQVUsYUFBYSxDQUFDLEdBQVEsRUFBRSxVQUE4QjtJQUNuRSxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRTtRQUN6RSxPQUFPLEdBQUcsQ0FBQyxZQUFZLENBQUM7WUFDdEIsSUFBSSxFQUFFLElBQUk7WUFDVixLQUFLLEVBQUUsS0FBSztZQUNaLElBQUksRUFBRSxRQUFRO1NBQ2YsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2YsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNOLENBQUM7QUFFRDs7R0FFRztBQUNILEtBQUssVUFBVSxlQUFlLENBQUMsR0FBUSxFQUFFLFVBQThCO0lBQ3JFLE1BQU0sVUFBVSxHQUE2QixJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ3ZELE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDbkUsSUFBSTtZQUNGLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLG1CQUFtQixDQUFDO2dCQUMzQyxVQUFVLEVBQUUsSUFBSTtnQkFDaEIsWUFBWSxFQUFFLFdBQVc7YUFDMUIsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsTUFBTSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUU7Z0JBQzVCLE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNwQyxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxTQUFTLElBQUksUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLFlBQVksRUFBRTtvQkFDN0QsVUFBVSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUM7d0JBQ2xCLENBQUMsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBRSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBQ3hDLENBQUMsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztpQkFDbEQ7WUFDSCxDQUFDLENBQUMsQ0FBQztTQUVKO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDViw4REFBOEQ7WUFDOUQsMERBQTBEO1lBQzFELElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxtQkFBbUIsRUFBRTtnQkFDbEMsT0FBTzthQUNSO1lBQ0QsTUFBTSxDQUFDLENBQUM7U0FDVDtJQUVILENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFSixJQUFJLFVBQVUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFO1FBQ3ZCLE1BQU0sT0FBTyxHQUFXLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDO2FBQy9DLEdBQUcsQ0FBQyxDQUFDLE1BQTBCLEVBQUUsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQywwQkFBMEIsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2FBQ2hHLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0NBQWdDLE9BQU8sRUFBRSxDQUFDLENBQUM7S0FDNUQ7QUFDSCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLE1BQU0sQ0FBQyxNQUEwQixFQUFFLE1BQTBCO0lBQ3BFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7U0FDdkIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzNFLE1BQU0sQ0FBQyxDQUFDLEdBQXVCLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDaEQsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNYLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IENyb3NzUmVnaW9uRXhwb3J0cywgRXhwb3J0V3JpdGVyQ1JQcm9wcyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gIGNvbnN0IGV4cG9ydHMgPSBwcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcblxuICBjb25zdCBzc20gPSBuZXcgU1NNKHsgcmVnaW9uOiBwcm9wcy5yZWdpb24gfSk7XG4gIHRyeSB7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHMgPSBvbGRQcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGV4Y2VwdChleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgICAgdGFnUmVzdWx0cy5oYXMobmFtZSlcbiAgICAgICAgICAgID8gdGFnUmVzdWx0cy5nZXQobmFtZSkhLmFkZCh0YWdQYXJ0c1syXSlcbiAgICAgICAgICAgIDogdGFnUmVzdWx0cy5zZXQobmFtZSwgbmV3IFNldChbdGFnUGFydHNbMl1dKSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gYW4gSW52YWxpZFJlc291cmNlSWQgbWVhbnMgdGhhdCB0aGUgcGFyYW1ldGVyIGRvZXNuJ3QgZXhpc3RcbiAgICAgIC8vIHdoaWNoIHdlIHNob3VsZCBpZ25vcmUgc2luY2UgdGhhdCBtZWFucyBpdCdzIG5vdCBpbiB1c2VcbiAgICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhyb3cgZTtcbiAgICB9XG5cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiBvbmx5IHRoZSBpdGVtcyBmcm9tIHNvdXJjZSB0aGF0IGRvIG5vdCBleGlzdCBpbiB0aGUgZmlsdGVyXG4gKlxuICogQHBhcmFtIHNvdXJjZSB0aGUgc291cmNlIG9iamVjdCB0byBwZXJmb3JtIHRoZSBmaWx0ZXIgb25cbiAqIEBwYXJhbSBmaWx0ZXIgZmlsdGVyIG91dCBpdGVtcyB0aGF0IGV4aXN0IGluIHRoaXMgb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGV4Y2VwdChzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts similarity index 77% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts index 69866b354da99..8970f2b163664 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts @@ -1,26 +1,26 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; -type CrossRegionExports = { [exportName: string]: string }; +import { CrossRegionExports, ExportWriterCRProps } from '../types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props = event.ResourceProperties; - const exports: CrossRegionExports = props.Exports; + const props: ExportWriterCRProps = event.ResourceProperties.WriterProps; + const exports = props.exports as CrossRegionExports; - const ssm = new SSM({ region: props.Region }); + const ssm = new SSM({ region: props.region }); try { switch (event.RequestType) { case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await throwIfAnyInUse(ssm, exports); await putParameters(ssm, exports); return; case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports: CrossRegionExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); + const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports as CrossRegionExports; + const newExports = except(exports, oldExports); await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); await putParameters(ssm, newExports); return; case 'Delete': @@ -61,10 +61,10 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis }).promise(); result.TagList?.forEach(tag => { const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'cdk-strong-ref') { + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[1]) - : tagResults.set(name, new Set([tagParts[1]])); + ? tagResults.get(name)!.add(tagParts[2]) + : tagResults.set(name, new Set([tagParts[2]])); } }); @@ -93,7 +93,7 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis * @param source the source object to perform the filter on * @param filter filter out items that exist in this object */ -function filterExports(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { +function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) .reduce((acc: CrossRegionExports, curr: string) => { diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js deleted file mode 100644 index 9df94382cc74e..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/__entrypoint__.js +++ /dev/null @@ -1,118 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = exports.external = void 0; -const https = require("https"); -const url = require("url"); -// for unit tests -exports.external = { - sendHttpRequest: defaultSendHttpRequest, - log: defaultLog, - includeStackTraces: true, - userHandlerIndex: './index', -}; -const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; -const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; -async function handler(event, context) { - const sanitizedEvent = { ...event, ResponseURL: '...' }; - exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); - // ignore DELETE event when the physical resource ID is the marker that - // indicates that this DELETE is a subsequent DELETE to a failed CREATE - // operation. - if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { - exports.external.log('ignoring DELETE event caused by a failed CREATE event'); - await submitResponse('SUCCESS', event); - return; - } - try { - // invoke the user handler. this is intentionally inside the try-catch to - // ensure that if there is an error it's reported as a failure to - // cloudformation (otherwise cfn waits). - // eslint-disable-next-line @typescript-eslint/no-require-imports - const userHandler = require(exports.external.userHandlerIndex).handler; - const result = await userHandler(sanitizedEvent, context); - // validate user response and create the combined event - const responseEvent = renderResponse(event, result); - // submit to cfn as success - await submitResponse('SUCCESS', responseEvent); - } - catch (e) { - const resp = { - ...event, - Reason: exports.external.includeStackTraces ? e.stack : e.message, - }; - if (!resp.PhysicalResourceId) { - // special case: if CREATE fails, which usually implies, we usually don't - // have a physical resource id. in this case, the subsequent DELETE - // operation does not have any meaning, and will likely fail as well. to - // address this, we use a marker so the provider framework can simply - // ignore the subsequent DELETE. - if (event.RequestType === 'Create') { - exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); - resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; - } - else { - // otherwise, if PhysicalResourceId is not specified, something is - // terribly wrong because all other events should have an ID. - exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); - } - } - // this is an actual error, fail the activity altogether and exist. - await submitResponse('FAILED', resp); - } -} -exports.handler = handler; -function renderResponse(cfnRequest, handlerResponse = {}) { - // if physical ID is not returned, we have some defaults for you based - // on the request type. - const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; - // if we are in DELETE and physical ID was changed, it's an error. - if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { - throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); - } - // merge request event and result event (result prevails). - return { - ...cfnRequest, - ...handlerResponse, - PhysicalResourceId: physicalResourceId, - }; -} -async function submitResponse(status, event) { - const json = { - Status: status, - Reason: event.Reason ?? status, - StackId: event.StackId, - RequestId: event.RequestId, - PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, - LogicalResourceId: event.LogicalResourceId, - NoEcho: event.NoEcho, - Data: event.Data, - }; - exports.external.log('submit response to cloudformation', json); - const responseBody = JSON.stringify(json); - const parsedUrl = url.parse(event.ResponseURL); - const req = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { 'content-type': '', 'content-length': responseBody.length }, - }; - await exports.external.sendHttpRequest(req, responseBody); -} -async function defaultSendHttpRequest(options, responseBody) { - return new Promise((resolve, reject) => { - try { - const request = https.request(options, _ => resolve()); - request.on('error', reject); - request.write(responseBody); - request.end(); - } - catch (e) { - reject(e); - } - }); -} -function defaultLog(fmt, ...params) { - // eslint-disable-next-line no-console - console.log(fmt, ...params); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFN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QzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js deleted file mode 100644 index a487b651f8d38..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc/index.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties; - const exports = props.Exports; - const ssm = new aws_sdk_1.SSM({ region: props.Region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties; - const oldExports = oldProps.Exports; - const newExports = filterExports(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.Region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm, parameters) { - const tagResults = new Map(); - await Promise.all(Object.keys(parameters).map(async (name) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'cdk-strong-ref') { - tagResults.has(name) - ? tagResults.get(name).add(tagParts[1]) - : tagResults.set(name, new Set([tagParts[1]])); - } - }); - } - catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - })); - if (tagResults.size > 0) { - const message = Object.entries(tagResults) - .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function filterExports(source, filter) { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztJQUN2QyxNQUFNLE9BQU8sR0FBdUIsS0FBSyxDQUFDLE9BQU8sQ0FBQztJQUVsRCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNwQyxNQUFNLGFBQWEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ2xDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDO2dCQUM3QyxNQUFNLFVBQVUsR0FBdUIsUUFBUSxDQUFDLE9BQU8sQ0FBQztnQkFDeEQsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdEQsTUFBTSxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUN2QyxPQUFPLENBQUMsSUFBSSxDQUFDLGdEQUFnRCxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztnQkFDN0UsTUFBTSxhQUFhLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUNyQyxPQUFPO1lBQ1QsS0FBSyxRQUFR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SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztTQUN2QixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7U0FDM0UsTUFBTSxDQUFDLENBQUMsR0FBdUIsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUNoRCxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ1gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSovXG4vKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IFNTTSB9IGZyb20gJ2F3cy1zZGsnO1xudHlwZSBDcm9zc1JlZ2lvbkV4cG9ydHMgPSB7IFtleHBvcnROYW1lOiBzdHJpbmddOiBzdHJpbmcgfTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXM7XG4gIGNvbnN0IGV4cG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMuUmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzOiBDcm9zc1JlZ2lvbkV4cG9ydHMgPSBvbGRQcm9wcy5FeHBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZmlsdGVyRXhwb3J0cyhleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5SZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdjZGstc3Ryb25nLXJlZicpIHtcbiAgICAgICAgICB0YWdSZXN1bHRzLmhhcyhuYW1lKVxuICAgICAgICAgICAgPyB0YWdSZXN1bHRzLmdldChuYW1lKSEuYWRkKHRhZ1BhcnRzWzFdKVxuICAgICAgICAgICAgOiB0YWdSZXN1bHRzLnNldChuYW1lLCBuZXcgU2V0KFt0YWdQYXJ0c1sxXV0pKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBhbiBJbnZhbGlkUmVzb3VyY2VJZCBtZWFucyB0aGF0IHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdFxuICAgICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgICAgaWYgKGUuY29kZSA9PT0gJ0ludmFsaWRSZXNvdXJjZUlkJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aHJvdyBlO1xuICAgIH1cblxuICB9KSk7XG5cbiAgaWYgKHRhZ1Jlc3VsdHMuc2l6ZSA+IDApIHtcbiAgICBjb25zdCBtZXNzYWdlOiBzdHJpbmcgPSBPYmplY3QuZW50cmllcyh0YWdSZXN1bHRzKVxuICAgICAgLm1hcCgocmVzdWx0OiBbc3RyaW5nLCBzdHJpbmdbXV0pID0+IGAke3Jlc3VsdFswXX0gaXMgaW4gdXNlIGJ5IHN0YWNrKHMpICR7cmVzdWx0WzFdLmpvaW4oJyAnKX1gKVxuICAgICAgLmpvaW4oJ1xcbicpO1xuICAgIHRocm93IG5ldyBFcnJvcihgRXhwb3J0cyBjYW5ub3QgYmUgdXBkYXRlZDogXFxuJHttZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZmlsdGVyRXhwb3J0cyhzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json index 009fba5ba2707..00b55c859608c 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json @@ -1,35 +1,35 @@ { "version": "21.0.0", "files": { - "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { + "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d": { "source": { - "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", + "path": "asset.bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d", "packaging": "zip" }, "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", + "objectKey": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915": { + "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878": { "source": { - "path": "asset.abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915", + "path": "asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878", "packaging": "zip" }, "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip", + "objectKey": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "a5134211444edbb4f9d3853d1801a105e752559035941a285e5b2ae3e30d8152": { + "2c37e7d81834902acece506443fca36aa859d1f164da38b0d0bfb11c94646293": { "source": { "path": "integ-pipeline-consumer-stack.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "a5134211444edbb4f9d3853d1801a105e752559035941a285e5b2ae3e30d8152.json", + "objectKey": "2c37e7d81834902acece506443fca36aa859d1f164da38b0d0bfb11c94646293.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json index d3972490fd4a9..d0539cf10a779 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json @@ -628,7 +628,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" + "S3Key": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip" }, "Timeout": 900, "MemorySize": 128, @@ -845,12 +845,14 @@ "Arn" ] }, - "Region": "us-east-2", - "StackName": "integ-pipeline-consumer-stack", - "Imports": [ - "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D", - "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73" - ] + "ReaderProps": { + "region": "us-east-2", + "prefix": "integ-pipeline-consumer-stack", + "imports": [ + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D", + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73" + ] + } }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -916,7 +918,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "S3Key": "abb99ab2b779b809c120ce2531aa4570108d2c73e461b9e97966d6804c58d915.zip" + "S3Key": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json index 627286dc43199..28aaa4fb4a65a 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json @@ -1,35 +1,35 @@ { "version": "21.0.0", "files": { - "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { + "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d": { "source": { - "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", + "path": "asset.bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", + "objectKey": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc": { + "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03": { "source": { - "path": "asset.a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc", + "path": "asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip", + "objectKey": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "abc19ca62a7325852844f25fd3eacf14242467233199d0107a8932113f751377": { + "c55f1e01cd2d11bf3e4c7117dbdd63890fbfe7bd129f38d29f96d88504a6c061": { "source": { "path": "integ-pipeline-producer-stack.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "abc19ca62a7325852844f25fd3eacf14242467233199d0107a8932113f751377.json", + "objectKey": "c55f1e01cd2d11bf3e4c7117dbdd63890fbfe7bd129f38d29f96d88504a6c061.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json index f2cf8c6e062fd..02b8c9b552fdb 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json @@ -158,7 +158,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" + "S3Key": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip" }, "Timeout": 900, "MemorySize": 128, @@ -196,16 +196,18 @@ "Arn" ] }, - "Region": "us-east-2", - "Exports": { - "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D": { - "Ref": "ReplicationBucket70D68737" - }, - "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73": { - "Fn::GetAtt": [ - "ReplicationKeyFCE40BF4", - "Arn" - ] + "WriterProps": { + "region": "us-east-2", + "exports": { + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D": { + "Ref": "ReplicationBucket70D68737" + }, + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73": { + "Fn::GetAtt": [ + "ReplicationKeyFCE40BF4", + "Arn" + ] + } } } }, @@ -271,7 +273,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "a44438f0402397af9022e6f4259fb57bbb4bd859db72879f14f40981be45fadc.zip" + "S3Key": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json index 1558b6bd2e586..321b2ea545264 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/abc19ca62a7325852844f25fd3eacf14242467233199d0107a8932113f751377.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/c55f1e01cd2d11bf3e4c7117dbdd63890fbfe7bd129f38d29f96d88504a6c061.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -118,7 +118,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/a5134211444edbb4f9d3853d1801a105e752559035941a285e5b2ae3e30d8152.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/2c37e7d81834902acece506443fca36aa859d1f164da38b0d0bfb11c94646293.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index f78bc0ce4d2a9..159b52705d6cf 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -207,7 +207,8 @@ In order to mimic strong references, a Custom Resource is also created in the co stack which marks the SSM parameters as being "imported". When a parameter has been successfully imported, the producing stack cannot update the value. -See the [adr](./adr/cross-region-stack-references.md) for more details on this feature. +See the [adr](https://github.com/aws/aws-cdk/blob/main/packages/@aws-cdk/core/adr/cross-region-stack-references) +for more details on this feature. ### Removing automatic cross-stack references diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 6f5eb7ba8fcaf..385719aab6b12 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -195,19 +195,29 @@ other. ## Accessing resources in a different stack and region -You can enable the feature flag `@aws-cdk/core:enableCrossRegionReferencesUsingCustomResources` +You can enable the Stack property `optInToCrossRegionReferences` in order to access resources in a different stack _and_ region. With this feature flag enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and an ACM certificate in `us-east-1`. ```ts -const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' } }); +const stack1 = new Stack(app, 'Stack1', { + env: { + region: 'us-east-1', + }, + optInToCrossRegionReferences: true, +}); const cert = new acm.Certificate(stack1, 'Cert', { domainName: '*.example.com', validation: acm.CertificateValidation.fromDns(route53.PublicHostedZone.fromHostedZoneId(stack1, 'Zone', 'Z0329774B51CGXTDQV3X')), }); -const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' } }); +const stack2 = new Stack(app, 'Stack2', { + env: { + region: 'us-east-2', + }, + optInToCrossRegionReferences: true, +}); new cloudfront.Distribution(stack2, 'Distribution', { defaultBehavior: { origin: new origins.HttpOrigin('example.com'), @@ -228,6 +238,9 @@ In order to mimic strong references, a Custom Resource is also created in the co stack which marks the SSM parameters as being "imported". When a parameter has been successfully imported, the producing stack cannot update the value. +See the [adr](https://github.com/aws/aws-cdk/blob/main/packages/@aws-cdk/core/adr/cross-region-stack-references) +for more details on this feature. + ### Removing automatic cross-stack references The automatic references created by CDK when you use resources across stacks From 01f336619627c990be74d20f2717084c8dd66457 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 20 Oct 2022 19:32:58 +0000 Subject: [PATCH 27/30] updating based on review comments --- .../index.d.ts | 1 - .../index.js | 102 --- .../index.ts | 103 --- .../index.js | 768 ++++++++++++++++++ .../__entrypoint__.js | 0 .../index.js | 148 ++++ .../cross-region-consumer.assets.json | 14 +- .../cross-region-consumer.template.json | 47 +- .../cross-region-producer.assets.json | 10 +- .../cross-region-producer.template.json | 3 +- ...erIntegNested815BEF8A.nested.template.json | 16 +- ...efaultTestDeployAssertAB7415FD.assets.json | 17 +- ...aultTestDeployAssertAB7415FD.template.json | 368 +++++++++ .../manifest.json | 96 ++- .../integ.core-cross-region-references.ts | 47 +- .../index.d.ts | 1 - .../index.js | 102 --- .../index.ts | 103 --- .../__entrypoint__.js | 0 .../index.js | 100 +++ .../index.d.ts | 1 - .../index.js | 124 --- .../index.ts | 124 --- .../__entrypoint__.js | 0 .../index.js | 148 ++++ .../integ-acm-stack.assets.json | 10 +- .../integ-acm-stack.template.json | 3 +- .../integ-cloudfront-stack.assets.json | 10 +- .../integ-cloudfront-stack.template.json | 17 +- .../manifest.json | 4 +- .../index.d.ts | 1 - .../index.js | 102 --- .../index.ts | 103 --- .../__entrypoint__.js | 0 .../index.js | 148 ++++ .../integ-pipeline-consumer-stack.assets.json | 16 +- ...nteg-pipeline-consumer-stack.template.json | 28 +- .../integ-pipeline-producer-stack.assets.json | 16 +- ...nteg-pipeline-producer-stack.template.json | 5 +- .../manifest.json | 4 +- .../cross-region-ssm-reader-handler/index.ts | 61 +- .../cross-region-ssm-writer-handler/index.ts | 96 ++- .../export-reader-provider.ts | 16 +- .../export-writer-provider.ts | 18 +- .../cross-region-export-providers/types.ts | 2 +- .../custom-resource-provider.ts | 4 +- packages/@aws-cdk/core/lib/private/refs.ts | 6 +- .../core/test/cross-environment-token.test.ts | 7 +- .../cross-region-ssm-reader-handler.test.ts | 49 +- .../cross-region-ssm-writer-handler.test.ts | 500 +++++++----- .../export-writer-provider.test.ts | 29 +- .../@aws-cdk/core/test/nested-stack.test.ts | 13 +- packages/@aws-cdk/core/test/stack.test.ts | 61 +- .../lib/assertions/waiter-state-machine.ts | 4 + 54 files changed, 2486 insertions(+), 1290 deletions(-) delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js delete mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle/index.js rename packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/{asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03 => asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3}/__entrypoint__.js (100%) create mode 100644 packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03 => asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741}/__entrypoint__.js (100%) create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741/index.js delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.d.ts delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js delete mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.ts rename packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/{asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878 => asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3}/__entrypoint__.js (100%) create mode 100644 packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts rename packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/{asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03 => asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3}/__entrypoint__.js (100%) create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts deleted file mode 100644 index 3554dc94d4617..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js deleted file mode 100644 index f432d8df83b5e..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties.WriterProps; - const exports = props.exports; - const ssm = new aws_sdk_1.SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties.WriterProps; - const oldExports = oldProps.exports; - const newExports = except(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm, parameters) { - const tagResults = new Map(); - await Promise.all(Object.keys(parameters).map(async (name) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { - tagResults.has(name) - ? tagResults.get(name).add(tagParts[2]) - : tagResults.set(name, new Set([tagParts[2]])); - } - }); - } - catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - })); - if (tagResults.size > 0) { - const message = Object.entries(tagResults) - .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source, filter) { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sZUFBZSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFD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QUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLE1BQU0sQ0FBQyxNQUEwQixFQUFFLE1BQTBCO0lBQ3BFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7U0FDdkIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzNFLE1BQU0sQ0FBQyxDQUFDLEdBQXVCLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDaEQsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNYLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IENyb3NzUmVnaW9uRXhwb3J0cywgRXhwb3J0V3JpdGVyQ1JQcm9wcyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gIGNvbnN0IGV4cG9ydHMgPSBwcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcblxuICBjb25zdCBzc20gPSBuZXcgU1NNKHsgcmVnaW9uOiBwcm9wcy5yZWdpb24gfSk7XG4gIHRyeSB7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHMgPSBvbGRQcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGV4Y2VwdChleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgICAgdGFnUmVzdWx0cy5oYXMobmFtZSlcbiAgICAgICAgICAgID8gdGFnUmVzdWx0cy5nZXQobmFtZSkhLmFkZCh0YWdQYXJ0c1syXSlcbiAgICAgICAgICAgIDogdGFnUmVzdWx0cy5zZXQobmFtZSwgbmV3IFNldChbdGFnUGFydHNbMl1dKSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gYW4gSW52YWxpZFJlc291cmNlSWQgbWVhbnMgdGhhdCB0aGUgcGFyYW1ldGVyIGRvZXNuJ3QgZXhpc3RcbiAgICAgIC8vIHdoaWNoIHdlIHNob3VsZCBpZ25vcmUgc2luY2UgdGhhdCBtZWFucyBpdCdzIG5vdCBpbiB1c2VcbiAgICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhyb3cgZTtcbiAgICB9XG5cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiBvbmx5IHRoZSBpdGVtcyBmcm9tIHNvdXJjZSB0aGF0IGRvIG5vdCBleGlzdCBpbiB0aGUgZmlsdGVyXG4gKlxuICogQHBhcmFtIHNvdXJjZSB0aGUgc291cmNlIG9iamVjdCB0byBwZXJmb3JtIHRoZSBmaWx0ZXIgb25cbiAqIEBwYXJhbSBmaWx0ZXIgZmlsdGVyIG91dCBpdGVtcyB0aGF0IGV4aXN0IGluIHRoaXMgb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGV4Y2VwdChzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts deleted file mode 100644 index 8970f2b163664..0000000000000 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -import { SSM } from 'aws-sdk'; -import { CrossRegionExports, ExportWriterCRProps } from '../types'; - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props: ExportWriterCRProps = event.ResourceProperties.WriterProps; - const exports = props.exports as CrossRegionExports; - - const ssm = new SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; - const oldExports = oldProps.exports as CrossRegionExports; - const newExports = except(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } catch (e) { - console.error('Error processing event: ', e); - throw e; - } -}; - -/** - * Create parameters for existing exports - */ -async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} - -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { - const tagResults: Map> = new Map(); - await Promise.all(Object.keys(parameters).map(async (name: string) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { - tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[2]) - : tagResults.set(name, new Set([tagParts[2]])); - } - }); - - } catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - - })); - - if (tagResults.size > 0) { - const message: string = Object.entries(tagResults) - .map((result: [string, string[]]) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} - -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc: CrossRegionExports, curr: string) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle/index.js new file mode 100644 index 0000000000000..2d6c2f0e85497 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle/index.js @@ -0,0 +1,768 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler, + isComplete: () => isComplete, + onTimeout: () => onTimeout +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// ../assertions/lib/matcher.ts +var Matcher = class { + static isMatcher(x) { + return x && x instanceof Matcher; + } +}; +var MatchResult = class { + constructor(target) { + this.failures = []; + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.target = target; + } + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + recordFailure(failure) { + this.failures.push(failure); + return this; + } + hasFailed() { + return this.failures.length !== 0; + } + get failCount() { + return this.failures.length; + } + compose(id, inner) { + const innerF = inner.failures; + this.failures.push(...innerF.map((f) => { + return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; + })); + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + toHumanStrings() { + return this.failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + } + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } +}; + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } +}; + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} + +// ../assertions/lib/match.ts +var Match = class { + static absent() { + return new AbsentMatch("absent"); + } + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + static not(pattern) { + return new NotMatch("not", pattern); + } + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + static anyValue() { + return new AnyMatch("anyValue"); + } + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } +}; +var LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } +}; +var ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + if (!this.subsequence && this.pattern.length !== actual.length) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected array of length ${this.pattern.length} but received ${actual.length}` + }); + } + let patternIdx = 0; + let actualIdx = 0; + const result = new MatchResult(actual); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + if (!this.subsequence || !innerResult.hasFailed()) { + result.compose(`[${actualIdx}]`, innerResult); + patternIdx++; + actualIdx++; + } else { + actualIdx++; + } + } + for (; patternIdx < this.pattern.length; patternIdx++) { + const pattern = this.pattern[patternIdx]; + const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; + result.recordFailure({ + matcher: this, + path: [], + message: `Missing element${element}at pattern index ${patternIdx}` + }); + } + return result; + } +}; +var ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [`/${a}`], + message: "Unexpected key" + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [`/${patternKey}`], + message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(`/${patternKey}`, inner); + } + return result; + } +}; +var SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + if (getType(actual) !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + return result; + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + result.recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + return result; + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + result.compose(`(${this.name})`, innerResult); + return result; + } +}; +var NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } +}; +var AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } +}; +var StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } +}; + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var AWS = __toESM(require("aws-sdk")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + async handle() { + try { + if ("stateMachineArn" in this.event.ResourceProperties) { + const req = { + stateMachineArn: this.event.ResourceProperties.stateMachineArn, + name: this.event.RequestId, + input: JSON.stringify(this.event) + }; + await this.startExecution(req); + return; + } else { + const response = await this.processEvent(this.event.ResourceProperties); + return response; + } + } catch (e) { + console.log(e); + throw e; + } finally { + clearTimeout(this.timeout); + } + } + async handleIsComplete() { + try { + const result = await this.processEvent(this.event.ResourceProperties); + return result; + } catch (e) { + console.log(e); + return; + } finally { + clearTimeout(this.timeout); + } + } + async startExecution(req) { + try { + const sfn = new AWS.StepFunctions(); + await sfn.startExecution(req).promise(); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } finally { + clearTimeout(this.timeout); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = decodeCall(request2.actual); + const expected = decodeCall(request2.expected); + let result; + const matcher = new MatchCreator(expected).getMatcher(); + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + failed: true, + assertion: JSON.stringify({ + status: "fail", + message: [ + ...matchResult.toHumanStrings(), + JSON.stringify(matchResult.target, void 0, 2) + ].join("\n") + }) + }; + if (request2.failDeployment) { + throw new Error(result.assertion); + } + } else { + result = { + assertion: JSON.stringify({ + status: "success" + }) + }; + } + return result; + } +}; +var MatchCreator = class { + constructor(obj) { + this.parsedObj = { + matcher: obj + }; + } + getMatcher() { + try { + const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { + const nested = Object.keys(v)[0]; + switch (nested) { + case "$ArrayWith": + return Match.arrayWith(v[nested]); + case "$ObjectLike": + return Match.objectLike(v[nested]); + case "$StringLike": + return Match.stringLikeRegexp(v[nested]); + default: + return v; + } + }); + if (Matcher.isMatcher(final.matcher)) { + return final.matcher; + } + return Match.exact(final.matcher); + } catch { + return Match.exact(this.parsedObj.matcher); + } + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + try { + const parsed = JSON.parse(call); + return parsed; + } catch (e) { + return call; + } +} + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign( + {}, + ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + let childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + if (typeof childKey === "string") { + childKey = isJsonString(childKey); + } + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object) + ); +} +var AwsApiCallHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS2 = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS2.VERSION}`); + if (!Object.prototype.hasOwnProperty.call(AWS2, request2.service)) { + throw Error(`Service ${request2.service} does not exist in AWS SDK version ${AWS2.VERSION}.`); + } + const service = new AWS2[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = { + ...flatten(respond) + }; + const resp = request2.flattenResponse === "true" ? flatData : respond; + console.log(`Returning result ${JSON.stringify(resp)}`); + return resp; + } +}; +function isJsonString(value) { + try { + return JSON.parse(value); + } catch { + return value; + } +} + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + if (event.RequestType === "Delete") { + await provider.respond({ + status: "SUCCESS", + reason: "OK" + }); + return; + } + const result = await provider.handle(); + if ("stateMachineArn" in event.ResourceProperties) { + console.info('Found "stateMachineArn", waiter statemachine started'); + return; + } else if ("expected" in event.ResourceProperties) { + console.info('Found "expected", testing assertions'); + const actualPath = event.ResourceProperties.actualPath; + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + try { + const assertionResult = await assertion.handle(); + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: { + ...assertionResult, + ...result + } + }); + return; + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } catch (e) { + await provider.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + return; + } + return; +} +async function onTimeout(timeoutEvent) { + const isCompleteRequest = JSON.parse(JSON.parse(timeoutEvent.Cause).errorMessage); + const provider = createResourceHandler(isCompleteRequest, standardContext); + await provider.respond({ + status: "FAILED", + reason: "Operation timed out: " + JSON.stringify(isCompleteRequest) + }); +} +async function isComplete(event, context) { + console.log(`Event: ${JSON.stringify({ ...event, ResponseURL: "..." })}`); + const provider = createResourceHandler(event, context); + try { + const result = await provider.handleIsComplete(); + const actualPath = event.ResourceProperties.actualPath; + if (result) { + const actual = actualPath ? result[`apiCallResponse.${actualPath}`] : result.apiCallResponse; + if ("expected" in event.ResourceProperties) { + const assertion = new AssertionHandler({ + ...event, + ResourceProperties: { + ServiceToken: event.ServiceToken, + actual, + expected: event.ResourceProperties.expected + } + }, context); + const assertionResult = await assertion.handleIsComplete(); + if (!(assertionResult == null ? void 0 : assertionResult.failed)) { + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: { + ...assertionResult, + ...result + } + }); + return; + } else { + console.log(`Assertion Failed: ${JSON.stringify(assertionResult)}`); + throw new Error(JSON.stringify(event)); + } + } + await provider.respond({ + status: "SUCCESS", + reason: "OK", + data: result + }); + } else { + console.log("No result"); + throw new Error(JSON.stringify(event)); + } + return; + } catch (e) { + console.log(e); + throw new Error(JSON.stringify(event)); + } +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new AwsApiCallHandler(event, context); + } else if (event.ResourceType.startsWith(ASSERT_RESOURCE_TYPE)) { + return new AssertionHandler(event, context); + } else { + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +var standardContext = { + getRemainingTimeInMillis: () => 9e4 +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler, + isComplete, + onTimeout +}); diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js rename to packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js new file mode 100644 index 0000000000000..9f71f540e4994 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js @@ -0,0 +1,148 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.WriterProps; + const exports = props.exports; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports; + const newExports = except(exports, oldExports); + // throw an error to fail the deployment if any export value is changing + const changedExports = changed(oldExports, exports); + if (changedExports.length > 0) { + throw new Error('Some exports have changed!\n' + changedExports.join('\n')); + } + // if we are removing any exports that are in use, then throw an + // error to fail the deployment + const removedExports = except(oldExports, exports); + await throwIfAnyInUse(ssm, removedExports); + // if the ones we are removing are not in use then delete them + await ssm.deleteParameters({ + Names: Object.keys(removedExports), + }).promise(); + // also throw an error if we are creating a new export that already exists for some reason + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // if any of the exports are currently in use then throw an error to fail + // the stack deletion. + await throwIfAnyInUse(ssm, exports); + // if none are in use then delete all of them + await ssm.deleteParameters({ + Names: Object.keys(exports), + }).promise(); + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + const result = await isInUse(ssm, name); + if (result.size > 0) { + tagResults.set(name, result); + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Check if a parameter is in use + */ +async function isInUse(ssm, parameterName) { + const tagResults = new Set(); + try { + const result = await ssm.listTagsForResource({ + ResourceId: parameterName, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { + tagResults.add(tagParts[2]); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return new Set(); + } + throw e; + } + return tagResults; +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + * @returns any exports that don't exist in the filter + */ +function except(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key))) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +/** + * Return items that exist in both the the old parameters and the new parameters, + * but have different values + * + * @param oldParams the exports that existed previous to this execution + * @param newParams the exports for the current execution + * @returns any parameters that have different values + */ +function changed(oldParams, newParams) { + return Object.keys(oldParams) + .filter(key => (newParams.hasOwnProperty(key) && oldParams[key] !== newParams[key])) + .reduce((acc, curr) => { + acc.push(curr); + return acc; + }, []); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBRS9DLHdFQUF3RTtnQkFDeEUsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDcEQsSUFBSSxjQUFjLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDN0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsR0FBRSxjQUFjLENBQUMs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Q0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztTQUM1QyxNQUFNLENBQUMsQ0FBQyxHQUF1QixFQUFFLElBQVksRUFBRSxFQUFFO1FBQ2hELEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDWCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsT0FBTyxDQUFDLFNBQTZCLEVBQUUsU0FBNkI7SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQztTQUMxQixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQ25GLE1BQU0sQ0FBQyxDQUFDLEdBQWEsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUN0QyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2YsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDWCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyplc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgeyBDcm9zc1JlZ2lvbkV4cG9ydHMsIEV4cG9ydFdyaXRlckNSUHJvcHMgfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGNvbnN0IHByb3BzOiBFeHBvcnRXcml0ZXJDUlByb3BzID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLldyaXRlclByb3BzO1xuICBjb25zdCBleHBvcnRzID0gcHJvcHMuZXhwb3J0cyBhcyBDcm9zc1JlZ2lvbkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMucmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzOiBFeHBvcnRXcml0ZXJDUlByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzLldyaXRlclByb3BzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzID0gb2xkUHJvcHMuZXhwb3J0cyBhcyBDcm9zc1JlZ2lvbkV4cG9ydHM7XG4gICAgICAgIGNvbnN0IG5ld0V4cG9ydHMgPSBleGNlcHQoZXhwb3J0cywgb2xkRXhwb3J0cyk7XG5cbiAgICAgICAgLy8gdGhyb3cgYW4gZXJyb3IgdG8gZmFpbCB0aGUgZGVwbG95bWVudCBpZiBhbnkgZXhwb3J0IHZhbHVlIGlzIGNoYW5naW5nXG4gICAgICAgIGNvbnN0IGNoYW5nZWRFeHBvcnRzID0gY2hhbmdlZChvbGRFeHBvcnRzLCBleHBvcnRzKTtcbiAgICAgICAgaWYgKGNoYW5nZWRFeHBvcnRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NvbWUgZXhwb3J0cyBoYXZlIGNoYW5nZWQhXFxuJysgY2hhbmdlZEV4cG9ydHMuam9pbignXFxuJykpO1xuICAgICAgICB9XG4gICAgICAgIC8vIGlmIHdlIGFyZSByZW1vdmluZyBhbnkgZXhwb3J0cyB0aGF0IGFyZSBpbiB1c2UsIHRoZW4gdGhyb3cgYW5cbiAgICAgICAgLy8gZXJyb3IgdG8gZmFpbCB0aGUgZGVwbG95bWVudFxuICAgICAgICBjb25zdCByZW1vdmVkRXhwb3J0cyA9IGV4Y2VwdChvbGRFeHBvcnRzLCBleHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgcmVtb3ZlZEV4cG9ydHMpO1xuICAgICAgICAvLyBpZiB0aGUgb25lcyB3ZSBhcmUgcmVtb3ZpbmcgYXJlIG5vdCBpbiB1c2UgdGhlbiBkZWxldGUgdGhlbVxuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IE9iamVjdC5rZXlzKHJlbW92ZWRFeHBvcnRzKSxcbiAgICAgICAgfSkucHJvbWlzZSgpO1xuXG4gICAgICAgIC8vIGFsc28gdGhyb3cgYW4gZXJyb3IgaWYgd2UgYXJlIGNyZWF0aW5nIGEgbmV3IGV4cG9ydCB0aGF0IGFscmVhZHkgZXhpc3RzIGZvciBzb21lIHJlYXNvblxuICAgICAgICBhd2FpdCB0aHJvd0lmQW55SW5Vc2Uoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgcHV0UGFyYW1ldGVycyhzc20sIG5ld0V4cG9ydHMpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdEZWxldGUnOlxuICAgICAgICAvLyBpZiBhbnkgb2YgdGhlIGV4cG9ydHMgYXJlIGN1cnJlbnRseSBpbiB1c2UgdGhlbiB0aHJvdyBhbiBlcnJvciB0byBmYWlsXG4gICAgICAgIC8vIHRoZSBzdGFjayBkZWxldGlvbi5cbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIC8vIGlmIG5vbmUgYXJlIGluIHVzZSB0aGVuIGRlbGV0ZSBhbGwgb2YgdGhlbVxuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IE9iamVjdC5rZXlzKGV4cG9ydHMpLFxuICAgICAgICB9KS5wcm9taXNlKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmVycm9yKCdFcnJvciBwcm9jZXNzaW5nIGV2ZW50OiAnLCBlKTtcbiAgICB0aHJvdyBlO1xuICB9XG59O1xuXG4vKipcbiAqIENyZWF0ZSBwYXJhbWV0ZXJzIGZvciBleGlzdGluZyBleHBvcnRzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHB1dFBhcmFtZXRlcnMoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChBcnJheS5mcm9tKE9iamVjdC5lbnRyaWVzKHBhcmFtZXRlcnMpLCAoW25hbWUsIHZhbHVlXSkgPT4ge1xuICAgIHJldHVybiBzc20ucHV0UGFyYW1ldGVyKHtcbiAgICAgIE5hbWU6IG5hbWUsXG4gICAgICBWYWx1ZTogdmFsdWUsXG4gICAgICBUeXBlOiAnU3RyaW5nJyxcbiAgICB9KS5wcm9taXNlKCk7XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBRdWVyeSBmb3IgZXhpc3RpbmcgcGFyYW1ldGVycyB0aGF0IGFyZSBpbiB1c2VcbiAqL1xuYXN5bmMgZnVuY3Rpb24gdGhyb3dJZkFueUluVXNlKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgdGFnUmVzdWx0czogTWFwPHN0cmluZywgU2V0PHN0cmluZz4+ID0gbmV3IE1hcCgpO1xuICBhd2FpdCBQcm9taXNlLmFsbChPYmplY3Qua2V5cyhwYXJhbWV0ZXJzKS5tYXAoYXN5bmMgKG5hbWU6IHN0cmluZykgPT4ge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGlzSW5Vc2Uoc3NtLCBuYW1lKTtcbiAgICBpZiAocmVzdWx0LnNpemUgPiAwKSB7XG4gICAgICB0YWdSZXN1bHRzLnNldChuYW1lLCByZXN1bHQpO1xuICAgIH1cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIENoZWNrIGlmIGEgcGFyYW1ldGVyIGlzIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiBpc0luVXNlKHNzbTogU1NNLCBwYXJhbWV0ZXJOYW1lOiBzdHJpbmcpOiBQcm9taXNlPFNldDxzdHJpbmc+PiB7XG4gIGNvbnN0IHRhZ1Jlc3VsdHM6IFNldDxzdHJpbmc+ID0gbmV3IFNldCgpO1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgIFJlc291cmNlSWQ6IHBhcmFtZXRlck5hbWUsXG4gICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgIH0pLnByb21pc2UoKTtcbiAgICByZXN1bHQuVGFnTGlzdD8uZm9yRWFjaCh0YWcgPT4ge1xuICAgICAgY29uc3QgdGFnUGFydHMgPSB0YWcuS2V5LnNwbGl0KCc6Jyk7XG4gICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgIHRhZ1Jlc3VsdHMuYWRkKHRhZ1BhcnRzWzJdKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIGFuIEludmFsaWRSZXNvdXJjZUlkIG1lYW5zIHRoYXQgdGhlIHBhcmFtZXRlciBkb2Vzbid0IGV4aXN0XG4gICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgIHJldHVybiBuZXcgU2V0KCk7XG4gICAgfVxuICAgIHRocm93IGU7XG4gIH1cbiAgcmV0dXJuIHRhZ1Jlc3VsdHM7XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqIEByZXR1cm5zIGFueSBleHBvcnRzIHRoYXQgZG9uJ3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICovXG5mdW5jdGlvbiBleGNlcHQoc291cmNlOiBDcm9zc1JlZ2lvbkV4cG9ydHMsIGZpbHRlcjogQ3Jvc3NSZWdpb25FeHBvcnRzKTogQ3Jvc3NSZWdpb25FeHBvcnRzIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKHNvdXJjZSlcbiAgICAuZmlsdGVyKGtleSA9PiAoIWZpbHRlci5oYXNPd25Qcm9wZXJ0eShrZXkpKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cblxuLyoqXG4gKiBSZXR1cm4gaXRlbXMgdGhhdCBleGlzdCBpbiBib3RoIHRoZSB0aGUgb2xkIHBhcmFtZXRlcnMgYW5kIHRoZSBuZXcgcGFyYW1ldGVycyxcbiAqIGJ1dCBoYXZlIGRpZmZlcmVudCB2YWx1ZXNcbiAqXG4gKiBAcGFyYW0gb2xkUGFyYW1zIHRoZSBleHBvcnRzIHRoYXQgZXhpc3RlZCBwcmV2aW91cyB0byB0aGlzIGV4ZWN1dGlvblxuICogQHBhcmFtIG5ld1BhcmFtcyB0aGUgZXhwb3J0cyBmb3IgdGhlIGN1cnJlbnQgZXhlY3V0aW9uXG4gKiBAcmV0dXJucyBhbnkgcGFyYW1ldGVycyB0aGF0IGhhdmUgZGlmZmVyZW50IHZhbHVlc1xuICovXG5mdW5jdGlvbiBjaGFuZ2VkKG9sZFBhcmFtczogQ3Jvc3NSZWdpb25FeHBvcnRzLCBuZXdQYXJhbXM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IHN0cmluZ1tdIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKG9sZFBhcmFtcylcbiAgICAuZmlsdGVyKGtleSA9PiAobmV3UGFyYW1zLmhhc093blByb3BlcnR5KGtleSkgJiYgb2xkUGFyYW1zW2tleV0gIT09IG5ld1BhcmFtc1trZXldKSlcbiAgICAucmVkdWNlKChhY2M6IHN0cmluZ1tdLCBjdXJyOiBzdHJpbmcpID0+IHtcbiAgICAgIGFjYy5wdXNoKGN1cnIpO1xuICAgICAgcmV0dXJuIGFjYztcbiAgICB9LCBbXSk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json index 5c15519036c1a..2eb2f3a9bf7f4 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.assets.json @@ -1,21 +1,21 @@ { "version": "21.0.0", "files": { - "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878": { + "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741": { "source": { - "path": "asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878", + "path": "asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741", "packaging": "zip" }, "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip", + "objectKey": "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "8951083c42b14c02bc23ef787fdc9dd81379182eba5b19431fb87aae9824399c": { + "e8a524074c21b1828365d86b0554b5a5282843ef099edb36de4091e50a8b4ac2": { "source": { "path": "crossregionconsumerIntegNested815BEF8A.nested.template.json", "packaging": "file" @@ -23,13 +23,13 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "8951083c42b14c02bc23ef787fdc9dd81379182eba5b19431fb87aae9824399c.json", + "objectKey": "e8a524074c21b1828365d86b0554b5a5282843ef099edb36de4091e50a8b4ac2.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "e1c8d2a77a2765a4e4cfdf07208097c34980e050bb50fe5d07702ff157e29adc": { + "33212ba7662e584fce97d4b64b2b7d157f5f1bac2b2ffe4e25e18545b514ec8b": { "source": { "path": "cross-region-consumer.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "e1c8d2a77a2765a4e4cfdf07208097c34980e050bb50fe5d07702ff157e29adc.json", + "objectKey": "33212ba7662e584fce97d4b64b2b7d157f5f1bac2b2ffe4e25e18545b514ec8b.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json index 011f4851d3e41..b441ed812b9b7 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-consumer.template.json @@ -15,9 +15,23 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "/8951083c42b14c02bc23ef787fdc9dd81379182eba5b19431fb87aae9824399c.json" + "/e8a524074c21b1828365d86b0554b5a5282843ef099edb36de4091e50a8b4ac2.json" ] ] + }, + "Parameters": { + "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B": { + "Fn::GetAtt": [ + "ExportsReader8B249524", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B" + ] + }, + "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E": { + "Fn::GetAtt": [ + "ExportsReader8B249524", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" + ] + } } }, "UpdateReplacePolicy": "Delete", @@ -27,7 +41,12 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B}}", + "Value": { + "Fn::GetAtt": [ + "ExportsReader8B249524", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B" + ] + }, "Name": "integ-parameter0" } }, @@ -35,7 +54,12 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E}}", + "Value": { + "Fn::GetAtt": [ + "ExportsReader8B249524", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" + ] + }, "Name": "integ-parameter1" } }, @@ -51,17 +75,12 @@ "ReaderProps": { "region": "us-east-2", "prefix": "cross-region-consumer", - "imports": [ - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E", - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B", - "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" - ] + "imports": { + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B}}", + "/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E}}" + } } }, - "DependsOn": [ - "IntegNestedNestedStackIntegNestedNestedStackResource168C5881" - ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, @@ -106,10 +125,8 @@ ] }, "Action": [ - "ssm:DeleteParameters", "ssm:AddTagsToResource", "ssm:RemoveTagsFromResource", - "ssm:GetParametersByPath", "ssm:GetParameters" ] } @@ -126,7 +143,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "S3Key": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip" + "S3Key": "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json index 458a24567cf12..a98a88fd5f9f8 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.assets.json @@ -1,15 +1,15 @@ { "version": "21.0.0", "files": { - "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03": { + "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3": { "source": { - "path": "asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03", + "path": "asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip", + "objectKey": "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } @@ -29,7 +29,7 @@ } } }, - "641a63e906e5a90f7362126c1648579dec5c836db6e43276dadbf3da7627b08c": { + "70e0b1af0ea278adf024d80a32c2797554d527123f1dc1eb9f2a894a6d46bc7a": { "source": { "path": "cross-region-producer.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "641a63e906e5a90f7362126c1648579dec5c836db6e43276dadbf3da7627b08c.json", + "objectKey": "70e0b1af0ea278adf024d80a32c2797554d527123f1dc1eb9f2a894a6d46bc7a.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json index c0193c9cf9d32..f0b15d4ab2e9e 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/cross-region-producer.template.json @@ -99,6 +99,7 @@ ] }, "Action": [ + "ssm:DeleteParameters", "ssm:ListTagsForResource", "ssm:GetParameters", "ssm:PutParameter" @@ -117,7 +118,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip" + "S3Key": "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json index 0086d1fcae47f..fea781033a45c 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionconsumerIntegNested815BEF8A.nested.template.json @@ -4,7 +4,9 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B}}", + "Value": { + "Ref": "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B" + }, "Name": "integ-nested-parameter0" } }, @@ -12,9 +14,19 @@ "Type": "AWS::SSM::Parameter", "Properties": { "Type": "String", - "Value": "{{resolve:ssm:/cdk/exports/cross-region-consumer/crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E}}", + "Value": { + "Ref": "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" + }, "Name": "integ-nested-parameter1" } } + }, + "Parameters": { + "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B": { + "Type": "String" + }, + "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E": { + "Type": "String" + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json index 5a6db16e6a7ef..3d31b494852c6 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.assets.json @@ -1,7 +1,20 @@ { "version": "21.0.0", "files": { - "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b": { + "source": { + "path": "asset.b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.bundle", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "e62b4ad819f8f21c0c8707091f053b8b322398afc1a04fd089b1be1436fb011a": { "source": { "path": "crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json", "packaging": "file" @@ -9,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "objectKey": "e62b4ad819f8f21c0c8707091f053b8b322398afc1a04fd089b1be1436fb011a.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json index ad9d0fb73d1dd..cb2253ab681b4 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/crossregionreferencesDefaultTestDeployAssertAB7415FD.template.json @@ -1,4 +1,372 @@ { + "Resources": { + "AwsApiCallCloudFormationdeleteStack": { + "Type": "Custom::DeployAssert@SdkCallCloudFormationdeleteStack", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "CloudFormation", + "api": "deleteStack", + "parameters": { + "StackName": "cross-region-producer" + }, + "flattenResponse": "false", + "salt": "1666292907086" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "cloudformation:DeleteStack" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "cloudformation:DescribeStacks" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "states:StartExecution" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + } + ] + } + } + ] + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip" + }, + "Timeout": 120, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + }, + "AwsApiCallCloudFormationdescribeStacks": { + "Type": "Custom::DeployAssert@SdkCallCloudFormationdescribeStacks", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "CloudFormation", + "api": "describeStacks", + "expected": "{\"$ObjectLike\":{\"Stacks\":{\"$ArrayWith\":[{\"$ObjectLike\":{\"StackName\":\"cross-region-producer\",\"StackStatus\":\"DELETE_FAILED\"}}]}}}", + "stateMachineArn": { + "Ref": "AwsApiCallCloudFormationdescribeStacksWaitFor1D722558" + }, + "parameters": { + "StackName": "cross-region-producer" + }, + "flattenResponse": "false", + "salt": "1666292907087" + }, + "DependsOn": [ + "AwsApiCallCloudFormationdeleteStack" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "AwsApiCallCloudFormationdescribeStacksWaitForIsCompleteProviderInvokeD8EB59C7": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE", + "Arn" + ] + }, + "Principal": { + "Fn::GetAtt": [ + "AwsApiCallCloudFormationdescribeStacksWaitForRoleEC9EDBA0", + "Arn" + ] + } + }, + "DependsOn": [ + "AwsApiCallCloudFormationdeleteStack" + ] + }, + "AwsApiCallCloudFormationdescribeStacksWaitForTimeoutProviderInvokeA2598EF3": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA", + "Arn" + ] + }, + "Principal": { + "Fn::GetAtt": [ + "AwsApiCallCloudFormationdescribeStacksWaitForRoleEC9EDBA0", + "Arn" + ] + } + }, + "DependsOn": [ + "AwsApiCallCloudFormationdeleteStack" + ] + }, + "AwsApiCallCloudFormationdescribeStacksWaitForRoleEC9EDBA0": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ] + }, + "Policies": [ + { + "PolicyName": "InlineInvokeFunctions", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA", + "Arn" + ] + } + ] + } + ] + } + } + ] + }, + "DependsOn": [ + "AwsApiCallCloudFormationdeleteStack" + ] + }, + "AwsApiCallCloudFormationdescribeStacksWaitFor1D722558": { + "Type": "AWS::StepFunctions::StateMachine", + "Properties": { + "DefinitionString": { + "Fn::Join": [ + "", + [ + "{\"StartAt\":\"framework-isComplete-task\",\"States\":{\"framework-isComplete-task\":{\"End\":true,\"Retry\":[{\"ErrorEquals\":[\"States.ALL\"],\"IntervalSeconds\":5,\"MaxAttempts\":360,\"BackoffRate\":1}],\"Catch\":[{\"ErrorEquals\":[\"States.ALL\"],\"Next\":\"framework-onTimeout-task\"}],\"Type\":\"Task\",\"Resource\":\"", + { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE", + "Arn" + ] + }, + "\"},\"framework-onTimeout-task\":{\"End\":true,\"Type\":\"Task\",\"Resource\":\"", + { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA", + "Arn" + ] + }, + "\"}}}" + ] + ] + }, + "RoleArn": { + "Fn::GetAtt": [ + "AwsApiCallCloudFormationdescribeStacksWaitForRoleEC9EDBA0", + "Arn" + ] + } + }, + "DependsOn": [ + "AwsApiCallCloudFormationdeleteStack", + "AwsApiCallCloudFormationdescribeStacksWaitForRoleEC9EDBA0" + ] + }, + "SingletonFunction76b3e830a873425f8453eddd85c86925Role918961BB": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "cloudformation:DescribeStacks" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + } + ] + } + } + ] + } + }, + "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip" + }, + "Timeout": 120, + "Handler": "index.isComplete", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction76b3e830a873425f8453eddd85c86925Role918961BB", + "Arn" + ] + } + } + }, + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aRoleB84BD8CE": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "b54b99043c35bd080b9d9d1afce31e3541cf15b679799ba980ed40c837dcb03b.zip" + }, + "Timeout": 120, + "Handler": "index.onTimeout", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aRoleB84BD8CE", + "Arn" + ] + } + } + } + }, + "Outputs": { + "AssertionResultsAwsApiCallCloudFormationdescribeStacks": { + "Value": { + "Fn::GetAtt": [ + "AwsApiCallCloudFormationdescribeStacks", + "assertion" + ] + } + } + }, "Parameters": { "BootstrapVersion": { "Type": "AWS::SSM::Parameter::Value", diff --git a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json index 89d81d8421b3d..251eda57fc9af 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudformation/test/core-cross-region-references.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/641a63e906e5a90f7362126c1648579dec5c836db6e43276dadbf3da7627b08c.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/70e0b1af0ea278adf024d80a32c2797554d527123f1dc1eb9f2a894a6d46bc7a.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -106,7 +106,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/e1c8d2a77a2765a4e4cfdf07208097c34980e050bb50fe5d07702ff157e29adc.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/33212ba7662e584fce97d4b64b2b7d157f5f1bac2b2ffe4e25e18545b514ec8b.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -135,6 +135,18 @@ "data": "IntegNestedParameter1DE6274D4" } ], + "/cross-region-consumer/IntegNested/reference-to-crossregionconsumerExportsReader5D0359E7--cdk--exports--cross-region-consumer--crossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B": [ + { + "type": "aws:cdk:logicalId", + "data": "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegQueue3A18718AQueueName8D8D3C9B" + } + ], + "/cross-region-consumer/IntegNested/reference-to-crossregionconsumerExportsReader5D0359E7--cdk--exports--cross-region-consumer--crossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E": [ + { + "type": "aws:cdk:logicalId", + "data": "referencetocrossregionconsumerExportsReader5D0359E7cdkexportscrossregionconsumercrossregionproduceruseast1FnGetAttIntegNestedNestedStackIntegNestedNestedStackResource168C5881OutputscrossregionproducerIntegNestedNestedIntegQueueD686DB69QueueNameC1C9C99E" + } + ], "/cross-region-consumer/IntegNested.NestedStack/IntegNested.NestedStackResource": [ { "type": "aws:cdk:logicalId", @@ -202,7 +214,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e62b4ad819f8f21c0c8707091f053b8b322398afc1a04fd089b1be1436fb011a.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -218,6 +230,84 @@ "crossregionreferencesDefaultTestDeployAssertAB7415FD.assets" ], "metadata": { + "/cross-region-references/DefaultTest/DeployAssert/AwsApiCallCloudFormationdeleteStack/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCloudFormationdeleteStack" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/AwsApiCallCloudFormationdescribeStacks/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCloudFormationdescribeStacks" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/AwsApiCallCloudFormationdescribeStacks/WaitFor/IsCompleteProvider/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCloudFormationdescribeStacksWaitForIsCompleteProviderInvokeD8EB59C7" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/AwsApiCallCloudFormationdescribeStacks/WaitFor/TimeoutProvider/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCloudFormationdescribeStacksWaitForTimeoutProviderInvokeA2598EF3" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/AwsApiCallCloudFormationdescribeStacks/WaitFor/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCloudFormationdescribeStacksWaitForRoleEC9EDBA0" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/AwsApiCallCloudFormationdescribeStacks/WaitFor/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallCloudFormationdescribeStacksWaitFor1D722558" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/AwsApiCallCloudFormationdescribeStacks/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsAwsApiCallCloudFormationdescribeStacks" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction76b3e830a873425f8453eddd85c86925Role918961BB" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/SingletonFunction76b3e830a873425f8453eddd85c86925/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction76b3e830a873425f8453eddd85c86925Handler81461ECE" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aRoleB84BD8CE" + } + ], + "/cross-region-references/DefaultTest/DeployAssert/SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41a/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction5c1898e096fb4e3e95d5f6c67f3ce41aHandlerADF3E6EA" + } + ], "/cross-region-references/DefaultTest/DeployAssert/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts index 6565d2311f8dc..3c8d84b955fcf 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts @@ -1,7 +1,7 @@ import { Queue, IQueue } from '@aws-cdk/aws-sqs'; import { StringParameter } from '@aws-cdk/aws-ssm'; import { App, Stack, StackProps, NestedStack } from '@aws-cdk/core'; -import { IntegTest } from '@aws-cdk/integ-tests'; +import { IntegTest, ExpectedResult, Match } from '@aws-cdk/integ-tests'; import { Construct } from 'constructs'; // GIVEN @@ -51,13 +51,46 @@ class ConsumerStack extends Stack { }); } } -const producer = new ProducerStack(app, 'cross-region-producer'); -const testCase = new ConsumerStack(app, 'cross-region-consumer', { - queues: [producer.queue, producer.nestedQueue], -}); + +class TestCase extends Construct { + public readonly testCase: Stack; + public readonly producer: ProducerStack; + constructor(scope: Construct, id: string) { + super(scope, id); + this.producer = new ProducerStack(app, 'cross-region-producer'); + this.testCase = new ConsumerStack(app, 'cross-region-consumer', { + queues: [this.producer.queue, this.producer.nestedQueue], + }); + } +} +const testCase1 = new TestCase(app, 'TestCase1'); // THEN -new IntegTest(app, 'cross-region-references', { - testCases: [testCase], +const integ = new IntegTest(app, 'cross-region-references', { + testCases: [testCase1.testCase], stackUpdateWorkflow: false, }); + + +/** + * Test that if the references are still in use, deleting the producer + * stack will fail + * + * When the test cleans up it will delete the consumer then the producer, which should + * test that the parameters are cleaned up correctly. + */ + +integ.assertions.awsApiCall('CloudFormation', 'deleteStack', { + StackName: testCase1.producer.stackName, +}).next( + integ.assertions.awsApiCall('CloudFormation', 'describeStacks', { + StackName: testCase1.producer.stackName, + }).expect(ExpectedResult.objectLike({ + Stacks: Match.arrayWith([ + Match.objectLike({ + StackName: testCase1.producer.stackName, + StackStatus: 'DELETE_FAILED', + }), + ]), + })).waitForAssertions(), +); diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts deleted file mode 100644 index 3554dc94d4617..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js deleted file mode 100644 index f432d8df83b5e..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties.WriterProps; - const exports = props.exports; - const ssm = new aws_sdk_1.SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties.WriterProps; - const oldExports = oldProps.exports; - const newExports = except(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm, parameters) { - const tagResults = new Map(); - await Promise.all(Object.keys(parameters).map(async (name) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { - tagResults.has(name) - ? tagResults.get(name).add(tagParts[2]) - : tagResults.set(name, new Set([tagParts[2]])); - } - }); - } - catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - })); - if (tagResults.size > 0) { - const message = Object.entries(tagResults) - .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source, filter) { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sZUFBZSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFD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QUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLE1BQU0sQ0FBQyxNQUEwQixFQUFFLE1BQTBCO0lBQ3BFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7U0FDdkIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzNFLE1BQU0sQ0FBQyxDQUFDLEdBQXVCLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDaEQsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNYLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IENyb3NzUmVnaW9uRXhwb3J0cywgRXhwb3J0V3JpdGVyQ1JQcm9wcyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gIGNvbnN0IGV4cG9ydHMgPSBwcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcblxuICBjb25zdCBzc20gPSBuZXcgU1NNKHsgcmVnaW9uOiBwcm9wcy5yZWdpb24gfSk7XG4gIHRyeSB7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHMgPSBvbGRQcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGV4Y2VwdChleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgICAgdGFnUmVzdWx0cy5oYXMobmFtZSlcbiAgICAgICAgICAgID8gdGFnUmVzdWx0cy5nZXQobmFtZSkhLmFkZCh0YWdQYXJ0c1syXSlcbiAgICAgICAgICAgIDogdGFnUmVzdWx0cy5zZXQobmFtZSwgbmV3IFNldChbdGFnUGFydHNbMl1dKSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gYW4gSW52YWxpZFJlc291cmNlSWQgbWVhbnMgdGhhdCB0aGUgcGFyYW1ldGVyIGRvZXNuJ3QgZXhpc3RcbiAgICAgIC8vIHdoaWNoIHdlIHNob3VsZCBpZ25vcmUgc2luY2UgdGhhdCBtZWFucyBpdCdzIG5vdCBpbiB1c2VcbiAgICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhyb3cgZTtcbiAgICB9XG5cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiBvbmx5IHRoZSBpdGVtcyBmcm9tIHNvdXJjZSB0aGF0IGRvIG5vdCBleGlzdCBpbiB0aGUgZmlsdGVyXG4gKlxuICogQHBhcmFtIHNvdXJjZSB0aGUgc291cmNlIG9iamVjdCB0byBwZXJmb3JtIHRoZSBmaWx0ZXIgb25cbiAqIEBwYXJhbSBmaWx0ZXIgZmlsdGVyIG91dCBpdGVtcyB0aGF0IGV4aXN0IGluIHRoaXMgb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGV4Y2VwdChzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts deleted file mode 100644 index 8970f2b163664..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -import { SSM } from 'aws-sdk'; -import { CrossRegionExports, ExportWriterCRProps } from '../types'; - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props: ExportWriterCRProps = event.ResourceProperties.WriterProps; - const exports = props.exports as CrossRegionExports; - - const ssm = new SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; - const oldExports = oldProps.exports as CrossRegionExports; - const newExports = except(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } catch (e) { - console.error('Error processing event: ', e); - throw e; - } -}; - -/** - * Create parameters for existing exports - */ -async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} - -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { - const tagResults: Map> = new Map(); - await Promise.all(Object.keys(parameters).map(async (name: string) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { - tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[2]) - : tagResults.set(name, new Set([tagParts[2]])); - } - }); - - } catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - - })); - - if (tagResults.size > 0) { - const message: string = Object.entries(tagResults) - .map((result: [string, string[]]) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} - -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc: CrossRegionExports, curr: string) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741/index.js new file mode 100644 index 0000000000000..77f7ee2324a9a --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741/index.js @@ -0,0 +1,100 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.ReaderProps; + const imports = props.imports; + const importNames = Object.keys(imports); + const keyName = `aws-cdk:strong-ref:${props.prefix}`; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info('Tagging SSM Parameter imports'); + await addTags(ssm, importNames, keyName); + break; + case 'Update': + const oldProps = event.OldResourceProperties.ReaderProps; + const oldExports = oldProps.imports; + const newExports = except(importNames, Object.keys(oldExports)); + const paramsToRelease = except(Object.keys(oldExports), importNames); + console.info('Releasing unused SSM Parameter imports'); + if (Object.keys(paramsToRelease).length > 0) { + await removeTags(ssm, paramsToRelease, keyName); + } + console.info('Tagging new SSM Parameter imports'); + await addTags(ssm, newExports, keyName); + break; + case 'Delete': + console.info('Releasing all SSM Parameter exports by removing tags'); + await removeTags(ssm, importNames, keyName); + return; + } + } + catch (e) { + console.error('Error importing cross region stack exports: ', e); + throw e; + } + console.info('returning imports: ', JSON.stringify(imports)); + return { + Data: imports, + }; +} +exports.handler = handler; +; +/** + * Add tag to parameters for existing exports + */ +async function addTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return await ssm.addTagsToResource({ + ResourceId: name, + ResourceType: 'Parameter', + Tags: [{ + Key: keyName, + Value: 'true', + }], + }).promise(); + } + catch (e) { + throw new Error(`Error importing ${name}: ${e}`); + } + })); +} +/** + * Remove tags from parameters + */ +async function removeTags(ssm, parameters, keyName) { + await Promise.all(parameters.map(async (name) => { + try { + return await ssm.removeTagsFromResource({ + TagKeys: [keyName], + ResourceType: 'Parameter', + ResourceId: name, + }).promise(); + } + catch (e) { + switch (e.code) { + // if the parameter doesn't exist then there is nothing to release + case 'InvalidResourceId': + return; + default: + throw new Error(`Error releasing import ${name}: ${e}`); + } + } + })); +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + */ +function except(source, filter) { + return source.filter(key => !filter.includes(key)); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUF1QixLQUFLLENBQUMsT0FBNkIsQ0FBQztJQUN4RSxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3pDLE1BQU0sT0FBTyxHQUFXLHNCQUFzQixLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7SUFFN0QsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsSUFBSTtRQUNGLFFBQVEsS0FBSyxDQUFDLFdBQVcsRUFBRTtZQUN6QixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxDQUFDLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO2dCQUM5QyxNQUFNLE9BQU8sQ0FBQyxHQUFHLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUN6QyxNQUFNO1lBQ1IsS0FBSyxRQUFRO2dCQUNYLE1BQU0sUUFBUSxHQUF3QixLQUFLLENBQUMscUJBQXFCLENBQUMsV0FBVyxDQUFDO2dCQUM5RSxNQUFNLFVBQVUsR0FBdUIsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzlFLE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxXQUFXLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO2dCQUNoRSxNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxXQUFXLENBQUMsQ0FBQztn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LCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IEV4cG9ydFJlYWRlckNSUHJvcHMsIENyb3NzUmVnaW9uRXhwb3J0cyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHM6IEV4cG9ydFJlYWRlckNSUHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuUmVhZGVyUHJvcHM7XG4gIGNvbnN0IGltcG9ydHM6IENyb3NzUmVnaW9uRXhwb3J0cyA9IHByb3BzLmltcG9ydHMgYXMgQ3Jvc3NSZWdpb25FeHBvcnRzO1xuICBjb25zdCBpbXBvcnROYW1lcyA9IE9iamVjdC5rZXlzKGltcG9ydHMpO1xuICBjb25zdCBrZXlOYW1lOiBzdHJpbmcgPSBgYXdzLWNkazpzdHJvbmctcmVmOiR7cHJvcHMucHJlZml4fWA7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMucmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgYXdhaXQgYWRkVGFncyhzc20sIGltcG9ydE5hbWVzLCBrZXlOYW1lKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wczogRXhwb3J0UmVhZGVyQ1JQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcy5SZWFkZXJQcm9wcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogQ3Jvc3NSZWdpb25FeHBvcnRzID0gb2xkUHJvcHMuaW1wb3J0cyBhcyBDcm9zc1JlZ2lvbkV4cG9ydHM7XG4gICAgICAgIGNvbnN0IG5ld0V4cG9ydHMgPSBleGNlcHQoaW1wb3J0TmFtZXMsIE9iamVjdC5rZXlzKG9sZEV4cG9ydHMpKTtcbiAgICAgICAgY29uc3QgcGFyYW1zVG9SZWxlYXNlID0gZXhjZXB0KE9iamVjdC5rZXlzKG9sZEV4cG9ydHMpLCBpbXBvcnROYW1lcyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbygnUmVsZWFzaW5nIHVudXNlZCBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgaWYgKE9iamVjdC5rZXlzKHBhcmFtc1RvUmVsZWFzZSkubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGF3YWl0IHJlbW92ZVRhZ3Moc3NtLCBwYXJhbXNUb1JlbGVhc2UsIGtleU5hbWUpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBuZXcgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGFkZFRhZ3Moc3NtLCBuZXdFeHBvcnRzLCBrZXlOYW1lKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdEZWxldGUnOlxuICAgICAgICBjb25zb2xlLmluZm8oJ1JlbGVhc2luZyBhbGwgU1NNIFBhcmFtZXRlciBleHBvcnRzIGJ5IHJlbW92aW5nIHRhZ3MnKTtcbiAgICAgICAgYXdhaXQgcmVtb3ZlVGFncyhzc20sIGltcG9ydE5hbWVzLCBrZXlOYW1lKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGltcG9ydGluZyBjcm9zcyByZWdpb24gc3RhY2sgZXhwb3J0czogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxuICBjb25zb2xlLmluZm8oJ3JldHVybmluZyBpbXBvcnRzOiAnLCBKU09OLnN0cmluZ2lmeShpbXBvcnRzKSk7XG4gIHJldHVybiB7XG4gICAgRGF0YTogaW1wb3J0cyxcbiAgfTtcbn07XG5cbi8qKlxuICogQWRkIHRhZyB0byBwYXJhbWV0ZXJzIGZvciBleGlzdGluZyBleHBvcnRzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGFkZFRhZ3Moc3NtOiBTU00sIHBhcmFtZXRlcnM6IHN0cmluZ1tdLCBrZXlOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgUHJvbWlzZS5hbGwocGFyYW1ldGVycy5tYXAoYXN5bmMgbmFtZSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBzc20uYWRkVGFnc1RvUmVzb3VyY2Uoe1xuICAgICAgICBSZXNvdXJjZUlkOiBuYW1lLFxuICAgICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgICAgICBUYWdzOiBbe1xuICAgICAgICAgIEtleToga2V5TmFtZSxcbiAgICAgICAgICBWYWx1ZTogJ3RydWUnLFxuICAgICAgICB9XSxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIGltcG9ydGluZyAke25hbWV9OiAke2V9YCk7XG4gICAgfVxuICB9KSk7XG59XG5cbi8qKlxuICogUmVtb3ZlIHRhZ3MgZnJvbSBwYXJhbWV0ZXJzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlbW92ZVRhZ3Moc3NtOiBTU00sIHBhcmFtZXRlcnM6IHN0cmluZ1tdLCBrZXlOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgUHJvbWlzZS5hbGwocGFyYW1ldGVycy5tYXAoYXN5bmMgbmFtZSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBzc20ucmVtb3ZlVGFnc0Zyb21SZXNvdXJjZSh7XG4gICAgICAgIFRhZ0tleXM6IFtrZXlOYW1lXSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBzd2l0Y2ggKGUuY29kZSkge1xuICAgICAgICAvLyBpZiB0aGUgcGFyYW1ldGVyIGRvZXNuJ3QgZXhpc3QgdGhlbiB0aGVyZSBpcyBub3RoaW5nIHRvIHJlbGVhc2VcbiAgICAgICAgY2FzZSAnSW52YWxpZFJlc291cmNlSWQnOlxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEVycm9yIHJlbGVhc2luZyBpbXBvcnQgJHtuYW1lfTogJHtlfWApO1xuICAgICAgfVxuICAgIH1cbiAgfSkpO1xufVxuXG4vKipcbiAqIFJldHVybiBvbmx5IHRoZSBpdGVtcyBmcm9tIHNvdXJjZSB0aGF0IGRvIG5vdCBleGlzdCBpbiB0aGUgZmlsdGVyXG4gKlxuICogQHBhcmFtIHNvdXJjZSB0aGUgc291cmNlIG9iamVjdCB0byBwZXJmb3JtIHRoZSBmaWx0ZXIgb25cbiAqIEBwYXJhbSBmaWx0ZXIgZmlsdGVyIG91dCBpdGVtcyB0aGF0IGV4aXN0IGluIHRoaXMgb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGV4Y2VwdChzb3VyY2U6IHN0cmluZ1tdLCBmaWx0ZXI6IHN0cmluZ1tdKTogc3RyaW5nW10ge1xuICByZXR1cm4gc291cmNlLmZpbHRlcihrZXkgPT4gIWZpbHRlci5pbmNsdWRlcyhrZXkpKTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.d.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.d.ts deleted file mode 100644 index 3554dc94d4617..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js deleted file mode 100644 index 0cc6cf063d1c7..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.js +++ /dev/null @@ -1,124 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties.ReaderProps; - const imports = props.imports; - const keyName = `aws-cdk:strong-ref:${props.prefix}`; - const ssm = new aws_sdk_1.SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info('Tagging SSM Parameter imports'); - await addTags(ssm, imports, keyName); - return; - case 'Update': - const oldProps = event.OldResourceProperties.ReaderProps; - const oldExports = oldProps.imports; - const newExports = except(imports, oldExports); - const paramsToDelete = except(oldExports, imports); - console.info('Releasing unused SSM Parameter imports'); - if (Object.keys(paramsToDelete).length > 0) { - await removeTags(ssm, paramsToDelete, keyName); - } - console.info('Tagging new SSM Parameter imports'); - await addTags(ssm, newExports, keyName); - return; - case 'Delete': - console.info('Deleting all SSM Parameter exports'); - await deleteParametersByPath(ssm, `/cdk/exports/${props.prefix}/`); - return; - default: - return; - } - } - catch (e) { - console.error('Error importing cross region stack exports: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Add tag to parameters for existing exports - */ -async function addTags(ssm, parameters, keyName) { - await Promise.all(parameters.map(async (name) => { - try { - return await ssm.addTagsToResource({ - ResourceId: name, - ResourceType: 'Parameter', - Tags: [{ - Key: keyName, - Value: 'true', - }], - }).promise(); - } - catch (e) { - throw new Error(`Error importing ${name}: ${e}`); - } - })); -} -/** - * Remove tags from parameters - */ -async function removeTags(ssm, parameters, keyName) { - await Promise.all(parameters.map(async (name) => { - try { - return await ssm.removeTagsFromResource({ - TagKeys: [keyName], - ResourceType: 'Parameter', - ResourceId: name, - }).promise(); - } - catch (e) { - switch (e.code) { - // if the parameter doesn't exist then there is nothing to release - case 'InvalidResourceId': - return; - default: - throw new Error(`Error releasing import ${name}: ${e}`); - } - } - })); -} -/** - * Get all parameters in a given path - * - * If the request fails for any reason it will fail the custom resource event. - * Since this is only run when the resource is deleted that is probably the behavior - * that is desired. - */ -async function getParametersByPath(ssm, path) { - const parameters = []; - let nextToken; - do { - const response = await ssm.getParametersByPath({ Path: path, NextToken: nextToken }).promise(); - parameters.push(...response.Parameters ?? []); - nextToken = response.NextToken; - } while (nextToken); - return parameters; -} -/** - * Delete all parameters in a give path - */ -async function deleteParametersByPath(ssm, path) { - const allParams = await getParametersByPath(ssm, path); - const names = allParams.map(param => param.Name).filter(x => !!x); - await ssm.deleteParameters({ - Names: names, - }).promise(); -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source, filter) { - return source.filter(key => !filter.includes(key)); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFhLEtBQUssQ0FBQyxPQUFPLENBQUM7SUFDeEMsTUFBTSxPQUFPLEdBQVcsc0JBQXNCLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUU3RCxNQUFNLEdBQUcsR0FBRyxJQUFJLGFBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUM5QyxJQUFJO1FBQ0YsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1lBQ3pCLEtBQUssUUFBUTtnQkFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLCtCQUErQixDQUFDLENBQUM7Z0JBQzlDLE1BQU0sT0FBTyxDQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3JDLE9BQU87WUFDVCxLQUFLLFFBQVE7Z0JBQ1gsTUFBTSxRQUFRLEdBQXdCLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxXQUFXLENBQUM7Z0JBQzlFLE1BQU0sVUFBVSxHQUFhLFFBQVEsQ0FBQyxPQUFPLENBQUM7Z0JBQzlDLE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sY0FBYyxHQUFHLE1BQU0sQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ25ELE9BQU8sQ0FBQyxJQUFJLENBQUMsd0NBQXdDLENBQUMsQ0FBQztnQkFDdkQsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7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LEVBQUU7SUFDcEIsT0FBTyxVQUFVLENBQUM7QUFDcEIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsS0FBSyxVQUFVLHNCQUFzQixDQUFDLEdBQVEsRUFBRSxJQUFZO0lBQzFELE1BQU0sU0FBUyxHQUFHLE1BQU0sbUJBQW1CLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3ZELE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBYSxDQUFDO0lBQzlFLE1BQU0sR0FBRyxDQUFDLGdCQUFnQixDQUFDO1FBQ3pCLEtBQUssRUFBRSxLQUFLO0tBQ2IsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ2YsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxNQUFNLENBQUMsTUFBZ0IsRUFBRSxNQUFnQjtJQUNoRCxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUNyRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyplc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgeyBFeHBvcnRSZWFkZXJDUlByb3BzIH0gZnJvbSAnLi4vdHlwZXMnO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaGFuZGxlcihldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCkge1xuICBjb25zdCBwcm9wczogRXhwb3J0UmVhZGVyQ1JQcm9wcyA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5SZWFkZXJQcm9wcztcbiAgY29uc3QgaW1wb3J0czogc3RyaW5nW10gPSBwcm9wcy5pbXBvcnRzO1xuICBjb25zdCBrZXlOYW1lOiBzdHJpbmcgPSBgYXdzLWNkazpzdHJvbmctcmVmOiR7cHJvcHMucHJlZml4fWA7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMucmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBTU00gUGFyYW1ldGVyIGltcG9ydHMnKTtcbiAgICAgICAgYXdhaXQgYWRkVGFncyhzc20sIGltcG9ydHMsIGtleU5hbWUpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdVcGRhdGUnOlxuICAgICAgICBjb25zdCBvbGRQcm9wczogRXhwb3J0UmVhZGVyQ1JQcm9wcyA9IGV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcy5SZWFkZXJQcm9wcztcbiAgICAgICAgY29uc3Qgb2xkRXhwb3J0czogc3RyaW5nW10gPSBvbGRQcm9wcy5pbXBvcnRzO1xuICAgICAgICBjb25zdCBuZXdFeHBvcnRzID0gZXhjZXB0KGltcG9ydHMsIG9sZEV4cG9ydHMpO1xuICAgICAgICBjb25zdCBwYXJhbXNUb0RlbGV0ZSA9IGV4Y2VwdChvbGRFeHBvcnRzLCBpbXBvcnRzKTtcbiAgICAgICAgY29uc29sZS5pbmZvKCdSZWxlYXNpbmcgdW51c2VkIFNTTSBQYXJhbWV0ZXIgaW1wb3J0cycpO1xuICAgICAgICBpZiAoT2JqZWN0LmtleXMocGFyYW1zVG9EZWxldGUpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCByZW1vdmVUYWdzKHNzbSwgcGFyYW1zVG9EZWxldGUsIGtleU5hbWUpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnNvbGUuaW5mbygnVGFnZ2luZyBuZXcgU1NNIFBhcmFtZXRlciBpbXBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGFkZFRhZ3Moc3NtLCBuZXdFeHBvcnRzLCBrZXlOYW1lKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKCdEZWxldGluZyBhbGwgU1NNIFBhcmFtZXRlciBleHBvcnRzJyk7XG4gICAgICAgIGF3YWl0IGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtLCBgL2Nkay9leHBvcnRzLyR7cHJvcHMucHJlZml4fS9gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGltcG9ydGluZyBjcm9zcyByZWdpb24gc3RhY2sgZXhwb3J0czogJywgZSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcblxuLyoqXG4gKiBBZGQgdGFnIHRvIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gYWRkVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5hZGRUYWdzVG9SZXNvdXJjZSh7XG4gICAgICAgIFJlc291cmNlSWQ6IG5hbWUsXG4gICAgICAgIFJlc291cmNlVHlwZTogJ1BhcmFtZXRlcicsXG4gICAgICAgIFRhZ3M6IFt7XG4gICAgICAgICAgS2V5OiBrZXlOYW1lLFxuICAgICAgICAgIFZhbHVlOiAndHJ1ZScsXG4gICAgICAgIH1dLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgaW1wb3J0aW5nICR7bmFtZX06ICR7ZX1gKTtcbiAgICB9XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBSZW1vdmUgdGFncyBmcm9tIHBhcmFtZXRlcnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVtb3ZlVGFncyhzc206IFNTTSwgcGFyYW1ldGVyczogc3RyaW5nW10sIGtleU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChwYXJhbWV0ZXJzLm1hcChhc3luYyBuYW1lID0+IHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNzbS5yZW1vdmVUYWdzRnJvbVJlc291cmNlKHtcbiAgICAgICAgVGFnS2V5czogW2tleU5hbWVdLFxuICAgICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgICAgICBSZXNvdXJjZUlkOiBuYW1lLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHN3aXRjaCAoZS5jb2RlKSB7XG4gICAgICAgIC8vIGlmIHRoZSBwYXJhbWV0ZXIgZG9lc24ndCBleGlzdCB0aGVuIHRoZXJlIGlzIG5vdGhpbmcgdG8gcmVsZWFzZVxuICAgICAgICBjYXNlICdJbnZhbGlkUmVzb3VyY2VJZCc6XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3IgcmVsZWFzaW5nIGltcG9ydCAke25hbWV9OiAke2V9YCk7XG4gICAgICB9XG4gICAgfVxuICB9KSk7XG59XG5cbi8qKlxuICogR2V0IGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZW4gcGF0aFxuICpcbiAqIElmIHRoZSByZXF1ZXN0IGZhaWxzIGZvciBhbnkgcmVhc29uIGl0IHdpbGwgZmFpbCB0aGUgY3VzdG9tIHJlc291cmNlIGV2ZW50LlxuICogU2luY2UgdGhpcyBpcyBvbmx5IHJ1biB3aGVuIHRoZSByZXNvdXJjZSBpcyBkZWxldGVkIHRoYXQgaXMgcHJvYmFibHkgdGhlIGJlaGF2aW9yXG4gKiB0aGF0IGlzIGRlc2lyZWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldFBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZyk6IFByb21pc2U8U1NNLlBhcmFtZXRlcltdPiB7XG4gIGNvbnN0IHBhcmFtZXRlcnM6IFNTTS5QYXJhbWV0ZXJbXSA9IFtdO1xuICBsZXQgbmV4dFRva2VuOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGRvIHtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHNzbS5nZXRQYXJhbWV0ZXJzQnlQYXRoKHsgUGF0aDogcGF0aCwgTmV4dFRva2VuOiBuZXh0VG9rZW4gfSkucHJvbWlzZSgpO1xuICAgIHBhcmFtZXRlcnMucHVzaCguLi5yZXNwb25zZS5QYXJhbWV0ZXJzID8/IFtdKTtcbiAgICBuZXh0VG9rZW4gPSByZXNwb25zZS5OZXh0VG9rZW47XG5cbiAgfSB3aGlsZSAobmV4dFRva2VuKTtcbiAgcmV0dXJuIHBhcmFtZXRlcnM7XG59XG5cbi8qKlxuICogRGVsZXRlIGFsbCBwYXJhbWV0ZXJzIGluIGEgZ2l2ZSBwYXRoXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGRlbGV0ZVBhcmFtZXRlcnNCeVBhdGgoc3NtOiBTU00sIHBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBhbGxQYXJhbXMgPSBhd2FpdCBnZXRQYXJhbWV0ZXJzQnlQYXRoKHNzbSwgcGF0aCk7XG4gIGNvbnN0IG5hbWVzID0gYWxsUGFyYW1zLm1hcChwYXJhbSA9PiBwYXJhbS5OYW1lKS5maWx0ZXIoeCA9PiAhIXgpIGFzIHN0cmluZ1tdO1xuICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgTmFtZXM6IG5hbWVzLFxuICB9KS5wcm9taXNlKCk7XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqL1xuZnVuY3Rpb24gZXhjZXB0KHNvdXJjZTogc3RyaW5nW10sIGZpbHRlcjogc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBzb3VyY2UuZmlsdGVyKGtleSA9PiAhZmlsdGVyLmluY2x1ZGVzKGtleSkpO1xufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.ts b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.ts deleted file mode 100644 index 7c92fde2bf1c7..0000000000000 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/index.ts +++ /dev/null @@ -1,124 +0,0 @@ -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -import { SSM } from 'aws-sdk'; -import { ExportReaderCRProps } from '../types'; - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props: ExportReaderCRProps = event.ResourceProperties.ReaderProps; - const imports: string[] = props.imports; - const keyName: string = `aws-cdk:strong-ref:${props.prefix}`; - - const ssm = new SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info('Tagging SSM Parameter imports'); - await addTags(ssm, imports, keyName); - return; - case 'Update': - const oldProps: ExportReaderCRProps = event.OldResourceProperties.ReaderProps; - const oldExports: string[] = oldProps.imports; - const newExports = except(imports, oldExports); - const paramsToDelete = except(oldExports, imports); - console.info('Releasing unused SSM Parameter imports'); - if (Object.keys(paramsToDelete).length > 0) { - await removeTags(ssm, paramsToDelete, keyName); - } - console.info('Tagging new SSM Parameter imports'); - await addTags(ssm, newExports, keyName); - return; - case 'Delete': - console.info('Deleting all SSM Parameter exports'); - await deleteParametersByPath(ssm, `/cdk/exports/${props.prefix}/`); - return; - default: - return; - } - } catch (e) { - console.error('Error importing cross region stack exports: ', e); - throw e; - } -}; - -/** - * Add tag to parameters for existing exports - */ -async function addTags(ssm: SSM, parameters: string[], keyName: string): Promise { - await Promise.all(parameters.map(async name => { - try { - return await ssm.addTagsToResource({ - ResourceId: name, - ResourceType: 'Parameter', - Tags: [{ - Key: keyName, - Value: 'true', - }], - }).promise(); - } catch (e) { - throw new Error(`Error importing ${name}: ${e}`); - } - })); -} - -/** - * Remove tags from parameters - */ -async function removeTags(ssm: SSM, parameters: string[], keyName: string): Promise { - await Promise.all(parameters.map(async name => { - try { - return await ssm.removeTagsFromResource({ - TagKeys: [keyName], - ResourceType: 'Parameter', - ResourceId: name, - }).promise(); - } catch (e) { - switch (e.code) { - // if the parameter doesn't exist then there is nothing to release - case 'InvalidResourceId': - return; - default: - throw new Error(`Error releasing import ${name}: ${e}`); - } - } - })); -} - -/** - * Get all parameters in a given path - * - * If the request fails for any reason it will fail the custom resource event. - * Since this is only run when the resource is deleted that is probably the behavior - * that is desired. - */ -async function getParametersByPath(ssm: SSM, path: string): Promise { - const parameters: SSM.Parameter[] = []; - let nextToken: string | undefined; - do { - const response = await ssm.getParametersByPath({ Path: path, NextToken: nextToken }).promise(); - parameters.push(...response.Parameters ?? []); - nextToken = response.NextToken; - - } while (nextToken); - return parameters; -} - -/** - * Delete all parameters in a give path - */ -async function deleteParametersByPath(ssm: SSM, path: string): Promise { - const allParams = await getParametersByPath(ssm, path); - const names = allParams.map(param => param.Name).filter(x => !!x) as string[]; - await ssm.deleteParameters({ - Names: names, - }).promise(); -} - -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source: string[], filter: string[]): string[] { - return source.filter(key => !filter.includes(key)); -} diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/__entrypoint__.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878/__entrypoint__.js rename to packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js new file mode 100644 index 0000000000000..9f71f540e4994 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js @@ -0,0 +1,148 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.WriterProps; + const exports = props.exports; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports; + const newExports = except(exports, oldExports); + // throw an error to fail the deployment if any export value is changing + const changedExports = changed(oldExports, exports); + if (changedExports.length > 0) { + throw new Error('Some exports have changed!\n' + changedExports.join('\n')); + } + // if we are removing any exports that are in use, then throw an + // error to fail the deployment + const removedExports = except(oldExports, exports); + await throwIfAnyInUse(ssm, removedExports); + // if the ones we are removing are not in use then delete them + await ssm.deleteParameters({ + Names: Object.keys(removedExports), + }).promise(); + // also throw an error if we are creating a new export that already exists for some reason + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // if any of the exports are currently in use then throw an error to fail + // the stack deletion. + await throwIfAnyInUse(ssm, exports); + // if none are in use then delete all of them + await ssm.deleteParameters({ + Names: Object.keys(exports), + }).promise(); + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + const result = await isInUse(ssm, name); + if (result.size > 0) { + tagResults.set(name, result); + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Check if a parameter is in use + */ +async function isInUse(ssm, parameterName) { + const tagResults = new Set(); + try { + const result = await ssm.listTagsForResource({ + ResourceId: parameterName, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { + tagResults.add(tagParts[2]); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return new Set(); + } + throw e; + } + return tagResults; +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + * @returns any exports that don't exist in the filter + */ +function except(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key))) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +/** + * Return items that exist in both the the old parameters and the new parameters, + * but have different values + * + * @param oldParams the exports that existed previous to this execution + * @param newParams the exports for the current execution + * @returns any parameters that have different values + */ +function changed(oldParams, newParams) { + return Object.keys(oldParams) + .filter(key => (newParams.hasOwnProperty(key) && oldParams[key] !== newParams[key])) + .reduce((acc, curr) => { + acc.push(curr); + return acc; + }, []); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBRS9DLHdFQUF3RTtnQkFDeEUsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDcEQsSUFBSSxjQUFjLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDN0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsR0FBRSxjQUFjLENBQUMs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Q0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztTQUM1QyxNQUFNLENBQUMsQ0FBQyxHQUF1QixFQUFFLElBQVksRUFBRSxFQUFFO1FBQ2hELEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDWCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsT0FBTyxDQUFDLFNBQTZCLEVBQUUsU0FBNkI7SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQztTQUMxQixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQ25GLE1BQU0sQ0FBQyxDQUFDLEdBQWEsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUN0QyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2YsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDWCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyplc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgeyBDcm9zc1JlZ2lvbkV4cG9ydHMsIEV4cG9ydFdyaXRlckNSUHJvcHMgfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGNvbnN0IHByb3BzOiBFeHBvcnRXcml0ZXJDUlByb3BzID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLldyaXRlclByb3BzO1xuICBjb25zdCBleHBvcnRzID0gcHJvcHMuZXhwb3J0cyBhcyBDcm9zc1JlZ2lvbkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMucmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzOiBFeHBvcnRXcml0ZXJDUlByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzLldyaXRlclByb3BzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzID0gb2xkUHJvcHMuZXhwb3J0cyBhcyBDcm9zc1JlZ2lvbkV4cG9ydHM7XG4gICAgICAgIGNvbnN0IG5ld0V4cG9ydHMgPSBleGNlcHQoZXhwb3J0cywgb2xkRXhwb3J0cyk7XG5cbiAgICAgICAgLy8gdGhyb3cgYW4gZXJyb3IgdG8gZmFpbCB0aGUgZGVwbG95bWVudCBpZiBhbnkgZXhwb3J0IHZhbHVlIGlzIGNoYW5naW5nXG4gICAgICAgIGNvbnN0IGNoYW5nZWRFeHBvcnRzID0gY2hhbmdlZChvbGRFeHBvcnRzLCBleHBvcnRzKTtcbiAgICAgICAgaWYgKGNoYW5nZWRFeHBvcnRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NvbWUgZXhwb3J0cyBoYXZlIGNoYW5nZWQhXFxuJysgY2hhbmdlZEV4cG9ydHMuam9pbignXFxuJykpO1xuICAgICAgICB9XG4gICAgICAgIC8vIGlmIHdlIGFyZSByZW1vdmluZyBhbnkgZXhwb3J0cyB0aGF0IGFyZSBpbiB1c2UsIHRoZW4gdGhyb3cgYW5cbiAgICAgICAgLy8gZXJyb3IgdG8gZmFpbCB0aGUgZGVwbG95bWVudFxuICAgICAgICBjb25zdCByZW1vdmVkRXhwb3J0cyA9IGV4Y2VwdChvbGRFeHBvcnRzLCBleHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgcmVtb3ZlZEV4cG9ydHMpO1xuICAgICAgICAvLyBpZiB0aGUgb25lcyB3ZSBhcmUgcmVtb3ZpbmcgYXJlIG5vdCBpbiB1c2UgdGhlbiBkZWxldGUgdGhlbVxuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IE9iamVjdC5rZXlzKHJlbW92ZWRFeHBvcnRzKSxcbiAgICAgICAgfSkucHJvbWlzZSgpO1xuXG4gICAgICAgIC8vIGFsc28gdGhyb3cgYW4gZXJyb3IgaWYgd2UgYXJlIGNyZWF0aW5nIGEgbmV3IGV4cG9ydCB0aGF0IGFscmVhZHkgZXhpc3RzIGZvciBzb21lIHJlYXNvblxuICAgICAgICBhd2FpdCB0aHJvd0lmQW55SW5Vc2Uoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgcHV0UGFyYW1ldGVycyhzc20sIG5ld0V4cG9ydHMpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdEZWxldGUnOlxuICAgICAgICAvLyBpZiBhbnkgb2YgdGhlIGV4cG9ydHMgYXJlIGN1cnJlbnRseSBpbiB1c2UgdGhlbiB0aHJvdyBhbiBlcnJvciB0byBmYWlsXG4gICAgICAgIC8vIHRoZSBzdGFjayBkZWxldGlvbi5cbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIC8vIGlmIG5vbmUgYXJlIGluIHVzZSB0aGVuIGRlbGV0ZSBhbGwgb2YgdGhlbVxuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IE9iamVjdC5rZXlzKGV4cG9ydHMpLFxuICAgICAgICB9KS5wcm9taXNlKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmVycm9yKCdFcnJvciBwcm9jZXNzaW5nIGV2ZW50OiAnLCBlKTtcbiAgICB0aHJvdyBlO1xuICB9XG59O1xuXG4vKipcbiAqIENyZWF0ZSBwYXJhbWV0ZXJzIGZvciBleGlzdGluZyBleHBvcnRzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHB1dFBhcmFtZXRlcnMoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChBcnJheS5mcm9tKE9iamVjdC5lbnRyaWVzKHBhcmFtZXRlcnMpLCAoW25hbWUsIHZhbHVlXSkgPT4ge1xuICAgIHJldHVybiBzc20ucHV0UGFyYW1ldGVyKHtcbiAgICAgIE5hbWU6IG5hbWUsXG4gICAgICBWYWx1ZTogdmFsdWUsXG4gICAgICBUeXBlOiAnU3RyaW5nJyxcbiAgICB9KS5wcm9taXNlKCk7XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBRdWVyeSBmb3IgZXhpc3RpbmcgcGFyYW1ldGVycyB0aGF0IGFyZSBpbiB1c2VcbiAqL1xuYXN5bmMgZnVuY3Rpb24gdGhyb3dJZkFueUluVXNlKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgdGFnUmVzdWx0czogTWFwPHN0cmluZywgU2V0PHN0cmluZz4+ID0gbmV3IE1hcCgpO1xuICBhd2FpdCBQcm9taXNlLmFsbChPYmplY3Qua2V5cyhwYXJhbWV0ZXJzKS5tYXAoYXN5bmMgKG5hbWU6IHN0cmluZykgPT4ge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGlzSW5Vc2Uoc3NtLCBuYW1lKTtcbiAgICBpZiAocmVzdWx0LnNpemUgPiAwKSB7XG4gICAgICB0YWdSZXN1bHRzLnNldChuYW1lLCByZXN1bHQpO1xuICAgIH1cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIENoZWNrIGlmIGEgcGFyYW1ldGVyIGlzIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiBpc0luVXNlKHNzbTogU1NNLCBwYXJhbWV0ZXJOYW1lOiBzdHJpbmcpOiBQcm9taXNlPFNldDxzdHJpbmc+PiB7XG4gIGNvbnN0IHRhZ1Jlc3VsdHM6IFNldDxzdHJpbmc+ID0gbmV3IFNldCgpO1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgIFJlc291cmNlSWQ6IHBhcmFtZXRlck5hbWUsXG4gICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgIH0pLnByb21pc2UoKTtcbiAgICByZXN1bHQuVGFnTGlzdD8uZm9yRWFjaCh0YWcgPT4ge1xuICAgICAgY29uc3QgdGFnUGFydHMgPSB0YWcuS2V5LnNwbGl0KCc6Jyk7XG4gICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgIHRhZ1Jlc3VsdHMuYWRkKHRhZ1BhcnRzWzJdKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIGFuIEludmFsaWRSZXNvdXJjZUlkIG1lYW5zIHRoYXQgdGhlIHBhcmFtZXRlciBkb2Vzbid0IGV4aXN0XG4gICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgIHJldHVybiBuZXcgU2V0KCk7XG4gICAgfVxuICAgIHRocm93IGU7XG4gIH1cbiAgcmV0dXJuIHRhZ1Jlc3VsdHM7XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqIEByZXR1cm5zIGFueSBleHBvcnRzIHRoYXQgZG9uJ3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICovXG5mdW5jdGlvbiBleGNlcHQoc291cmNlOiBDcm9zc1JlZ2lvbkV4cG9ydHMsIGZpbHRlcjogQ3Jvc3NSZWdpb25FeHBvcnRzKTogQ3Jvc3NSZWdpb25FeHBvcnRzIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKHNvdXJjZSlcbiAgICAuZmlsdGVyKGtleSA9PiAoIWZpbHRlci5oYXNPd25Qcm9wZXJ0eShrZXkpKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cblxuLyoqXG4gKiBSZXR1cm4gaXRlbXMgdGhhdCBleGlzdCBpbiBib3RoIHRoZSB0aGUgb2xkIHBhcmFtZXRlcnMgYW5kIHRoZSBuZXcgcGFyYW1ldGVycyxcbiAqIGJ1dCBoYXZlIGRpZmZlcmVudCB2YWx1ZXNcbiAqXG4gKiBAcGFyYW0gb2xkUGFyYW1zIHRoZSBleHBvcnRzIHRoYXQgZXhpc3RlZCBwcmV2aW91cyB0byB0aGlzIGV4ZWN1dGlvblxuICogQHBhcmFtIG5ld1BhcmFtcyB0aGUgZXhwb3J0cyBmb3IgdGhlIGN1cnJlbnQgZXhlY3V0aW9uXG4gKiBAcmV0dXJucyBhbnkgcGFyYW1ldGVycyB0aGF0IGhhdmUgZGlmZmVyZW50IHZhbHVlc1xuICovXG5mdW5jdGlvbiBjaGFuZ2VkKG9sZFBhcmFtczogQ3Jvc3NSZWdpb25FeHBvcnRzLCBuZXdQYXJhbXM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IHN0cmluZ1tdIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKG9sZFBhcmFtcylcbiAgICAuZmlsdGVyKGtleSA9PiAobmV3UGFyYW1zLmhhc093blByb3BlcnR5KGtleSkgJiYgb2xkUGFyYW1zW2tleV0gIT09IG5ld1BhcmFtc1trZXldKSlcbiAgICAucmVkdWNlKChhY2M6IHN0cmluZ1tdLCBjdXJyOiBzdHJpbmcpID0+IHtcbiAgICAgIGFjYy5wdXNoKGN1cnIpO1xuICAgICAgcmV0dXJuIGFjYztcbiAgICB9LCBbXSk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json index b96a348a60978..84c977fbd60ae 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.assets.json @@ -1,21 +1,21 @@ { "version": "21.0.0", "files": { - "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03": { + "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3": { "source": { - "path": "asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03", + "path": "asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3", "packaging": "zip" }, "destinations": { "12345678-us-east-1": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", - "objectKey": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip", + "objectKey": "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" } } }, - "8f45c6dc78189ec64aedbbe51f14c1ab502625ff42f76ea4b3133f6518d09ab4": { + "0b14fe35281a6e4200c148a24548f1b44d7a5accad61a70b660cc9d8ddd984b7": { "source": { "path": "integ-acm-stack.template.json", "packaging": "file" @@ -23,7 +23,7 @@ "destinations": { "12345678-us-east-1": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", - "objectKey": "8f45c6dc78189ec64aedbbe51f14c1ab502625ff42f76ea4b3133f6518d09ab4.json", + "objectKey": "0b14fe35281a6e4200c148a24548f1b44d7a5accad61a70b660cc9d8ddd984b7.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json index 38c7ef05ce839..98fe3105c3147 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-acm-stack.template.json @@ -64,6 +64,7 @@ "Effect": "Allow", "Resource": "arn:aws:ssm:us-east-2:12345678:parameter/cdk/exports/*", "Action": [ + "ssm:DeleteParameters", "ssm:ListTagsForResource", "ssm:GetParameters", "ssm:PutParameter" @@ -80,7 +81,7 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-1", - "S3Key": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip" + "S3Key": "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json index ee98d77e6ccd9..3946746155b52 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.assets.json @@ -1,21 +1,21 @@ { "version": "21.0.0", "files": { - "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878": { + "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741": { "source": { - "path": "asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878", + "path": "asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741", "packaging": "zip" }, "destinations": { "12345678-us-east-2": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip", + "objectKey": "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } } }, - "a34d8c9d8e266042eca500b0479cd35e6f3a1b782b3c37fd090a83ac30f2f24a": { + "88d1a4132b2a5bcd2d72a458f2ad58efc59bdd316e41bbf4b8cacad3720e5d7d": { "source": { "path": "integ-cloudfront-stack.template.json", "packaging": "file" @@ -23,7 +23,7 @@ "destinations": { "12345678-us-east-2": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-2", - "objectKey": "a34d8c9d8e266042eca500b0479cd35e6f3a1b782b3c37fd090a83ac30f2f24a.json", + "objectKey": "88d1a4132b2a5bcd2d72a458f2ad58efc59bdd316e41bbf4b8cacad3720e5d7d.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-2" } diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json index c77430560de99..43aed077d4116 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/integ-cloudfront-stack.template.json @@ -26,7 +26,12 @@ } ], "ViewerCertificate": { - "AcmCertificateArn": "{{resolve:ssm:/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2}}", + "AcmCertificateArn": { + "Fn::GetAtt": [ + "ExportsReader8B249524", + "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2" + ] + }, "MinimumProtocolVersion": "TLSv1.2_2021", "SslSupportMethod": "sni-only" } @@ -45,9 +50,9 @@ "ReaderProps": { "region": "us-east-2", "prefix": "integ-cloudfront-stack", - "imports": [ - "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2" - ] + "imports": { + "/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2": "{{resolve:ssm:/cdk/exports/integ-cloudfront-stack/integacmstackuseast1RefCert5C9FAEC18647F8A2}}" + } } }, "UpdateReplacePolicy": "Delete", @@ -83,10 +88,8 @@ "Effect": "Allow", "Resource": "arn:aws:ssm:us-east-2:12345678:parameter/cdk/exports/integ-cloudfront-stack/*", "Action": [ - "ssm:DeleteParameters", "ssm:AddTagsToResource", "ssm:RemoveTagsFromResource", - "ssm:GetParametersByPath", "ssm:GetParameters" ] } @@ -101,7 +104,7 @@ "Properties": { "Code": { "S3Bucket": "cdk-hnb659fds-assets-12345678-us-east-2", - "S3Key": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip" + "S3Key": "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json index 92386d70e6b6a..e1e2d290bb5f0 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-cloudfront/test/cloudfront-cross-region-cert.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/8f45c6dc78189ec64aedbbe51f14c1ab502625ff42f76ea4b3133f6518d09ab4.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/0b14fe35281a6e4200c148a24548f1b44d7a5accad61a70b660cc9d8ddd984b7.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -88,7 +88,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/a34d8c9d8e266042eca500b0479cd35e6f3a1b782b3c37fd090a83ac30f2f24a.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-2/88d1a4132b2a5bcd2d72a458f2ad58efc59bdd316e41bbf4b8cacad3720e5d7d.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts deleted file mode 100644 index 3554dc94d4617..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js deleted file mode 100644 index f432d8df83b5e..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -const aws_sdk_1 = require("aws-sdk"); -async function handler(event) { - const props = event.ResourceProperties.WriterProps; - const exports = props.exports; - const ssm = new aws_sdk_1.SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps = event.OldResourceProperties.WriterProps; - const oldExports = oldProps.exports; - const newExports = except(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } - catch (e) { - console.error('Error processing event: ', e); - throw e; - } -} -exports.handler = handler; -; -/** - * Create parameters for existing exports - */ -async function putParameters(ssm, parameters) { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm, parameters) { - const tagResults = new Map(); - await Promise.all(Object.keys(parameters).map(async (name) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { - tagResults.has(name) - ? tagResults.get(name).add(tagParts[2]) - : tagResults.set(name, new Set([tagParts[2]])); - } - }); - } - catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - })); - if (tagResults.size > 0) { - const message = Object.entries(tagResults) - .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source, filter) { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc, curr) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQy9DLE1BQU0sZUFBZSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyxnREFBZ0QsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFD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QUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLE1BQU0sQ0FBQyxNQUEwQixFQUFFLE1BQTBCO0lBQ3BFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7U0FDdkIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQzNFLE1BQU0sQ0FBQyxDQUFDLEdBQXVCLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDaEQsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNYLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKmVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUqL1xuLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBTU00gfSBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCB7IENyb3NzUmVnaW9uRXhwb3J0cywgRXhwb3J0V3JpdGVyQ1JQcm9wcyB9IGZyb20gJy4uL3R5cGVzJztcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgY29uc3QgcHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gIGNvbnN0IGV4cG9ydHMgPSBwcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcblxuICBjb25zdCBzc20gPSBuZXcgU1NNKHsgcmVnaW9uOiBwcm9wcy5yZWdpb24gfSk7XG4gIHRyeSB7XG4gICAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgICAgY2FzZSAnQ3JlYXRlJzpcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBleHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgICAgY29uc3Qgb2xkUHJvcHM6IEV4cG9ydFdyaXRlckNSUHJvcHMgPSBldmVudC5PbGRSZXNvdXJjZVByb3BlcnRpZXMuV3JpdGVyUHJvcHM7XG4gICAgICAgIGNvbnN0IG9sZEV4cG9ydHMgPSBvbGRQcm9wcy5leHBvcnRzIGFzIENyb3NzUmVnaW9uRXhwb3J0cztcbiAgICAgICAgY29uc3QgbmV3RXhwb3J0cyA9IGV4Y2VwdChleHBvcnRzLCBvbGRFeHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgbmV3RXhwb3J0cyk7XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHB1dFBhcmFtZXRlcnMoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgICAgLy8gY29uc3VtaW5nIHN0YWNrIHdpbGwgZGVsZXRlIHBhcmFtZXRlcnNcbiAgICAgICAgcmV0dXJuO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHByb2Nlc3NpbmcgZXZlbnQ6ICcsIGUpO1xuICAgIHRocm93IGU7XG4gIH1cbn07XG5cbi8qKlxuICogQ3JlYXRlIHBhcmFtZXRlcnMgZm9yIGV4aXN0aW5nIGV4cG9ydHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gcHV0UGFyYW1ldGVycyhzc206IFNTTSwgcGFyYW1ldGVyczogQ3Jvc3NSZWdpb25FeHBvcnRzKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oT2JqZWN0LmVudHJpZXMocGFyYW1ldGVycyksIChbbmFtZSwgdmFsdWVdKSA9PiB7XG4gICAgcmV0dXJuIHNzbS5wdXRQYXJhbWV0ZXIoe1xuICAgICAgTmFtZTogbmFtZSxcbiAgICAgIFZhbHVlOiB2YWx1ZSxcbiAgICAgIFR5cGU6ICdTdHJpbmcnLFxuICAgIH0pLnByb21pc2UoKTtcbiAgfSkpO1xufVxuXG4vKipcbiAqIFF1ZXJ5IGZvciBleGlzdGluZyBwYXJhbWV0ZXJzIHRoYXQgYXJlIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiB0aHJvd0lmQW55SW5Vc2Uoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB0YWdSZXN1bHRzOiBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4gPSBuZXcgTWFwKCk7XG4gIGF3YWl0IFByb21pc2UuYWxsKE9iamVjdC5rZXlzKHBhcmFtZXRlcnMpLm1hcChhc3luYyAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgICAgUmVzb3VyY2VJZDogbmFtZSxcbiAgICAgICAgUmVzb3VyY2VUeXBlOiAnUGFyYW1ldGVyJyxcbiAgICAgIH0pLnByb21pc2UoKTtcbiAgICAgIHJlc3VsdC5UYWdMaXN0Py5mb3JFYWNoKHRhZyA9PiB7XG4gICAgICAgIGNvbnN0IHRhZ1BhcnRzID0gdGFnLktleS5zcGxpdCgnOicpO1xuICAgICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgICAgdGFnUmVzdWx0cy5oYXMobmFtZSlcbiAgICAgICAgICAgID8gdGFnUmVzdWx0cy5nZXQobmFtZSkhLmFkZCh0YWdQYXJ0c1syXSlcbiAgICAgICAgICAgIDogdGFnUmVzdWx0cy5zZXQobmFtZSwgbmV3IFNldChbdGFnUGFydHNbMl1dKSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gYW4gSW52YWxpZFJlc291cmNlSWQgbWVhbnMgdGhhdCB0aGUgcGFyYW1ldGVyIGRvZXNuJ3QgZXhpc3RcbiAgICAgIC8vIHdoaWNoIHdlIHNob3VsZCBpZ25vcmUgc2luY2UgdGhhdCBtZWFucyBpdCdzIG5vdCBpbiB1c2VcbiAgICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhyb3cgZTtcbiAgICB9XG5cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybiBvbmx5IHRoZSBpdGVtcyBmcm9tIHNvdXJjZSB0aGF0IGRvIG5vdCBleGlzdCBpbiB0aGUgZmlsdGVyXG4gKlxuICogQHBhcmFtIHNvdXJjZSB0aGUgc291cmNlIG9iamVjdCB0byBwZXJmb3JtIHRoZSBmaWx0ZXIgb25cbiAqIEBwYXJhbSBmaWx0ZXIgZmlsdGVyIG91dCBpdGVtcyB0aGF0IGV4aXN0IGluIHRoaXMgb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGV4Y2VwdChzb3VyY2U6IENyb3NzUmVnaW9uRXhwb3J0cywgZmlsdGVyOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBDcm9zc1JlZ2lvbkV4cG9ydHMge1xuICByZXR1cm4gT2JqZWN0LmtleXMoc291cmNlKVxuICAgIC5maWx0ZXIoa2V5ID0+ICghZmlsdGVyLmhhc093blByb3BlcnR5KGtleSkgfHwgc291cmNlW2tleV0gIT09IGZpbHRlcltrZXldKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts deleted file mode 100644 index 8970f2b163664..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/index.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*eslint-disable no-console*/ -/* eslint-disable import/no-extraneous-dependencies */ -import { SSM } from 'aws-sdk'; -import { CrossRegionExports, ExportWriterCRProps } from '../types'; - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { - const props: ExportWriterCRProps = event.ResourceProperties.WriterProps; - const exports = props.exports as CrossRegionExports; - - const ssm = new SSM({ region: props.region }); - try { - switch (event.RequestType) { - case 'Create': - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await throwIfAnyInUse(ssm, exports); - await putParameters(ssm, exports); - return; - case 'Update': - const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; - const oldExports = oldProps.exports as CrossRegionExports; - const newExports = except(exports, oldExports); - await throwIfAnyInUse(ssm, newExports); - console.info(`Creating new SSM Parameter exports in region ${props.region}`); - await putParameters(ssm, newExports); - return; - case 'Delete': - // consuming stack will delete parameters - return; - default: - return; - } - } catch (e) { - console.error('Error processing event: ', e); - throw e; - } -}; - -/** - * Create parameters for existing exports - */ -async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise { - await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { - return ssm.putParameter({ - Name: name, - Value: value, - Type: 'String', - }).promise(); - })); -} - -/** - * Query for existing parameters that are in use - */ -async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { - const tagResults: Map> = new Map(); - await Promise.all(Object.keys(parameters).map(async (name: string) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { - tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[2]) - : tagResults.set(name, new Set([tagParts[2]])); - } - }); - - } catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; - } - - })); - - if (tagResults.size > 0) { - const message: string = Object.entries(tagResults) - .map((result: [string, string[]]) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) - .join('\n'); - throw new Error(`Exports cannot be updated: \n${message}`); - } -} - -/** - * Return only the items from source that do not exist in the filter - * - * @param source the source object to perform the filter on - * @param filter filter out items that exist in this object - */ -function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { - return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) - .reduce((acc: CrossRegionExports, curr: string) => { - acc[curr] = source[curr]; - return acc; - }, {}); -} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/__entrypoint__.js similarity index 100% rename from packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03/__entrypoint__.js rename to packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/__entrypoint__.js diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js new file mode 100644 index 0000000000000..9f71f540e4994 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3/index.js @@ -0,0 +1,148 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +/*eslint-disable no-console*/ +/* eslint-disable import/no-extraneous-dependencies */ +const aws_sdk_1 = require("aws-sdk"); +async function handler(event) { + const props = event.ResourceProperties.WriterProps; + const exports = props.exports; + const ssm = new aws_sdk_1.SSM({ region: props.region }); + try { + switch (event.RequestType) { + case 'Create': + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await throwIfAnyInUse(ssm, exports); + await putParameters(ssm, exports); + return; + case 'Update': + const oldProps = event.OldResourceProperties.WriterProps; + const oldExports = oldProps.exports; + const newExports = except(exports, oldExports); + // throw an error to fail the deployment if any export value is changing + const changedExports = changed(oldExports, exports); + if (changedExports.length > 0) { + throw new Error('Some exports have changed!\n' + changedExports.join('\n')); + } + // if we are removing any exports that are in use, then throw an + // error to fail the deployment + const removedExports = except(oldExports, exports); + await throwIfAnyInUse(ssm, removedExports); + // if the ones we are removing are not in use then delete them + await ssm.deleteParameters({ + Names: Object.keys(removedExports), + }).promise(); + // also throw an error if we are creating a new export that already exists for some reason + await throwIfAnyInUse(ssm, newExports); + console.info(`Creating new SSM Parameter exports in region ${props.region}`); + await putParameters(ssm, newExports); + return; + case 'Delete': + // if any of the exports are currently in use then throw an error to fail + // the stack deletion. + await throwIfAnyInUse(ssm, exports); + // if none are in use then delete all of them + await ssm.deleteParameters({ + Names: Object.keys(exports), + }).promise(); + return; + default: + return; + } + } + catch (e) { + console.error('Error processing event: ', e); + throw e; + } +} +exports.handler = handler; +; +/** + * Create parameters for existing exports + */ +async function putParameters(ssm, parameters) { + await Promise.all(Array.from(Object.entries(parameters), ([name, value]) => { + return ssm.putParameter({ + Name: name, + Value: value, + Type: 'String', + }).promise(); + })); +} +/** + * Query for existing parameters that are in use + */ +async function throwIfAnyInUse(ssm, parameters) { + const tagResults = new Map(); + await Promise.all(Object.keys(parameters).map(async (name) => { + const result = await isInUse(ssm, name); + if (result.size > 0) { + tagResults.set(name, result); + } + })); + if (tagResults.size > 0) { + const message = Object.entries(tagResults) + .map((result) => `${result[0]} is in use by stack(s) ${result[1].join(' ')}`) + .join('\n'); + throw new Error(`Exports cannot be updated: \n${message}`); + } +} +/** + * Check if a parameter is in use + */ +async function isInUse(ssm, parameterName) { + const tagResults = new Set(); + try { + const result = await ssm.listTagsForResource({ + ResourceId: parameterName, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { + tagResults.add(tagParts[2]); + } + }); + } + catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return new Set(); + } + throw e; + } + return tagResults; +} +/** + * Return only the items from source that do not exist in the filter + * + * @param source the source object to perform the filter on + * @param filter filter out items that exist in this object + * @returns any exports that don't exist in the filter + */ +function except(source, filter) { + return Object.keys(source) + .filter(key => (!filter.hasOwnProperty(key))) + .reduce((acc, curr) => { + acc[curr] = source[curr]; + return acc; + }, {}); +} +/** + * Return items that exist in both the the old parameters and the new parameters, + * but have different values + * + * @param oldParams the exports that existed previous to this execution + * @param newParams the exports for the current execution + * @returns any parameters that have different values + */ +function changed(oldParams, newParams) { + return Object.keys(oldParams) + .filter(key => (newParams.hasOwnProperty(key) && oldParams[key] !== newParams[key])) + .reduce((acc, curr) => { + acc.push(curr); + return acc; + }, []); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0Isc0RBQXNEO0FBQ3RELHFDQUE4QjtBQUd2QixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtEO0lBQzlFLE1BQU0sS0FBSyxHQUF3QixLQUFLLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDO0lBQ3hFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUE2QixDQUFDO0lBRXBELE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxDQUFDLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLElBQUk7UUFDRixRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7WUFDekIsS0FBSyxRQUFRO2dCQUNYLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0RBQWdELEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUM3RSxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7Z0JBQ3BDLE1BQU0sYUFBYSxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDbEMsT0FBTztZQUNULEtBQUssUUFBUTtnQkFDWCxNQUFNLFFBQVEsR0FBd0IsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQztnQkFDOUUsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLE9BQTZCLENBQUM7Z0JBQzFELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBRS9DLHdFQUF3RTtnQkFDeEUsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDcEQsSUFBSSxjQUFjLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtvQkFDN0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsR0FBRSxjQUFjLENBQUMs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Q0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztTQUM1QyxNQUFNLENBQUMsQ0FBQyxHQUF1QixFQUFFLElBQVksRUFBRSxFQUFFO1FBQ2hELEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDWCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILFNBQVMsT0FBTyxDQUFDLFNBQTZCLEVBQUUsU0FBNkI7SUFDM0UsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQztTQUMxQixNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1NBQ25GLE1BQU0sQ0FBQyxDQUFDLEdBQWEsRUFBRSxJQUFZLEVBQUUsRUFBRTtRQUN0QyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2YsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDWCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyplc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlKi9cbi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuaW1wb3J0IHsgU1NNIH0gZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgeyBDcm9zc1JlZ2lvbkV4cG9ydHMsIEV4cG9ydFdyaXRlckNSUHJvcHMgfSBmcm9tICcuLi90eXBlcyc7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGNvbnN0IHByb3BzOiBFeHBvcnRXcml0ZXJDUlByb3BzID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLldyaXRlclByb3BzO1xuICBjb25zdCBleHBvcnRzID0gcHJvcHMuZXhwb3J0cyBhcyBDcm9zc1JlZ2lvbkV4cG9ydHM7XG5cbiAgY29uc3Qgc3NtID0gbmV3IFNTTSh7IHJlZ2lvbjogcHJvcHMucmVnaW9uIH0pO1xuICB0cnkge1xuICAgIHN3aXRjaCAoZXZlbnQuUmVxdWVzdFR5cGUpIHtcbiAgICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICAgIGNvbnNvbGUuaW5mbyhgQ3JlYXRpbmcgbmV3IFNTTSBQYXJhbWV0ZXIgZXhwb3J0cyBpbiByZWdpb24gJHtwcm9wcy5yZWdpb259YCk7XG4gICAgICAgIGF3YWl0IHRocm93SWZBbnlJblVzZShzc20sIGV4cG9ydHMpO1xuICAgICAgICBhd2FpdCBwdXRQYXJhbWV0ZXJzKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgJ1VwZGF0ZSc6XG4gICAgICAgIGNvbnN0IG9sZFByb3BzOiBFeHBvcnRXcml0ZXJDUlByb3BzID0gZXZlbnQuT2xkUmVzb3VyY2VQcm9wZXJ0aWVzLldyaXRlclByb3BzO1xuICAgICAgICBjb25zdCBvbGRFeHBvcnRzID0gb2xkUHJvcHMuZXhwb3J0cyBhcyBDcm9zc1JlZ2lvbkV4cG9ydHM7XG4gICAgICAgIGNvbnN0IG5ld0V4cG9ydHMgPSBleGNlcHQoZXhwb3J0cywgb2xkRXhwb3J0cyk7XG5cbiAgICAgICAgLy8gdGhyb3cgYW4gZXJyb3IgdG8gZmFpbCB0aGUgZGVwbG95bWVudCBpZiBhbnkgZXhwb3J0IHZhbHVlIGlzIGNoYW5naW5nXG4gICAgICAgIGNvbnN0IGNoYW5nZWRFeHBvcnRzID0gY2hhbmdlZChvbGRFeHBvcnRzLCBleHBvcnRzKTtcbiAgICAgICAgaWYgKGNoYW5nZWRFeHBvcnRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NvbWUgZXhwb3J0cyBoYXZlIGNoYW5nZWQhXFxuJysgY2hhbmdlZEV4cG9ydHMuam9pbignXFxuJykpO1xuICAgICAgICB9XG4gICAgICAgIC8vIGlmIHdlIGFyZSByZW1vdmluZyBhbnkgZXhwb3J0cyB0aGF0IGFyZSBpbiB1c2UsIHRoZW4gdGhyb3cgYW5cbiAgICAgICAgLy8gZXJyb3IgdG8gZmFpbCB0aGUgZGVwbG95bWVudFxuICAgICAgICBjb25zdCByZW1vdmVkRXhwb3J0cyA9IGV4Y2VwdChvbGRFeHBvcnRzLCBleHBvcnRzKTtcbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgcmVtb3ZlZEV4cG9ydHMpO1xuICAgICAgICAvLyBpZiB0aGUgb25lcyB3ZSBhcmUgcmVtb3ZpbmcgYXJlIG5vdCBpbiB1c2UgdGhlbiBkZWxldGUgdGhlbVxuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IE9iamVjdC5rZXlzKHJlbW92ZWRFeHBvcnRzKSxcbiAgICAgICAgfSkucHJvbWlzZSgpO1xuXG4gICAgICAgIC8vIGFsc28gdGhyb3cgYW4gZXJyb3IgaWYgd2UgYXJlIGNyZWF0aW5nIGEgbmV3IGV4cG9ydCB0aGF0IGFscmVhZHkgZXhpc3RzIGZvciBzb21lIHJlYXNvblxuICAgICAgICBhd2FpdCB0aHJvd0lmQW55SW5Vc2Uoc3NtLCBuZXdFeHBvcnRzKTtcbiAgICAgICAgY29uc29sZS5pbmZvKGBDcmVhdGluZyBuZXcgU1NNIFBhcmFtZXRlciBleHBvcnRzIGluIHJlZ2lvbiAke3Byb3BzLnJlZ2lvbn1gKTtcbiAgICAgICAgYXdhaXQgcHV0UGFyYW1ldGVycyhzc20sIG5ld0V4cG9ydHMpO1xuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlICdEZWxldGUnOlxuICAgICAgICAvLyBpZiBhbnkgb2YgdGhlIGV4cG9ydHMgYXJlIGN1cnJlbnRseSBpbiB1c2UgdGhlbiB0aHJvdyBhbiBlcnJvciB0byBmYWlsXG4gICAgICAgIC8vIHRoZSBzdGFjayBkZWxldGlvbi5cbiAgICAgICAgYXdhaXQgdGhyb3dJZkFueUluVXNlKHNzbSwgZXhwb3J0cyk7XG4gICAgICAgIC8vIGlmIG5vbmUgYXJlIGluIHVzZSB0aGVuIGRlbGV0ZSBhbGwgb2YgdGhlbVxuICAgICAgICBhd2FpdCBzc20uZGVsZXRlUGFyYW1ldGVycyh7XG4gICAgICAgICAgTmFtZXM6IE9iamVjdC5rZXlzKGV4cG9ydHMpLFxuICAgICAgICB9KS5wcm9taXNlKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmVycm9yKCdFcnJvciBwcm9jZXNzaW5nIGV2ZW50OiAnLCBlKTtcbiAgICB0aHJvdyBlO1xuICB9XG59O1xuXG4vKipcbiAqIENyZWF0ZSBwYXJhbWV0ZXJzIGZvciBleGlzdGluZyBleHBvcnRzXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHB1dFBhcmFtZXRlcnMoc3NtOiBTU00sIHBhcmFtZXRlcnM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBQcm9taXNlLmFsbChBcnJheS5mcm9tKE9iamVjdC5lbnRyaWVzKHBhcmFtZXRlcnMpLCAoW25hbWUsIHZhbHVlXSkgPT4ge1xuICAgIHJldHVybiBzc20ucHV0UGFyYW1ldGVyKHtcbiAgICAgIE5hbWU6IG5hbWUsXG4gICAgICBWYWx1ZTogdmFsdWUsXG4gICAgICBUeXBlOiAnU3RyaW5nJyxcbiAgICB9KS5wcm9taXNlKCk7XG4gIH0pKTtcbn1cblxuLyoqXG4gKiBRdWVyeSBmb3IgZXhpc3RpbmcgcGFyYW1ldGVycyB0aGF0IGFyZSBpbiB1c2VcbiAqL1xuYXN5bmMgZnVuY3Rpb24gdGhyb3dJZkFueUluVXNlKHNzbTogU1NNLCBwYXJhbWV0ZXJzOiBDcm9zc1JlZ2lvbkV4cG9ydHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgdGFnUmVzdWx0czogTWFwPHN0cmluZywgU2V0PHN0cmluZz4+ID0gbmV3IE1hcCgpO1xuICBhd2FpdCBQcm9taXNlLmFsbChPYmplY3Qua2V5cyhwYXJhbWV0ZXJzKS5tYXAoYXN5bmMgKG5hbWU6IHN0cmluZykgPT4ge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGlzSW5Vc2Uoc3NtLCBuYW1lKTtcbiAgICBpZiAocmVzdWx0LnNpemUgPiAwKSB7XG4gICAgICB0YWdSZXN1bHRzLnNldChuYW1lLCByZXN1bHQpO1xuICAgIH1cbiAgfSkpO1xuXG4gIGlmICh0YWdSZXN1bHRzLnNpemUgPiAwKSB7XG4gICAgY29uc3QgbWVzc2FnZTogc3RyaW5nID0gT2JqZWN0LmVudHJpZXModGFnUmVzdWx0cylcbiAgICAgIC5tYXAoKHJlc3VsdDogW3N0cmluZywgc3RyaW5nW11dKSA9PiBgJHtyZXN1bHRbMF19IGlzIGluIHVzZSBieSBzdGFjayhzKSAke3Jlc3VsdFsxXS5qb2luKCcgJyl9YClcbiAgICAgIC5qb2luKCdcXG4nKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEV4cG9ydHMgY2Fubm90IGJlIHVwZGF0ZWQ6IFxcbiR7bWVzc2FnZX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIENoZWNrIGlmIGEgcGFyYW1ldGVyIGlzIGluIHVzZVxuICovXG5hc3luYyBmdW5jdGlvbiBpc0luVXNlKHNzbTogU1NNLCBwYXJhbWV0ZXJOYW1lOiBzdHJpbmcpOiBQcm9taXNlPFNldDxzdHJpbmc+PiB7XG4gIGNvbnN0IHRhZ1Jlc3VsdHM6IFNldDxzdHJpbmc+ID0gbmV3IFNldCgpO1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHNzbS5saXN0VGFnc0ZvclJlc291cmNlKHtcbiAgICAgIFJlc291cmNlSWQ6IHBhcmFtZXRlck5hbWUsXG4gICAgICBSZXNvdXJjZVR5cGU6ICdQYXJhbWV0ZXInLFxuICAgIH0pLnByb21pc2UoKTtcbiAgICByZXN1bHQuVGFnTGlzdD8uZm9yRWFjaCh0YWcgPT4ge1xuICAgICAgY29uc3QgdGFnUGFydHMgPSB0YWcuS2V5LnNwbGl0KCc6Jyk7XG4gICAgICBpZiAodGFnUGFydHNbMF0gPT09ICdhd3MtY2RrJyAmJiB0YWdQYXJ0c1sxXSA9PT0gJ3N0cm9uZy1yZWYnKSB7XG4gICAgICAgIHRhZ1Jlc3VsdHMuYWRkKHRhZ1BhcnRzWzJdKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIGFuIEludmFsaWRSZXNvdXJjZUlkIG1lYW5zIHRoYXQgdGhlIHBhcmFtZXRlciBkb2Vzbid0IGV4aXN0XG4gICAgLy8gd2hpY2ggd2Ugc2hvdWxkIGlnbm9yZSBzaW5jZSB0aGF0IG1lYW5zIGl0J3Mgbm90IGluIHVzZVxuICAgIGlmIChlLmNvZGUgPT09ICdJbnZhbGlkUmVzb3VyY2VJZCcpIHtcbiAgICAgIHJldHVybiBuZXcgU2V0KCk7XG4gICAgfVxuICAgIHRocm93IGU7XG4gIH1cbiAgcmV0dXJuIHRhZ1Jlc3VsdHM7XG59XG5cbi8qKlxuICogUmV0dXJuIG9ubHkgdGhlIGl0ZW1zIGZyb20gc291cmNlIHRoYXQgZG8gbm90IGV4aXN0IGluIHRoZSBmaWx0ZXJcbiAqXG4gKiBAcGFyYW0gc291cmNlIHRoZSBzb3VyY2Ugb2JqZWN0IHRvIHBlcmZvcm0gdGhlIGZpbHRlciBvblxuICogQHBhcmFtIGZpbHRlciBmaWx0ZXIgb3V0IGl0ZW1zIHRoYXQgZXhpc3QgaW4gdGhpcyBvYmplY3RcbiAqIEByZXR1cm5zIGFueSBleHBvcnRzIHRoYXQgZG9uJ3QgZXhpc3QgaW4gdGhlIGZpbHRlclxuICovXG5mdW5jdGlvbiBleGNlcHQoc291cmNlOiBDcm9zc1JlZ2lvbkV4cG9ydHMsIGZpbHRlcjogQ3Jvc3NSZWdpb25FeHBvcnRzKTogQ3Jvc3NSZWdpb25FeHBvcnRzIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKHNvdXJjZSlcbiAgICAuZmlsdGVyKGtleSA9PiAoIWZpbHRlci5oYXNPd25Qcm9wZXJ0eShrZXkpKSlcbiAgICAucmVkdWNlKChhY2M6IENyb3NzUmVnaW9uRXhwb3J0cywgY3Vycjogc3RyaW5nKSA9PiB7XG4gICAgICBhY2NbY3Vycl0gPSBzb3VyY2VbY3Vycl07XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9KTtcbn1cblxuLyoqXG4gKiBSZXR1cm4gaXRlbXMgdGhhdCBleGlzdCBpbiBib3RoIHRoZSB0aGUgb2xkIHBhcmFtZXRlcnMgYW5kIHRoZSBuZXcgcGFyYW1ldGVycyxcbiAqIGJ1dCBoYXZlIGRpZmZlcmVudCB2YWx1ZXNcbiAqXG4gKiBAcGFyYW0gb2xkUGFyYW1zIHRoZSBleHBvcnRzIHRoYXQgZXhpc3RlZCBwcmV2aW91cyB0byB0aGlzIGV4ZWN1dGlvblxuICogQHBhcmFtIG5ld1BhcmFtcyB0aGUgZXhwb3J0cyBmb3IgdGhlIGN1cnJlbnQgZXhlY3V0aW9uXG4gKiBAcmV0dXJucyBhbnkgcGFyYW1ldGVycyB0aGF0IGhhdmUgZGlmZmVyZW50IHZhbHVlc1xuICovXG5mdW5jdGlvbiBjaGFuZ2VkKG9sZFBhcmFtczogQ3Jvc3NSZWdpb25FeHBvcnRzLCBuZXdQYXJhbXM6IENyb3NzUmVnaW9uRXhwb3J0cyk6IHN0cmluZ1tdIHtcbiAgcmV0dXJuIE9iamVjdC5rZXlzKG9sZFBhcmFtcylcbiAgICAuZmlsdGVyKGtleSA9PiAobmV3UGFyYW1zLmhhc093blByb3BlcnR5KGtleSkgJiYgb2xkUGFyYW1zW2tleV0gIT09IG5ld1BhcmFtc1trZXldKSlcbiAgICAucmVkdWNlKChhY2M6IHN0cmluZ1tdLCBjdXJyOiBzdHJpbmcpID0+IHtcbiAgICAgIGFjYy5wdXNoKGN1cnIpO1xuICAgICAgcmV0dXJuIGFjYztcbiAgICB9LCBbXSk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json index 00b55c859608c..8469a55817166 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.assets.json @@ -1,35 +1,35 @@ { "version": "21.0.0", "files": { - "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d": { + "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c": { "source": { - "path": "asset.bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d", + "path": "asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c", "packaging": "zip" }, "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip", + "objectKey": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878": { + "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741": { "source": { - "path": "asset.92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878", + "path": "asset.4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741", "packaging": "zip" }, "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip", + "objectKey": "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741.zip", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } } }, - "2c37e7d81834902acece506443fca36aa859d1f164da38b0d0bfb11c94646293": { + "143ebf10248652fdf40e751a3fce16c5f63478c655577efc66530b02f4fb647a": { "source": { "path": "integ-pipeline-consumer-stack.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-2": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2", - "objectKey": "2c37e7d81834902acece506443fca36aa859d1f164da38b0d0bfb11c94646293.json", + "objectKey": "143ebf10248652fdf40e751a3fce16c5f63478c655577efc66530b02f4fb647a.json", "region": "us-east-2", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-2" } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json index d0539cf10a779..854af79e34bf1 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-consumer-stack.template.json @@ -302,10 +302,20 @@ { "ArtifactStore": { "EncryptionKey": { - "Id": "{{resolve:ssm:/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73}}", + "Id": { + "Fn::GetAtt": [ + "ExportsReader8B249524", + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73" + ] + }, "Type": "KMS" }, - "Location": "{{resolve:ssm:/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D}}", + "Location": { + "Fn::GetAtt": [ + "ExportsReader8B249524", + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D" + ] + }, "Type": "S3" }, "Region": "us-east-1" @@ -628,7 +638,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "S3Key": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip" + "S3Key": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.zip" }, "Timeout": 900, "MemorySize": 128, @@ -848,10 +858,10 @@ "ReaderProps": { "region": "us-east-2", "prefix": "integ-pipeline-consumer-stack", - "imports": [ - "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D", - "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73" - ] + "imports": { + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D": "{{resolve:ssm:/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1RefReplicationBucket70D68737DB32483D}}", + "/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73": "{{resolve:ssm:/cdk/exports/integ-pipeline-consumer-stack/integpipelineproducerstackuseast1FnGetAttReplicationKeyFCE40BF4ArnFA0E5A73}}" + } } }, "UpdateReplacePolicy": "Delete", @@ -898,10 +908,8 @@ ] }, "Action": [ - "ssm:DeleteParameters", "ssm:AddTagsToResource", "ssm:RemoveTagsFromResource", - "ssm:GetParametersByPath", "ssm:GetParameters" ] } @@ -918,7 +926,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2" }, - "S3Key": "92be7db38242a4b44c0a0f5a7150a6e1df03622d969fa0a5ef0c6cad6852b878.zip" + "S3Key": "4607a5d8b4a4167cb2ff3c986ec884e3eb44c22626b7bce07ac9807730147741.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json index 28aaa4fb4a65a..f3f2e22967613 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.assets.json @@ -1,35 +1,35 @@ { "version": "21.0.0", "files": { - "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d": { + "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c": { "source": { - "path": "asset.bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d", + "path": "asset.33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip", + "objectKey": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03": { + "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3": { "source": { - "path": "asset.1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03", + "path": "asset.f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3", "packaging": "zip" }, "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip", + "objectKey": "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3.zip", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } } }, - "c55f1e01cd2d11bf3e4c7117dbdd63890fbfe7bd129f38d29f96d88504a6c061": { + "0eb69c88f002c044720b8030b26cd2250aa898c42cf4749d5f3f8c125876fa9a": { "source": { "path": "integ-pipeline-producer-stack.template.json", "packaging": "file" @@ -37,7 +37,7 @@ "destinations": { "current_account-us-east-1": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1", - "objectKey": "c55f1e01cd2d11bf3e4c7117dbdd63890fbfe7bd129f38d29f96d88504a6c061.json", + "objectKey": "0eb69c88f002c044720b8030b26cd2250aa898c42cf4749d5f3f8c125876fa9a.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-us-east-1" } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json index 02b8c9b552fdb..09a244715f430 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/integ-pipeline-producer-stack.template.json @@ -158,7 +158,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "bb426cfb5fed5237e5928f871893b243ddf86a591a592b558bd29f60e28bad9d.zip" + "S3Key": "33e2651435a0d472a75c1e033c9832b21321d9e56711926b04c5705e5f63874c.zip" }, "Timeout": 900, "MemorySize": 128, @@ -255,6 +255,7 @@ ] }, "Action": [ + "ssm:DeleteParameters", "ssm:ListTagsForResource", "ssm:GetParameters", "ssm:PutParameter" @@ -273,7 +274,7 @@ "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1" }, - "S3Key": "1d845554a45d04080ed4ceefef342f0f4f40798a6d388f7f0ac7c8927557af03.zip" + "S3Key": "f86f86755c3b2013542fc4f9405bfe145a86c0bec508dd0b37baabe2055d33f3.zip" }, "Timeout": 900, "MemorySize": 128, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json index 321b2ea545264..c56c5756613f5 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/pipeline-with-replication.integ.snapshot/manifest.json @@ -17,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-1", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-1", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/c55f1e01cd2d11bf3e4c7117dbdd63890fbfe7bd129f38d29f96d88504a6c061.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-1/0eb69c88f002c044720b8030b26cd2250aa898c42cf4749d5f3f8c125876fa9a.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -118,7 +118,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-us-east-2", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-us-east-2", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/2c37e7d81834902acece506443fca36aa859d1f164da38b0d0bfb11c94646293.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-us-east-2/143ebf10248652fdf40e751a3fce16c5f63478c655577efc66530b02f4fb647a.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts index 7c92fde2bf1c7..3a92ad71ccfa8 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts @@ -1,11 +1,12 @@ /*eslint-disable no-console*/ /* eslint-disable import/no-extraneous-dependencies */ import { SSM } from 'aws-sdk'; -import { ExportReaderCRProps } from '../types'; +import { ExportReaderCRProps, CrossRegionExports } from '../types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { const props: ExportReaderCRProps = event.ResourceProperties.ReaderProps; - const imports: string[] = props.imports; + const imports: CrossRegionExports = props.imports as CrossRegionExports; + const importNames = Object.keys(imports); const keyName: string = `aws-cdk:strong-ref:${props.prefix}`; const ssm = new SSM({ region: props.region }); @@ -13,31 +14,33 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent switch (event.RequestType) { case 'Create': console.info('Tagging SSM Parameter imports'); - await addTags(ssm, imports, keyName); - return; + await addTags(ssm, importNames, keyName); + break; case 'Update': const oldProps: ExportReaderCRProps = event.OldResourceProperties.ReaderProps; - const oldExports: string[] = oldProps.imports; - const newExports = except(imports, oldExports); - const paramsToDelete = except(oldExports, imports); + const oldExports: CrossRegionExports = oldProps.imports as CrossRegionExports; + const newExports = except(importNames, Object.keys(oldExports)); + const paramsToRelease = except(Object.keys(oldExports), importNames); console.info('Releasing unused SSM Parameter imports'); - if (Object.keys(paramsToDelete).length > 0) { - await removeTags(ssm, paramsToDelete, keyName); + if (Object.keys(paramsToRelease).length > 0) { + await removeTags(ssm, paramsToRelease, keyName); } console.info('Tagging new SSM Parameter imports'); await addTags(ssm, newExports, keyName); - return; + break; case 'Delete': - console.info('Deleting all SSM Parameter exports'); - await deleteParametersByPath(ssm, `/cdk/exports/${props.prefix}/`); - return; - default: + console.info('Releasing all SSM Parameter exports by removing tags'); + await removeTags(ssm, importNames, keyName); return; } } catch (e) { console.error('Error importing cross region stack exports: ', e); throw e; } + console.info('returning imports: ', JSON.stringify(imports)); + return { + Data: imports, + }; }; /** @@ -83,36 +86,6 @@ async function removeTags(ssm: SSM, parameters: string[], keyName: string): Prom })); } -/** - * Get all parameters in a given path - * - * If the request fails for any reason it will fail the custom resource event. - * Since this is only run when the resource is deleted that is probably the behavior - * that is desired. - */ -async function getParametersByPath(ssm: SSM, path: string): Promise { - const parameters: SSM.Parameter[] = []; - let nextToken: string | undefined; - do { - const response = await ssm.getParametersByPath({ Path: path, NextToken: nextToken }).promise(); - parameters.push(...response.Parameters ?? []); - nextToken = response.NextToken; - - } while (nextToken); - return parameters; -} - -/** - * Delete all parameters in a give path - */ -async function deleteParametersByPath(ssm: SSM, path: string): Promise { - const allParams = await getParametersByPath(ssm, path); - const names = allParams.map(param => param.Name).filter(x => !!x) as string[]; - await ssm.deleteParameters({ - Names: names, - }).promise(); -} - /** * Return only the items from source that do not exist in the filter * diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler/index.ts index 8970f2b163664..84d0e4fe679b1 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-writer-handler/index.ts @@ -19,12 +19,34 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent const oldProps: ExportWriterCRProps = event.OldResourceProperties.WriterProps; const oldExports = oldProps.exports as CrossRegionExports; const newExports = except(exports, oldExports); + + // throw an error to fail the deployment if any export value is changing + const changedExports = changed(oldExports, exports); + if (changedExports.length > 0) { + throw new Error('Some exports have changed!\n'+ changedExports.join('\n')); + } + // if we are removing any exports that are in use, then throw an + // error to fail the deployment + const removedExports = except(oldExports, exports); + await throwIfAnyInUse(ssm, removedExports); + // if the ones we are removing are not in use then delete them + await ssm.deleteParameters({ + Names: Object.keys(removedExports), + }).promise(); + + // also throw an error if we are creating a new export that already exists for some reason await throwIfAnyInUse(ssm, newExports); console.info(`Creating new SSM Parameter exports in region ${props.region}`); await putParameters(ssm, newExports); return; case 'Delete': - // consuming stack will delete parameters + // if any of the exports are currently in use then throw an error to fail + // the stack deletion. + await throwIfAnyInUse(ssm, exports); + // if none are in use then delete all of them + await ssm.deleteParameters({ + Names: Object.keys(exports), + }).promise(); return; default: return; @@ -54,29 +76,10 @@ async function putParameters(ssm: SSM, parameters: CrossRegionExports): Promise< async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promise { const tagResults: Map> = new Map(); await Promise.all(Object.keys(parameters).map(async (name: string) => { - try { - const result = await ssm.listTagsForResource({ - ResourceId: name, - ResourceType: 'Parameter', - }).promise(); - result.TagList?.forEach(tag => { - const tagParts = tag.Key.split(':'); - if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { - tagResults.has(name) - ? tagResults.get(name)!.add(tagParts[2]) - : tagResults.set(name, new Set([tagParts[2]])); - } - }); - - } catch (e) { - // an InvalidResourceId means that the parameter doesn't exist - // which we should ignore since that means it's not in use - if (e.code === 'InvalidResourceId') { - return; - } - throw e; + const result = await isInUse(ssm, name); + if (result.size > 0) { + tagResults.set(name, result); } - })); if (tagResults.size > 0) { @@ -87,17 +90,62 @@ async function throwIfAnyInUse(ssm: SSM, parameters: CrossRegionExports): Promis } } +/** + * Check if a parameter is in use + */ +async function isInUse(ssm: SSM, parameterName: string): Promise> { + const tagResults: Set = new Set(); + try { + const result = await ssm.listTagsForResource({ + ResourceId: parameterName, + ResourceType: 'Parameter', + }).promise(); + result.TagList?.forEach(tag => { + const tagParts = tag.Key.split(':'); + if (tagParts[0] === 'aws-cdk' && tagParts[1] === 'strong-ref') { + tagResults.add(tagParts[2]); + } + }); + } catch (e) { + // an InvalidResourceId means that the parameter doesn't exist + // which we should ignore since that means it's not in use + if (e.code === 'InvalidResourceId') { + return new Set(); + } + throw e; + } + return tagResults; +} + /** * Return only the items from source that do not exist in the filter * * @param source the source object to perform the filter on * @param filter filter out items that exist in this object + * @returns any exports that don't exist in the filter */ function except(source: CrossRegionExports, filter: CrossRegionExports): CrossRegionExports { return Object.keys(source) - .filter(key => (!filter.hasOwnProperty(key) || source[key] !== filter[key])) + .filter(key => (!filter.hasOwnProperty(key))) .reduce((acc: CrossRegionExports, curr: string) => { acc[curr] = source[curr]; return acc; }, {}); } + +/** + * Return items that exist in both the the old parameters and the new parameters, + * but have different values + * + * @param oldParams the exports that existed previous to this execution + * @param newParams the exports for the current execution + * @returns any parameters that have different values + */ +function changed(oldParams: CrossRegionExports, newParams: CrossRegionExports): string[] { + return Object.keys(oldParams) + .filter(key => (newParams.hasOwnProperty(key) && oldParams[key] !== newParams[key])) + .reduce((acc: string[], curr: string) => { + acc.push(curr); + return acc; + }, []); +} diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts index a6389b28c0c1d..76653660c41f3 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-reader-provider.ts @@ -3,9 +3,10 @@ import { Construct } from 'constructs'; import { CfnResource } from '../../cfn-resource'; import { CustomResource } from '../../custom-resource'; import { Lazy } from '../../lazy'; +import { Intrinsic } from '../../private/intrinsic'; import { Stack } from '../../stack'; import { CustomResourceProvider, CustomResourceProviderRuntime } from '../custom-resource-provider'; -import { SSM_EXPORT_PATH_PREFIX, ExportReaderCRProps } from './types'; +import { SSM_EXPORT_PATH_PREFIX, ExportReaderCRProps, CrossRegionExports } from './types'; /** @@ -28,7 +29,7 @@ export class ExportReader extends Construct { : new ExportReader(stack, uniqueId); } - private readonly importParametersNames: string[] = []; + private readonly importParameters: CrossRegionExports = {}; private readonly customResource: CustomResource; constructor(scope: Construct, id: string, _props: ExportReaderProps = {}) { super(scope, id); @@ -46,10 +47,8 @@ export class ExportReader extends Construct { resourceName: `${SSM_EXPORT_PATH_PREFIX}${stack.stackName}/*`, }), Action: [ - 'ssm:DeleteParameters', 'ssm:AddTagsToResource', 'ssm:RemoveTagsFromResource', - 'ssm:GetParametersByPath', 'ssm:GetParameters', ], }], @@ -58,7 +57,7 @@ export class ExportReader extends Construct { const properties: ExportReaderCRProps = { region: stack.region, prefix: stack.stackName, - imports: Lazy.list({ produce: () => this.importParametersNames }), + imports: Lazy.any({ produce: () => this.importParameters }), }; this.customResource = new CustomResource(this, 'Resource', { resourceType: resourceType, @@ -86,9 +85,10 @@ export class ExportReader extends Construct { * the export by creating an SSM parameter in the region that the consuming * stack is created. * - * @param exportName the unique name associated with the export + * @param exports map of unique name associated with the export to SSM Dynamic reference */ - public importValue(exportName: string): void { - this.importParametersNames.push(exportName); + public importValue(name: string, value: Intrinsic): Intrinsic { + this.importParameters[name] = value.toString(); + return this.customResource.getAtt(name); } } diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts index 6b2fa661e4dff..a83a7a178c05c 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/export-writer-provider.ts @@ -72,6 +72,7 @@ export class ExportWriter extends Construct { resourceName: `${SSM_EXPORT_PATH_PREFIX}*`, }), Action: [ + 'ssm:DeleteParameters', 'ssm:ListTagsForResource', 'ssm:GetParameters', 'ssm:PutParameter', @@ -101,32 +102,25 @@ export class ExportWriter extends Construct { * * @param exportName the unique name associated with the export * @param reference the value that will be exported - * @returns a dynamic reference to an ssm parameter + * @returns a reference to the reader custom resource */ public exportValue(exportName: string, reference: Reference, importStack: Stack): Intrinsic { const stack = Stack.of(this); const parameterName = `/${SSM_EXPORT_PATH_PREFIX}${exportName}`; - this.addToExportReader(parameterName, importStack); + const ref = new CfnDynamicReference(CfnDynamicReferenceService.SSM, parameterName); this._references[parameterName] = stack.resolve(reference.toString()); - return new CfnDynamicReference(CfnDynamicReferenceService.SSM, parameterName); + return this.addToExportReader(parameterName, ref, importStack); } /** * Add the export to the export reader which is created in the importing stack */ - private addToExportReader(exportName: string, importStack: Stack): void { + private addToExportReader(exportName: string, exportValueRef: Intrinsic, importStack: Stack): Intrinsic { const readerConstructName = makeUniqueId(['ExportsReader']); const exportReader = ExportReader.getOrCreate(importStack.nestedStackParent ?? importStack, readerConstructName); - // if the reference is being imported into a nested stack we create the export reader - // in the parent stack and then add a dependency on the nested stack - // this ensures that the nested stack deploys and consumes the reference before - // the ExportReader is executed - if (importStack.nestedStackResource) { - exportReader.addDependency(importStack.nestedStackResource); - } - exportReader.importValue(exportName); + return exportReader.importValue(exportName, exportValueRef); } } diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts index b097da1cc8999..01d64b00f06fd 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/types.ts @@ -30,7 +30,7 @@ export interface ExportReaderCRProps { * A list of imports used by this stack. * Will be a list of parameter names */ - readonly imports: string[]; + readonly imports: CrossRegionExports | IResolvable; } /** diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts index 35d563a83447b..c394ec3959cef 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/custom-resource-provider.ts @@ -209,7 +209,7 @@ export class CustomResourceProvider extends Construct { } const stagingDirectory = FileSystem.mkdtemp('cdk-custom-resource'); - fse.copySync(props.codeDirectory, stagingDirectory); + fse.copySync(props.codeDirectory, stagingDirectory, { filter: (src, _dest) => !src.endsWith('.ts') }); fs.copyFileSync(ENTRYPOINT_NODEJS_SOURCE, path.join(stagingDirectory, `${ENTRYPOINT_FILENAME}.js`)); const staging = new AssetStaging(this, 'Staging', { @@ -349,4 +349,4 @@ function customResourceProviderRuntimeToString(x: CustomResourceProviderRuntime) case CustomResourceProviderRuntime.NODEJS_16_X: return 'nodejs16.x'; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index b61fc5f5654d2..2a4c605404078 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -223,7 +223,11 @@ function createCrossRegionImportValue(reference: Reference, importStack: Stack): region: importStack.region, }); - return exportReader.exportValue(exportName, reference, importStack); + const exported = exportReader.exportValue(exportName, reference, importStack); + if (importStack.nestedStackParent) { + return createNestedStackParameter(importStack, (exported as CfnReference), exported); + } + return exported; } /** diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index 7113fa842d2d2..0ed60d779b4ca 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -240,7 +240,12 @@ describe('cross environment', () => { }); expect(template2?.Outputs).toEqual({ 'Output': { - 'Value': '{{resolve:ssm:/cdk/exports/Stack2/Stack1bermudatriangle1337RefMyResource6073B41F66B72887}}', + 'Value': { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack1bermudatriangle1337RefMyResource6073B41F66B72887', + ], + }, }, }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts index 9785f957312e4..cdd021a54a9f6 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-reader-handler.test.ts @@ -9,11 +9,6 @@ jest.mock('aws-sdk', () => { return { SSM: jest.fn(() => { return { - deleteParameters: jest.fn((params) => { - return { - promise: () => mockDeleteParameters(params), - }; - }), addTagsToResource: jest.fn((params) => { return { promise: () => mockAddTagsToResource(params), @@ -56,7 +51,9 @@ describe('cross-region-ssm-reader entrypoint', () => { ReaderProps: { region: 'us-east-1', prefix: 'MyStack', - imports: ['/cdk/exports/MyStack/MyExport'], + imports: { + '/cdk/exports/MyStack/MyExport': 'abc', + }, }, ServiceToken: '', }, @@ -85,9 +82,9 @@ describe('cross-region-ssm-reader entrypoint', () => { ReaderProps: { region: 'us-east-1', prefix: 'MyStack', - imports: [ - '/cdk/exports/MyStack/ExistingExport', - ], + imports: { + '/cdk/exports/MyStack/ExistingExport': 'abc', + }, }, ServiceToken: '', }, @@ -95,10 +92,10 @@ describe('cross-region-ssm-reader entrypoint', () => { ReaderProps: { r: 'us-east-1', prefix: 'MyStack', - imports: [ - '/cdk/exports/MyStack/ExistingExport', - '/cdk/exports/MyStack/MyExport', - ], + imports: { + '/cdk/exports/MyStack/ExistingExport': 'abc', + '/cdk/exports/MyStack/MyExport': 'xyz', + }, }, ServiceToken: '', }, @@ -129,9 +126,9 @@ describe('cross-region-ssm-reader entrypoint', () => { ReaderProps: { region: 'us-east-1', prefix: 'MyStack', - imports: [ - '/cdk/exports/MyStack/RemovedExport', - ], + imports: { + '/cdk/exports/MyStack/RemovedExport': 'abc', + }, }, ServiceToken: '', }, @@ -140,9 +137,9 @@ describe('cross-region-ssm-reader entrypoint', () => { ReaderProps: { region: 'us-east-1', prefix: 'MyStack', - imports: [ - '/cdk/exports/MyStack/MyExport', - ], + imports: { + '/cdk/exports/MyStack/MyExport': 'abc', + }, }, }, }); @@ -176,9 +173,9 @@ describe('cross-region-ssm-reader entrypoint', () => { ReaderProps: { region: 'us-east-1', prefix: 'MyStack', - imports: [ - '/cdk/exports/MyStack/RemovedExport', - ], + imports: { + '/cdk/exports/MyStack/RemovedExport': 'abc', + }, }, }, }); @@ -194,9 +191,11 @@ describe('cross-region-ssm-reader entrypoint', () => { await handler(event); // THEN - expect(mockDeleteParameters).toHaveBeenCalledTimes(1); - expect(mockDeleteParameters).toHaveBeenCalledWith({ - Names: ['/cdk/exports/MyStack/OtherExport'], + expect(mockRemoveTagsFromResource).toHaveBeenCalledTimes(1); + expect(mockRemoveTagsFromResource).toHaveBeenCalledWith({ + ResourceType: 'Parameter', + ResourceId: '/cdk/exports/MyStack/RemovedExport', + TagKeys: ['aws-cdk:strong-ref:MyStack'], }); }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts index e78bb47018dc3..17ee6b3c26a31 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/cross-region-ssm-writer-handler.test.ts @@ -3,6 +3,7 @@ import { SSM_EXPORT_PATH_PREFIX } from '../../lib/custom-resource-provider/cross let mockPutParameter: jest.Mock ; let mocklistTagsForResource: jest.Mock; +let mockDeleteParameters: jest.Mock; jest.mock('aws-sdk', () => { return { SSM: jest.fn(() => { @@ -17,6 +18,11 @@ jest.mock('aws-sdk', () => { promise: () => mocklistTagsForResource(params), }; }), + deleteParameters: jest.fn((params) => { + return { + promise: () => mockDeleteParameters(params), + }; + }), }; }), }; @@ -25,6 +31,9 @@ beforeEach(() => { jest.spyOn(console, 'info').mockImplementation(() => {}); jest.spyOn(console, 'error').mockImplementation(() => {}); mockPutParameter = jest.fn(); + mockDeleteParameters = jest.fn().mockImplementation(() => { + return {}; + }); mocklistTagsForResource = jest.fn().mockImplementation(() => { return {}; }); @@ -37,238 +46,363 @@ afterEach(() => { }); describe('cross-region-ssm-writer throws', () => { - test('create throws if params already exist', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Create', - ResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + +}); + +describe('cross-region-ssm-writer entrypoint', () => { + describe('create events', () => { + test('Create event', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, - }, + }); + + // WHEN + await handler(event); + + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, + Value: 'Value', + Type: 'String', + }); + expect(mockPutParameter).toHaveBeenCalledTimes(1); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); }); - // WHEN - mocklistTagsForResource.mockImplementation(() => { - return { - TagList: [{ - Key: 'aws-cdk:strong-ref:MyStack', - Value: 'true', - }], - }; + test('create throws if params already exist', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, + }, + }, + }); + + // WHEN + mocklistTagsForResource.mockImplementation(() => { + return { + TagList: [{ + Key: 'aws-cdk:strong-ref:MyStack', + Value: 'true', + }], + }; + }); + + // THEN + await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); }); - // THEN - await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); + test('Create event does not throw for new parameters', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Create', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, + }, + }, + }); + + // WHEN + mocklistTagsForResource.mockRejectedValue({ + code: 'InvalidResourceId', + }); + await handler(event); + + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, + Value: 'Value', + Type: 'String', + }); + expect(mockPutParameter).toHaveBeenCalledTimes(1); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); + }); }); - test('update throws if params already exist', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Update', - OldResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + describe('Update events', () => { + test('new export added', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + }, }, }, - }, - ResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/AlreadyExists': 'Value', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, - }, - }); + }); - // WHEN - mocklistTagsForResource.mockImplementation(() => { - return { - TagList: [{ - Key: 'aws-cdk:strong-ref:MyStack', - Value: 'true', - }], - }; - }); + // WHEN + await handler(event); - // THEN - await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); - }); + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, + Value: 'Value', + Type: 'String', + }); + expect(mockPutParameter).toHaveBeenCalledTimes(1); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); + }); - test('update throws if value changes for existing parameter', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Update', - OldResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/AlreadyExists': 'Original', + test('removed exports are deleted', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + '/cdk/exports/MyStack/RemovedExport': 'MyExistingValue', + }, }, }, - }, - ResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/MyExport': 'Value', - '/cdk/exports/MyStack/AlreadyExists': 'NewValue', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, - }, - }); + }); + + // WHEN + await handler(event); - // WHEN - mocklistTagsForResource.mockImplementation((params) => { - expect(params).toEqual({ - ResourceId: '/cdk/exports/MyStack/AlreadyExists', - ResourceType: 'Parameter', + // THEN + expect(mockPutParameter).toHaveBeenCalledWith({ + Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, + Value: 'Value', + Type: 'String', + }); + expect(mockPutParameter).toHaveBeenCalledTimes(1); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(2); + expect(mockDeleteParameters).toHaveBeenCalledTimes(1); + expect(mockDeleteParameters).toHaveBeenCalledWith({ + Names: ['/cdk/exports/MyStack/RemovedExport'], }); - return { - TagList: [{ - Key: 'aws-cdk:strong-ref:MyStack', - Value: 'true', - }], - }; }); - // THEN - await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); - }); -}); - -describe('cross-region-ssm-writer entrypoint', () => { - test('Create event', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Create', - ResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + test('update throws if params already exist', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, - }, - }); + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'Value', + }, + }, + }, + }); - // WHEN - await handler(event); + // WHEN + mocklistTagsForResource.mockImplementation(() => { + return { + TagList: [{ + Key: 'aws-cdk:strong-ref:MyStack', + Value: 'true', + }], + }; + }); - // THEN - expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, - Value: 'Value', - Type: 'String', + // THEN + await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); }); - expect(mockPutParameter).toHaveBeenCalledTimes(1); - expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); - }); - test('Create event does not throw for new parameters', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Create', - ResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/MyExport': 'Value', + test('update throws if value changes for existing parameter', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'Original', + }, }, }, - }, - }); + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + '/cdk/exports/MyStack/AlreadyExists': 'NewValue', + }, + }, + }, + }); - // WHEN - mocklistTagsForResource.mockRejectedValue({ - code: 'InvalidResourceId', - }); - await handler(event); + // WHEN + mocklistTagsForResource.mockImplementation((params) => { + expect(params).toEqual({ + ResourceId: '/cdk/exports/MyStack/AlreadyExists', + ResourceType: 'Parameter', + }); + return { + TagList: [{ + Key: 'aws-cdk:strong-ref:MyStack', + Value: 'true', + }], + }; + }); - // THEN - expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, - Value: 'Value', - Type: 'String', + // THEN + await expect(handler(event)).rejects.toThrow(/Some exports have changed/); }); - expect(mockPutParameter).toHaveBeenCalledTimes(1); - expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); - }); - test('Update event', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Update', - OldResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', + test('update throws if in use param is deleted', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Update', + OldResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/MyExport': 'Value', + }, }, }, - }, - ResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/ExistingExport': 'MyExistingValue', - '/cdk/exports/MyStack/MyExport': 'Value', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/AlreadyExists': 'Value', + }, }, }, - }, - }); + }); - // WHEN - await handler(event); + // WHEN + mocklistTagsForResource.mockImplementation(() => { + return { + TagList: [{ + Key: 'aws-cdk:strong-ref:MyStack', + Value: 'true', + }], + }; + }); - // THEN - expect(mockPutParameter).toHaveBeenCalledWith({ - Name: `/${SSM_EXPORT_PATH_PREFIX}MyStack/MyExport`, - Value: 'Value', - Type: 'String', + // THEN + await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); }); - expect(mockPutParameter).toHaveBeenCalledTimes(1); - expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); }); - test('Delete event', async () => { - // GIVEN - const event = makeEvent({ - RequestType: 'Delete', - ResourceProperties: { - ServiceToken: '', - WriterProps: { - region: 'us-east-1', - exports: { - '/cdk/exports/MyStack/RemovedExport': 'RemovedValue', + describe('delete events', () => { + test('parameters are deleted', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Delete', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/RemovedExport': 'RemovedValue', + }, }, }, - }, + }); + + // WHEN + await handler(event); + + // THEN + expect(mockPutParameter).toHaveBeenCalledTimes(0); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); + expect(mockDeleteParameters).toHaveBeenCalledTimes(1); + expect(mockDeleteParameters).toHaveBeenCalledWith({ + Names: ['/cdk/exports/MyStack/RemovedExport'], + }); }); - // WHEN - await handler(event); + test('thorws if parameters are in use', async () => { + // GIVEN + const event = makeEvent({ + RequestType: 'Delete', + ResourceProperties: { + ServiceToken: '', + WriterProps: { + region: 'us-east-1', + exports: { + '/cdk/exports/MyStack/RemovedExport': 'RemovedValue', + }, + }, + }, + }); + + // WHEN + mocklistTagsForResource.mockImplementation(() => { + return { + TagList: [{ + Key: 'aws-cdk:strong-ref:MyStack', + Value: 'true', + }], + }; + }); - // THEN - expect(mockPutParameter).toHaveBeenCalledTimes(0); - expect(mocklistTagsForResource).toHaveBeenCalledTimes(0); + // THEN + await expect(handler(event)).rejects.toThrow(/Exports cannot be updated/); + expect(mockPutParameter).toHaveBeenCalledTimes(0); + expect(mocklistTagsForResource).toHaveBeenCalledTimes(1); + expect(mockDeleteParameters).toHaveBeenCalledTimes(0); + }); }); }); diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index b0282b0f1e884..54273f32d4a32 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -25,7 +25,12 @@ describe('export writer provider', () => { const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; const assetHash = staging.assetHash; - expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/MyResourceName}}'); + expect(stack.resolve(exportValue)).toEqual({ + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/MyResourceName', + ], + }); expect(cfn).toEqual({ Resources: { MyResource: { @@ -52,6 +57,7 @@ describe('export writer provider', () => { Statement: [ { Action: [ + 'ssm:DeleteParameters', 'ssm:ListTagsForResource', 'ssm:GetParameters', 'ssm:PutParameter', @@ -188,10 +194,8 @@ describe('export writer provider', () => { Statement: [ { Action: [ - 'ssm:DeleteParameters', 'ssm:AddTagsToResource', 'ssm:RemoveTagsFromResource', - 'ssm:GetParametersByPath', 'ssm:GetParameters', ], Effect: 'Allow', @@ -229,9 +233,9 @@ describe('export writer provider', () => { DeletionPolicy: 'Delete', Properties: { ReaderProps: { - imports: [ - '/cdk/exports/MyResourceName', - ], + imports: { + '/cdk/exports/MyResourceName': '{{resolve:ssm:/cdk/exports/MyResourceName}}', + }, region: { Ref: 'AWS::Region', }, @@ -273,7 +277,9 @@ describe('export writer provider', () => { const staging = stack.node.tryFindChild('Custom::CrossRegionExportWriterCustomResourceProvider')?.node.tryFindChild('Staging') as AssetStaging; const assetHash = staging.assetHash; - expect(stack.resolve(exportValue)).toEqual('{{resolve:ssm:/cdk/exports/MyResourceName}}'); + expect(stack.resolve(exportValue)).toEqual({ + 'Fn::GetAtt': ['ExportsReader8B249524', '/cdk/exports/MyResourceName'], + }); expect(cfn).toEqual({ Resources: { MyResource: { @@ -300,6 +306,7 @@ describe('export writer provider', () => { Statement: [ { Action: [ + 'ssm:DeleteParameters', 'ssm:ListTagsForResource', 'ssm:GetParameters', 'ssm:PutParameter', @@ -437,10 +444,8 @@ describe('export writer provider', () => { Statement: [ { Action: [ - 'ssm:DeleteParameters', 'ssm:AddTagsToResource', 'ssm:RemoveTagsFromResource', - 'ssm:GetParametersByPath', 'ssm:GetParameters', ], Effect: 'Allow', @@ -478,9 +483,9 @@ describe('export writer provider', () => { DeletionPolicy: 'Delete', Properties: { ReaderProps: { - imports: [ - '/cdk/exports/MyResourceName', - ], + imports: { + '/cdk/exports/MyResourceName': '{{resolve:ssm:/cdk/exports/MyResourceName}}', + }, region: { Ref: 'AWS::Region', }, diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 8ca9d890e4764..82df8e9eab41e 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -72,7 +72,12 @@ describe('nested-stack', () => { Resources: { Resource2: { Properties: { - Prop1: '{{resolve:ssm:/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E}}', + Prop1: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E', + ], + }, }, Type: 'My::Resource', }, @@ -84,9 +89,9 @@ describe('nested-stack', () => { DeletionPolicy: 'Delete', Properties: { ReaderProps: { - imports: [ - '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E', - ], + imports: { + '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E': '{{resolve:ssm:/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E}}', + }, region: 'bermuda-triangle-42', prefix: 'Stack2', }, diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 6f2fa0c2dfab0..a58b3db4bbb90 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -520,7 +520,12 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F}}', + Name: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F', + ], + }, }, }, }, @@ -589,10 +594,8 @@ describe('stack', () => { Statement: [ { Action: [ - 'ssm:DeleteParameters', 'ssm:AddTagsToResource', 'ssm:RemoveTagsFromResource', - 'ssm:GetParametersByPath', 'ssm:GetParameters', ], Effect: 'Allow', @@ -626,11 +629,11 @@ describe('stack', () => { DeletionPolicy: 'Delete', Properties: { ReaderProps: { - imports: [ - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F', - '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1', - '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B', - ], + imports: { + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F': '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F}}', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1': '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1}}', + '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B': '{{resolve:ssm:/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B}}', + }, region: 'us-east-2', prefix: 'Stack2', }, @@ -647,9 +650,24 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F}}', - Other: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B}}', + Name: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F', + ], + }, + Other: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1', + ], + }, + Other2: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack3useast1FnGetAttSomeResourceExportother2190A679B', + ], + }, }, }, }, @@ -763,9 +781,24 @@ describe('stack', () => { SomeResource: { Type: 'AWS::S3::Bucket', Properties: { - Name: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F}}', - Other: '{{resolve:ssm:/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1}}', - Other2: '{{resolve:ssm:/cdk/exports/Stack2/Stack3uswest1FnGetAttSomeResourceExportother2491B5DA7}}', + Name: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportname47AD304F', + ], + }, + Other: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack1useast1FnGetAttSomeResourceExportotherC6F8CBD1', + ], + }, + Other2: { + 'Fn::GetAtt': [ + 'ExportsReader8B249524', + '/cdk/exports/Stack2/Stack3uswest1FnGetAttSomeResourceExportother2491B5DA7', + ], + }, }, }, }, diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/waiter-state-machine.ts b/packages/@aws-cdk/integ-tests/lib/assertions/waiter-state-machine.ts index a2a63df342a7e..5b994265b6bc8 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/waiter-state-machine.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/waiter-state-machine.ts @@ -169,6 +169,10 @@ export class WaiterStateMachine extends Construct { * Calculate the max number of retries */ function calculateMaxRetries(maxSeconds: number, intervalSeconds: number, backoff: number): number { + // if backoff === 1 then we aren't really using backoff + if (backoff === 1) { + return Math.floor(maxSeconds / intervalSeconds); + } let retries = 1; let nextInterval = intervalSeconds; let i = 0; From 9e1c2dc3b9ef507f9896a3e00b207d114f38ace6 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 20 Oct 2022 19:41:38 +0000 Subject: [PATCH 28/30] fixing tests --- .../cross-region-ssm-reader-handler/index.ts | 1 - .../export-writer-provider.test.ts | 3 --- packages/@aws-cdk/core/test/nested-stack.test.ts | 8 +------- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts index 3a92ad71ccfa8..4a9535d804e88 100644 --- a/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts +++ b/packages/@aws-cdk/core/lib/custom-resource-provider/cross-region-export-providers/cross-region-ssm-reader-handler/index.ts @@ -37,7 +37,6 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent console.error('Error importing cross region stack exports: ', e); throw e; } - console.info('returning imports: ', JSON.stringify(imports)); return { Data: imports, }; diff --git a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts index 54273f32d4a32..90f71197929a0 100644 --- a/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts +++ b/packages/@aws-cdk/core/test/custom-resource-provider/export-writer-provider.test.ts @@ -500,9 +500,6 @@ describe('export writer provider', () => { }, Type: 'Custom::CrossRegionExportReader', UpdateReplacePolicy: 'Delete', - DependsOn: [ - 'Nested1NestedStackNested1NestedStackResourceCD0AD36B', - ], }, Nested1NestedStackNested1NestedStackResourceCD0AD36B: { DeletionPolicy: 'Delete', diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 82df8e9eab41e..431950574f3fa 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -73,10 +73,7 @@ describe('nested-stack', () => { Resource2: { Properties: { Prop1: { - 'Fn::GetAtt': [ - 'ExportsReader8B249524', - '/cdk/exports/Stack2/Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E', - ], + Ref: 'referencetoStack2ExportsReader861D07DCcdkexportsStack2Stack1bermudatriangle1337FnGetAttNested1NestedStackNested1NestedStackResourceCD0AD36BOutputsStack1Nested1Resource178AEB067RefCEEE331E', }, }, Type: 'My::Resource', @@ -102,9 +99,6 @@ describe('nested-stack', () => { ], }, }, - DependsOn: [ - 'Nested2NestedStackNested2NestedStackResource877A1112', - ], Type: 'Custom::CrossRegionExportReader', UpdateReplacePolicy: 'Delete', }, From 6b9660db507179749c7cf2fa116a6a4a232684c3 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:06:45 +0000 Subject: [PATCH 29/30] updating docs to indicate that this feature is experimental --- packages/@aws-cdk/aws-cloudfront/README.md | 36 ++++++++++++++++++++++ packages/@aws-cdk/core/README.md | 2 ++ packages/@aws-cdk/core/lib/stack.ts | 2 ++ packages/aws-cdk-lib/README.md | 2 ++ 4 files changed, 42 insertions(+) diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index 223e0bf5f6a7d..7f9616e9e316b 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -125,6 +125,42 @@ new cloudfront.Distribution(this, 'myDist', { }); ``` +#### Cross Region Certificates + +> **This feature is currently experimental** + +You can enable the Stack property `optInToCrossRegionReferences` +in order to access resources in a different stack _and_ region. With this feature flag +enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and +an ACM certificate in `us-east-1`. + +```ts +const stack1 = new Stack(app, 'Stack1', { + env: { + region: 'us-east-1', + }, + optInToCrossRegionReferences: true, +}); +const cert = new acm.Certificate(stack1, 'Cert', { + domainName: '*.example.com', + validation: acm.CertificateValidation.fromDns(route53.PublicHostedZone.fromHostedZoneId(stack1, 'Zone', 'Z0329774B51CGXTDQV3X')), +}); + +const stack2 = new Stack(app, 'Stack2', { + env: { + region: 'us-east-2', + }, + optInToCrossRegionReferences: true, +}); +new cloudfront.Distribution(stack2, 'Distribution', { + defaultBehavior: { + origin: new origins.HttpOrigin('example.com'), + }, + domainNames: ['dev.example.com'], + certificate: cert, +}); +``` + ### Multiple Behaviors & Origins Each distribution has a default behavior which applies to all requests to that distribution; additional behaviors may be specified for a diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 159b52705d6cf..9389f7d96c2ee 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -164,6 +164,8 @@ other. ## Accessing resources in a different stack and region +> **This feature is currently experimental** + You can enable the Stack property `optInToCrossRegionReferences` in order to access resources in a different stack _and_ region. With this feature flag enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 7d6a8257a5d89..42d55eb085f6a 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -146,6 +146,8 @@ export interface StackProps { * Enabling this will create a CloudFormation custom resource * in both the producing stack and consuming stack in order to perform the export/import * + * This feature is currently experimental + * * @default false */ readonly optInToCrossRegionReferences?: boolean; diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 385719aab6b12..814c693a4f539 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -195,6 +195,8 @@ other. ## Accessing resources in a different stack and region +> **This feature is currently experimental** + You can enable the Stack property `optInToCrossRegionReferences` in order to access resources in a different stack _and_ region. With this feature flag enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and From fdea66379bc34c1c99be3dd73be8d2ec1f6f8970 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:41:29 +0000 Subject: [PATCH 30/30] removing `optIn` from property name --- .../integ.core-cross-region-references.ts | 4 +-- packages/@aws-cdk/aws-cloudfront/README.md | 6 ++--- .../integ.cloudfront-cross-region-cert.ts | 4 +-- .../test/integ.pipeline-with-replication.ts | 4 +-- packages/@aws-cdk/core/README.md | 6 ++--- .../core/adr/cross-region-stack-references.md | 2 +- packages/@aws-cdk/core/lib/nested-stack.ts | 2 +- packages/@aws-cdk/core/lib/private/refs.ts | 2 +- packages/@aws-cdk/core/lib/stack.ts | 4 +-- .../core/test/cross-environment-token.test.ts | 12 ++++----- .../@aws-cdk/core/test/nested-stack.test.ts | 8 +++--- packages/@aws-cdk/core/test/stack.test.ts | 26 +++++++++---------- packages/aws-cdk-lib/README.md | 6 ++--- 13 files changed, 43 insertions(+), 43 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts index 3c8d84b955fcf..5e6408139889d 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.core-cross-region-references.ts @@ -17,7 +17,7 @@ class ProducerStack extends Stack { env: { region: 'us-east-1', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const nested = new NestedStack(this, 'IntegNested'); this.queue = new Queue(this, 'IntegQueue'); @@ -35,7 +35,7 @@ class ConsumerStack extends Stack { env: { region: 'us-east-2', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const nested = new NestedStack(this, 'IntegNested'); diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index 7f9616e9e316b..4991464d5f1b1 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -129,7 +129,7 @@ new cloudfront.Distribution(this, 'myDist', { > **This feature is currently experimental** -You can enable the Stack property `optInToCrossRegionReferences` +You can enable the Stack property `crossRegionReferences` in order to access resources in a different stack _and_ region. With this feature flag enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and an ACM certificate in `us-east-1`. @@ -139,7 +139,7 @@ const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const cert = new acm.Certificate(stack1, 'Cert', { domainName: '*.example.com', @@ -150,7 +150,7 @@ const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); new cloudfront.Distribution(stack2, 'Distribution', { defaultBehavior: { diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts index 2b3d73ac562bd..546e9c0a97ccc 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.cloudfront-cross-region-cert.ts @@ -21,7 +21,7 @@ const acmStack = new cdk.Stack(app, 'integ-acm-stack', { region: 'us-east-1', account, }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const cloudFrontStack = new cdk.Stack(app, 'integ-cloudfront-stack', { @@ -29,7 +29,7 @@ const cloudFrontStack = new cdk.Stack(app, 'integ-cloudfront-stack', { region: 'us-east-2', account, }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts index 1e0447f6347ee..045f7bda8ef06 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-with-replication.ts @@ -14,13 +14,13 @@ const stack1 = new Stack(app, 'integ-pipeline-producer-stack', { env: { region: 'us-east-1', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const stack2 = new Stack(app, 'integ-pipeline-consumer-stack', { env: { region: 'us-east-2', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); diff --git a/packages/@aws-cdk/core/README.md b/packages/@aws-cdk/core/README.md index 9389f7d96c2ee..b22c9eba3ec01 100644 --- a/packages/@aws-cdk/core/README.md +++ b/packages/@aws-cdk/core/README.md @@ -166,7 +166,7 @@ other. > **This feature is currently experimental** -You can enable the Stack property `optInToCrossRegionReferences` +You can enable the Stack property `crossRegionReferences` in order to access resources in a different stack _and_ region. With this feature flag enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and an ACM certificate in `us-east-1`. @@ -176,7 +176,7 @@ const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const cert = new acm.Certificate(stack1, 'Cert', { domainName: '*.example.com', @@ -187,7 +187,7 @@ const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); new cloudfront.Distribution(stack2, 'Distribution', { defaultBehavior: { diff --git a/packages/@aws-cdk/core/adr/cross-region-stack-references.md b/packages/@aws-cdk/core/adr/cross-region-stack-references.md index c513279e75d0c..306497cb15d3b 100644 --- a/packages/@aws-cdk/core/adr/cross-region-stack-references.md +++ b/packages/@aws-cdk/core/adr/cross-region-stack-references.md @@ -219,7 +219,7 @@ optional Stack property. ```ts new Stack(app, 'MyStack', { - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); ``` diff --git a/packages/@aws-cdk/core/lib/nested-stack.ts b/packages/@aws-cdk/core/lib/nested-stack.ts index df7bf4a10189b..039a611b28eb7 100644 --- a/packages/@aws-cdk/core/lib/nested-stack.ts +++ b/packages/@aws-cdk/core/lib/nested-stack.ts @@ -120,7 +120,7 @@ export class NestedStack extends Stack { env: { account: parentStack.account, region: parentStack.region }, synthesizer: new NestedStackSynthesizer(parentStack.synthesizer), description: props.description, - optInToCrossRegionReferences: parentStack._crossRegionReferences, + crossRegionReferences: parentStack._crossRegionReferences, }); this._parentStack = parentStack; diff --git a/packages/@aws-cdk/core/lib/private/refs.ts b/packages/@aws-cdk/core/lib/private/refs.ts index 2a4c605404078..7569907e62d87 100644 --- a/packages/@aws-cdk/core/lib/private/refs.ts +++ b/packages/@aws-cdk/core/lib/private/refs.ts @@ -70,7 +70,7 @@ function resolveValue(consumer: Stack, reference: CfnReference): IResolvable { throw new Error( `Stack "${consumer.node.path}" cannot consume a cross reference from stack "${producer.node.path}". ` + 'Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack. ' + - 'Set optInToCrossRegionReferences=true to enable cross region references'); + 'Set crossRegionReferences=true to enable cross region references'); } // ---------------------------------------------------------------------- diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 42d55eb085f6a..744da3288649c 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -150,7 +150,7 @@ export interface StackProps { * * @default false */ - readonly optInToCrossRegionReferences?: boolean; + readonly crossRegionReferences?: boolean; } /** @@ -363,7 +363,7 @@ export class Stack extends Construct implements ITaggable { this._missingContext = new Array(); this._stackDependencies = { }; this.templateOptions = { }; - this._crossRegionReferences = !!props.optInToCrossRegionReferences; + this._crossRegionReferences = !!props.crossRegionReferences; Object.defineProperty(this, STACK_SYMBOL, { value: true }); diff --git a/packages/@aws-cdk/core/test/cross-environment-token.test.ts b/packages/@aws-cdk/core/test/cross-environment-token.test.ts index 0ed60d779b4ca..7270d94822b50 100644 --- a/packages/@aws-cdk/core/test/cross-environment-token.test.ts +++ b/packages/@aws-cdk/core/test/cross-environment-token.test.ts @@ -186,7 +186,7 @@ describe('cross environment', () => { /Cannot use resource 'Stack1\/MyResource' in a cross-environment fashion/); }); - test('can reference a deploy-time physical name across regions, when optInToCrossRegionReferences=true', () => { + test('can reference a deploy-time physical name across regions, when crossRegionReferences=true', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { @@ -194,14 +194,14 @@ describe('cross environment', () => { account: '123456789012', region: 'bermuda-triangle-1337', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789012', region: 'bermuda-triangle-42', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); // WHEN @@ -250,7 +250,7 @@ describe('cross environment', () => { }); }); - test('cannot reference a deploy-time physical name across regions, when optInToCrossRegionReferences=false', () => { + test('cannot reference a deploy-time physical name across regions, when crossRegionReferences=false', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { @@ -258,14 +258,14 @@ describe('cross environment', () => { account: '123456789012', region: 'bermuda-triangle-1337', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789012', region: 'bermuda-triangle-42', }, - optInToCrossRegionReferences: false, + crossRegionReferences: false, }); // WHEN diff --git a/packages/@aws-cdk/core/test/nested-stack.test.ts b/packages/@aws-cdk/core/test/nested-stack.test.ts index 431950574f3fa..4d2ffbec186ac 100644 --- a/packages/@aws-cdk/core/test/nested-stack.test.ts +++ b/packages/@aws-cdk/core/test/nested-stack.test.ts @@ -35,7 +35,7 @@ describe('nested-stack', () => { expect(nestedStack.templateOptions.description).toEqual(description); }); - test('can create cross region references when optInToCrossRegionReferences=true', () => { + test('can create cross region references when crossRegionReferences=true', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { @@ -43,14 +43,14 @@ describe('nested-stack', () => { account: '123456789012', region: 'bermuda-triangle-1337', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const stack2 = new Stack(app, 'Stack2', { env: { account: '123456789012', region: 'bermuda-triangle-42', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const nestedStack = new NestedStack(stack1, 'Nested1'); const nestedStack2 = new NestedStack(stack2, 'Nested2'); @@ -141,7 +141,7 @@ describe('nested-stack', () => { }); }); - test('cannot create cross region references when optInToCrossRegionReferences=false', () => { + test('cannot create cross region references when crossRegionReferences=false', () => { // GIVEN const app = new App(); const stack1 = new Stack(app, 'Stack1', { diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index a58b3db4bbb90..ae1cc4419944b 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -462,14 +462,14 @@ describe('stack', () => { }); }); - test('cross-region stack references, optInToCrossRegionReferences=true', () => { + test('cross-region stack references, crossRegionReferences=true', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, crossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, optInToCrossRegionReferences: true }); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, crossRegionReferences: true }); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { @@ -535,7 +535,7 @@ describe('stack', () => { test('cross-region stack references throws error', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, crossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); @@ -552,21 +552,21 @@ describe('stack', () => { // THEN expect(() => { app.synth(); - }).toThrow(/Set optInToCrossRegionReferences=true to enable cross region references/); + }).toThrow(/Set crossRegionReferences=true to enable cross region references/); }); - test('cross region stack references with multiple stacks, optInToCrossRegionReferences=true', () => { + test('cross region stack references with multiple stacks, crossRegionReferences=true', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, crossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); + const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-east-1' }, crossRegionReferences: true }); const exportResource3 = new CfnResource(stack3, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, optInToCrossRegionReferences: true }); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, crossRegionReferences: true }); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { @@ -740,18 +740,18 @@ describe('stack', () => { }); }); - test('cross region stack references with multiple stacks and multiple regions, optInToCrossRegionReferences=true', () => { + test('cross region stack references with multiple stacks and multiple regions, crossRegionReferences=true', () => { // GIVEN const app = new App(); - const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, optInToCrossRegionReferences: true }); + const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1' }, crossRegionReferences: true }); const exportResource = new CfnResource(stack1, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-west-1' }, optInToCrossRegionReferences: true }); + const stack3 = new Stack(app, 'Stack3', { env: { region: 'us-west-1' }, crossRegionReferences: true }); const exportResource3 = new CfnResource(stack3, 'SomeResourceExport', { type: 'AWS::S3::Bucket', }); - const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, optInToCrossRegionReferences: true }); + const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2' }, crossRegionReferences: true }); // WHEN - used in another stack new CfnResource(stack2, 'SomeResource', { diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 814c693a4f539..92bb8a042acec 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -197,7 +197,7 @@ other. > **This feature is currently experimental** -You can enable the Stack property `optInToCrossRegionReferences` +You can enable the Stack property `crossRegionReferences` in order to access resources in a different stack _and_ region. With this feature flag enabled it is possible to do something like creating a CloudFront distribution in `us-east-2` and an ACM certificate in `us-east-1`. @@ -207,7 +207,7 @@ const stack1 = new Stack(app, 'Stack1', { env: { region: 'us-east-1', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); const cert = new acm.Certificate(stack1, 'Cert', { domainName: '*.example.com', @@ -218,7 +218,7 @@ const stack2 = new Stack(app, 'Stack2', { env: { region: 'us-east-2', }, - optInToCrossRegionReferences: true, + crossRegionReferences: true, }); new cloudfront.Distribution(stack2, 'Distribution', { defaultBehavior: {