diff --git a/packages/aws-cdk/lib/api/deploy-stack.ts b/packages/aws-cdk/lib/api/deploy-stack.ts index 6d3a5c71e79e9..b7b9fe7ce849b 100644 --- a/packages/aws-cdk/lib/api/deploy-stack.ts +++ b/packages/aws-cdk/lib/api/deploy-stack.ts @@ -7,7 +7,7 @@ import { prepareAssets } from '../assets'; import { debug, error } from '../logging'; import { Mode } from './aws-auth/credentials'; import { ToolkitInfo } from './toolkit-info'; -import { describeStack, stackExists, waitForChangeSet, waitForStack } from './util/cloudformation'; +import { describeStack, stackExists, stackFailedCreating, waitForChangeSet, waitForStack } from './util/cloudformation'; import { StackActivityMonitor } from './util/cloudformation/stack-activity-monitor'; import { StackStatus } from './util/cloudformation/stack-status'; import { SDK } from './util/sdk'; @@ -43,6 +43,15 @@ export async function deployStack(stack: cxapi.SynthesizedStack, const cfn = await sdk.cloudFormation(stack.environment, Mode.ForWriting); const bodyParameter = await makeBodyParameter(stack, toolkitInfo); + if (await stackFailedCreating(cfn, deployName)) { + debug(`Found existing stack ${deployName} that had previously failed creation. Deleting it before attempting to re-create it.`); + await cfn.deleteStack({ StackName: deployName }).promise(); + const deletedStack = await waitForStack(cfn, deployName, false); + if (deletedStack && deletedStack.StackStatus !== 'DELETE_COMPLETE') { + throw new Error(`Failed deleting stack ${deployName} that had previously failed creation (current state: ${deletedStack.StackStatus})`); + } + } + const update = await stackExists(cfn, deployName); const changeSetName = `CDK-${executionId}`; diff --git a/packages/aws-cdk/lib/api/util/cloudformation.ts b/packages/aws-cdk/lib/api/util/cloudformation.ts index 52f1bc076bfd1..3551bca8e7ae3 100644 --- a/packages/aws-cdk/lib/api/util/cloudformation.ts +++ b/packages/aws-cdk/lib/api/util/cloudformation.ts @@ -49,6 +49,20 @@ export async function stackExists(cfn: CloudFormation, stackName: string): Promi return description !== undefined; } +/** + * Checks whether a stack has failed creation in CloudFormation. This is identified by the current stack Status being + * ``ROLLBACK_COMPLETE``. + * + * @param cfn a CloudFormation client + * @param stackName the name of the stack to be checked for + * + * @returns +true+ if the stack exists and is in failed-creation state. + */ +export async function stackFailedCreating(cfn: CloudFormation, stackName: string): Promise { + const description = await describeStack(cfn, stackName); + return description != null && description.StackStatus === 'ROLLBACK_COMPLETE'; +} + /** * Waits for a function to return non-+undefined+ before returning. *