diff --git a/packages/aws-cdk/lib/api/deploy-stack.ts b/packages/aws-cdk/lib/api/deploy-stack.ts index 02d1d7b0f0324..390320b04baae 100644 --- a/packages/aws-cdk/lib/api/deploy-stack.ts +++ b/packages/aws-cdk/lib/api/deploy-stack.ts @@ -478,16 +478,24 @@ class FullCloudFormationDeployment { const startTime = new Date(); if (this.update) { - await this.cfn.updateStack({ - StackName: this.stackName, - ClientRequestToken: `update${this.uuid}`, - ...this.commonPrepareOptions(), - ...this.commonExecuteOptions(), - }).promise(); - - const ret = await this.monitorDeployment(startTime, undefined); await this.updateTerminationProtection(); - return ret; + + try { + await this.cfn.updateStack({ + StackName: this.stackName, + ClientRequestToken: `update${this.uuid}`, + ...this.commonPrepareOptions(), + ...this.commonExecuteOptions(), + }).promise(); + } catch (err: any) { + if (err.message === 'No updates are to be performed.') { + debug('No updates are to be performed for stack %s', this.stackName); + return { noOp: true, outputs: this.cloudFormationStack.outputs, stackArn: this.cloudFormationStack.stackId }; + } + throw err; + } + + return this.monitorDeployment(startTime, undefined); } else { // Take advantage of the fact that we can set termination protection during create const terminationProtection = this.stackArtifact.terminationProtection ?? false; diff --git a/packages/aws-cdk/test/api/deploy-stack.test.ts b/packages/aws-cdk/test/api/deploy-stack.test.ts index 440d95cbff8b2..666d4f43410ec 100644 --- a/packages/aws-cdk/test/api/deploy-stack.test.ts +++ b/packages/aws-cdk/test/api/deploy-stack.test.ts @@ -206,6 +206,25 @@ test('call UpdateStack when method=direct and the stack exists already', async ( expect(cfnMocks.updateStack).toHaveBeenCalled(); }); +test('method=direct and no updates to be performed', async () => { + cfnMocks.updateStack?.mockRejectedValueOnce({ + code: 'ValidationError', + message: 'No updates are to be performed.', + } as never); + + // WHEN + givenStackExists(); + + const ret = await deployStack({ + ...standardDeployStackArguments(), + deploymentMethod: { method: 'direct' }, + force: true, + }); + + // THEN + expect(ret).toEqual(expect.objectContaining({ noOp: true })); +}); + test("does not call tryHotswapDeployment() if 'hotswap' is false", async () => { // WHEN await deployStack({