Skip to content

Commit f1d9a7d

Browse files
authored
fix(cli): cdk diff fails when deploy role requires tags (#33340)
The call to `createChangeSet` is not passing that stack's tags. If the deploy role has some policy that requires specific tags, it will fail to create a change set and, therefore, to create a diff. Pass the tags along to `createChangeSet`. Fixes #33316. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 4744ee5 commit f1d9a7d

File tree

2 files changed

+48
-9
lines changed

2 files changed

+48
-9
lines changed

packages/aws-cdk/lib/api/deployments/cloudformation.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ export async function uploadStackTemplateAssets(stack: cxapi.CloudFormationStack
459459
}
460460
}
461461

462-
async function createChangeSet(options: CreateChangeSetOptions): Promise<DescribeChangeSetCommandOutput> {
462+
export async function createChangeSet(options: CreateChangeSetOptions): Promise<DescribeChangeSetCommandOutput> {
463463
await cleanupOldChangeset(options.changeSetName, options.stack.stackName, options.cfn);
464464

465465
debug(`Attempting to create ChangeSet with name ${options.changeSetName} for stack ${options.stack.stackName}`);
@@ -478,6 +478,7 @@ async function createChangeSet(options: CreateChangeSetOptions): Promise<Describ
478478
Parameters: stackParams.apiParameters,
479479
ResourcesToImport: options.resourcesToImport,
480480
RoleARN: options.role,
481+
Tags: toCfnTags(options.stack.tags),
481482
Capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'],
482483
});
483484

@@ -491,6 +492,13 @@ async function createChangeSet(options: CreateChangeSetOptions): Promise<Describ
491492
return createdChangeSet;
492493
}
493494

495+
function toCfnTags(tags: { [id: string]: string }): Tag[] {
496+
return Object.entries(tags).map(([k, v]) => ({
497+
Key: k,
498+
Value: v,
499+
}));
500+
}
501+
494502
export async function cleanupOldChangeset(changeSetName: string, stackName: string, cfn: ICloudFormationClient) {
495503
// Delete any existing change sets generated by CDK since change set names must be unique.
496504
// The delete request is successful as long as the stack exists (even if the change set does not exist).

packages/aws-cdk/test/api/deployments/cloudformation-deployments.test.ts

+39-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
jest.mock('../../../lib/api/deployments/deploy-stack');
2-
jest.mock('../../../lib/api/deployments/asset-publishing');
3-
1+
import * as cxapi from '@aws-cdk/cx-api';
42
import {
3+
ContinueUpdateRollbackCommand,
4+
DescribeStackEventsCommand,
55
DescribeStacksCommand,
66
ListStackResourcesCommand,
7-
StackStatus,
8-
type StackResourceSummary,
97
RollbackStackCommand,
10-
ContinueUpdateRollbackCommand,
11-
DescribeStackEventsCommand,
8+
type StackResourceSummary,
9+
StackStatus,
10+
DescribeChangeSetCommand,
11+
ChangeSetStatus,
12+
CreateChangeSetCommand,
1213
} from '@aws-sdk/client-cloudformation';
1314
import { GetParameterCommand } from '@aws-sdk/client-ssm';
14-
import { CloudFormationStack, Deployments } from '../../../lib/api/deployments';
15+
import { CloudFormationStack, createChangeSet, Deployments } from '../../../lib/api/deployments';
1516
import { deployStack } from '../../../lib/api/deployments/deploy-stack';
1617
import { HotswapMode } from '../../../lib/api/hotswap/common';
1718
import { ToolkitInfo } from '../../../lib/api/toolkit-info';
@@ -27,6 +28,9 @@ import {
2728
} from '../../util/mock-sdk';
2829
import { FakeCloudformationStack } from '../fake-cloudformation-stack';
2930

31+
jest.mock('../../../lib/api/deployments/deploy-stack');
32+
jest.mock('../../../lib/api/deployments/asset-publishing');
33+
3034
let sdkProvider: MockSdkProvider;
3135
let sdk: MockSdk;
3236
let deployments: Deployments;
@@ -1132,6 +1136,33 @@ describe('stackExists', () => {
11321136
});
11331137
});
11341138

1139+
test('tags are passed along to create change set', async () => {
1140+
mockCloudFormationClient.on(DescribeChangeSetCommand).resolves({
1141+
Status: ChangeSetStatus.CREATE_COMPLETE,
1142+
});
1143+
1144+
const stack: any = {};
1145+
stack.tags = { SomeKey: 'SomeValue' };
1146+
for (const methodName of Object.getOwnPropertyNames(cxapi.CloudFormationStackArtifact.prototype)) {
1147+
stack[methodName] = jest.fn();
1148+
}
1149+
1150+
await createChangeSet({
1151+
stack: stack,
1152+
cfn: new MockSdk().cloudFormation(),
1153+
changeSetName: 'foo',
1154+
willExecute: false,
1155+
exists: true,
1156+
uuid: '142DF82A-8ED8-4944-8EEB-A5BAE141F13F',
1157+
bodyParameter: {},
1158+
parameters: {},
1159+
});
1160+
1161+
expect(mockCloudFormationClient).toHaveReceivedCommandWith(CreateChangeSetCommand, {
1162+
Tags: [{ Key: 'SomeKey', Value: 'SomeValue' }],
1163+
});
1164+
});
1165+
11351166
function pushStackResourceSummaries(stackName: string, ...items: StackResourceSummary[]) {
11361167
if (!currentCfnStackResources[stackName]) {
11371168
currentCfnStackResources[stackName] = [];

0 commit comments

Comments
 (0)