diff --git a/packages/aws-cdk/lib/api/util/checks.ts b/packages/aws-cdk/lib/api/util/checks.ts index a094156ba74fb..13afbc1b6d135 100644 --- a/packages/aws-cdk/lib/api/util/checks.ts +++ b/packages/aws-cdk/lib/api/util/checks.ts @@ -60,15 +60,14 @@ export async function getBootstrapStackInfo(sdk: ISDK, stackName: string): Promi throw new Error(`Invalid BootstrapVersion value: ${versionOutput.OutputValue}`); } - // try to get bucketname from the logical resource id - let bucketName: string | undefined; - const resourcesResponse = await cfn.describeStackResources({ StackName: stackName }).promise(); - const bucketResource = resourcesResponse.StackResources?.find(resource => - resource.ResourceType === 'AWS::S3::Bucket', - ); - bucketName = bucketResource?.PhysicalResourceId; - - let hasStagingBucket = !!bucketName; + // try to get bucketname from the logical resource id. If there is no + // bucketname, or the value doesn't look like an S3 bucket name, we assume + // the bucket doesn't exist. + // + // We would have preferred to look at the stack resources here, but + // unfortunately the deploy role doesn't have permissions call DescribeStackResources. + const bucketName = stack.Outputs?.find(output => output.OutputKey === 'BucketName')?.OutputValue; + const hasStagingBucket = !!(bucketName && bucketName.match(/^[a-z0-9-]+$/)); return { hasStagingBucket, diff --git a/packages/aws-cdk/test/api/util/checks.test.ts b/packages/aws-cdk/test/api/util/checks.test.ts index 660577a8f6aec..04731c064a37c 100644 --- a/packages/aws-cdk/test/api/util/checks.test.ts +++ b/packages/aws-cdk/test/api/util/checks.test.ts @@ -25,10 +25,6 @@ describe('determineAllowCrossAccountAssetPublishing', () => { }); }); - AWSMock.mock('CloudFormation', 'describeStackResources', (_params: any, callback: Function) => { - callback(null, { StackResources: [] }); - }); - const result = await determineAllowCrossAccountAssetPublishing(mockSDK); expect(result).toBe(true); }); @@ -37,15 +33,14 @@ describe('determineAllowCrossAccountAssetPublishing', () => { AWSMock.mock('CloudFormation', 'describeStacks', (_params: any, callback: Function) => { callback(null, { Stacks: [{ - Outputs: [{ OutputKey: 'BootstrapVersion', OutputValue: '21' }], + Outputs: [ + { OutputKey: 'BootstrapVersion', OutputValue: '21' }, + { OutputKey: 'BucketName', OutputValue: 'some-bucket' }, + ], }], }); }); - AWSMock.mock('CloudFormation', 'describeStackResources', (_params: any, callback: Function) => { - callback(null, { StackResources: [{ ResourceType: 'AWS::S3::Bucket', PhysicalResourceId: 'some-bucket' }] }); - }); - const result = await determineAllowCrossAccountAssetPublishing(mockSDK); expect(result).toBe(true); }); @@ -63,15 +58,14 @@ describe('determineAllowCrossAccountAssetPublishing', () => { AWSMock.mock('CloudFormation', 'describeStacks', (_params: any, callback: Function) => { callback(null, { Stacks: [{ - Outputs: [{ OutputKey: 'BootstrapVersion', OutputValue: '20' }], + Outputs: [ + { OutputKey: 'BootstrapVersion', OutputValue: '20' }, + { OutputKey: 'BucketName', OutputValue: 'some-bucket' }, + ], }], }); }); - AWSMock.mock('CloudFormation', 'describeStackResources', (_params: any, callback: Function) => { - callback(null, { StackResources: [{ ResourceType: 'AWS::S3::Bucket', PhysicalResourceId: 'some-bucket' }] }); - }); - const result = await determineAllowCrossAccountAssetPublishing(mockSDK); expect(result).toBe(false); }); @@ -94,15 +88,14 @@ describe('getBootstrapStackInfo', () => { AWSMock.mock('CloudFormation', 'describeStacks', (_params: any, callback: Function) => { callback(null, { Stacks: [{ - Outputs: [{ OutputKey: 'BootstrapVersion', OutputValue: '21' }], + Outputs: [ + { OutputKey: 'BootstrapVersion', OutputValue: '21' }, + { OutputKey: 'BucketName', OutputValue: 'some-bucket' }, + ], }], }); }); - AWSMock.mock('CloudFormation', 'describeStackResources', (_params: any, callback: Function) => { - callback(null, { StackResources: [{ ResourceType: 'AWS::S3::Bucket', PhysicalResourceId: 'some-bucket' }] }); - }); - const result = await getBootstrapStackInfo(mockSDK, 'CDKToolkit'); expect(result).toEqual({ hasStagingBucket: true,