Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions packages/aws-cdk-lib/core/lib/tag-aspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Annotations } from './annotations';
import { IAspect, Aspects, AspectOptions } from './aspect';
import { UnscopedValidationError } from './errors';
import { FeatureFlags } from './feature-flags';
import { Stack } from './stack';
import * as cxapi from '../../cx-api';
import { mutatingAspectPrio32333 } from './private/aspect-prio';
import { ITaggable, ITaggableV2, TagManager } from './tag-manager';
Expand Down Expand Up @@ -38,6 +39,18 @@ export interface TagProps {
*/
readonly includeResourceTypes?: string[];

/**
* Whether to apply tags to CloudFormation ChangeSets
*
* This ensures tags are applied to ChangeSets even when the
* explicitStackTags feature flag excludes stack-level tags.
* This is important for compliance with SCP policies that
* require tags on ChangeSets.
*
* @default true
*/
readonly applyToChangeSets?: boolean;

/**
* Priority of the tag operation
*
Expand Down Expand Up @@ -186,7 +199,18 @@ export class Tags {
*/
public add(key: string, value: string, props: TagProps = {}) {
// Implicitly add `aws:cdk:stack` to the `excludeResourceTypes` array in modern behavior
// BUT: If applyToChangeSets is true (default), we need to ensure ChangeSets still get tagged
if (this.explicitStackTags && !props.includeResourceTypes?.includes('aws:cdk:stack')) {
// Check if we should still apply to ChangeSets
const applyToChangeSets = props.applyToChangeSets !== false;

// If applyToChangeSets is true, we need to ensure the stack still gets the tags
// for ChangeSets, even though resources won't get them from the stack
if (applyToChangeSets && Stack.isStack(this.scope)) {
// Apply the tag directly to the stack for ChangeSet purposes
(this.scope as Stack).addStackTag(key, value);
}

props = {
...props,
excludeResourceTypes: [...props.excludeResourceTypes ?? [], 'aws:cdk:stack'],
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Outputs": {
"TestOutput": {
"Description": "Test output for integration test",
"Value": "test-value"
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions packages/aws-cdk-lib/core/test/integ.stack-tags-changeset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Integration test for stack tagging behavior with explicitStackTags feature flag.
* This test verifies that ChangeSets receive proper tags even when the explicitStackTags
* feature flag is enabled, ensuring compliance with SCP policies that require tags on ChangeSets.
*/

import { App, Stack, Tags, CfnOutput } from 'aws-cdk-lib';

const app = new App({
context: {
'@aws-cdk/core:explicitStackTags': true,
},
});

// Stack with Tags.of(stack).add() - should apply to ChangeSets
const stack = new Stack(app, 'integ-stack-tags-changeset', {
env: { region: 'us-east-1' },
});

// Add tags using Tags.of(stack).add() - these should be applied to ChangeSets
Tags.of(stack).add('Environment', 'IntegTest');
Tags.of(stack).add('Project', 'CDK-Core');
Tags.of(stack).add('Owner', 'CDK-Team');
Tags.of(stack).add('CostCenter', 'Engineering');

// Test the new applyToChangeSets property
Tags.of(stack).add('ChangeSetTag', 'ShouldAppear');
Tags.of(stack).add('NoChangeSetTag', 'ShouldNotAppear', { applyToChangeSets: false });

// Add direct stack tags as well
stack.addStackTag('DirectTag', 'DirectValue');

// Add a simple resource to make the stack deployable
new CfnOutput(stack, 'TestOutput', {
value: 'test-value',
description: 'Test output for integration test',
});

app.synth();
Loading
Loading