Skip to content

Commit

Permalink
fix(stack): check stack tags for deploy-time values
Browse files Browse the repository at this point in the history
Stack tags are not rendered to the template, but instead are passed via
API call.

Verify that stack tags do not contain unresolved values, as they won't
work.

Closes #28017.
  • Loading branch information
rix0rrr committed Sep 16, 2024
1 parent 4392ab4 commit 9facdb6
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 3 deletions.
18 changes: 15 additions & 3 deletions packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Node, IConstruct } from 'constructs';
import { ISynthesisSession } from './types';
import * as cxschema from '../../../cloud-assembly-schema';
import { Stack } from '../stack';
import { Token } from '../token';

/**
* Shared logic of writing stack artifact to the Cloud Assembly
Expand All @@ -20,10 +21,21 @@ export function addStackArtifactToAssembly(
stackProps: Partial<cxschema.AwsCloudFormationStackProperties>,
additionalStackDependencies: string[]) {


const stackTags = stack.tags.tagValues();

// nested stack tags are applied at the AWS::CloudFormation::Stack resource
// level and are not needed in the cloud assembly.
if (stack.tags.hasTags()) {
stack.node.addMetadata(cxschema.ArtifactMetadataEntryType.STACK_TAGS, stack.tags.renderTags());
if (Object.entries(stackTags).length > 0) {
stack.node.addMetadata(
cxschema.ArtifactMetadataEntryType.STACK_TAGS,
Object.entries(stackTags).map(([key, value]) => ({ Key: key, Value: value })));

for (const [k, v] of Object.entries(stackTags)) {
if (Token.isUnresolved(k) || Token.isUnresolved(v)) {
throw new Error(`Stack tags may not contain deploy-time values (tag: ${k}=${v}). Apply tags containing deploy-time values to resources only, avoid tagging stacks.`);
}
}
}

const deps = [
Expand All @@ -46,7 +58,7 @@ export function addStackArtifactToAssembly(
const properties: cxschema.AwsCloudFormationStackProperties = {
templateFile: stack.templateFile,
terminationProtection: stack.terminationProtection,
tags: nonEmptyDict(stack.tags.tagValues()),
tags: nonEmptyDict(stackTags),
validateOnSynth: session.validateOnSynth,
...stackProps,
...stackNameProperty,
Expand Down
15 changes: 15 additions & 0 deletions packages/aws-cdk-lib/core/test/stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2075,6 +2075,21 @@ describe('stack', () => {
expect(asm.getStackArtifact(stack2.artifactId).tags).toEqual(expected);
});

test('stack tags may not contain tokens', () => {
// GIVEN
const app = new App({
stackTraces: false,
});

const stack = new Stack(app, 'stack1', {
tags: {
foo: Lazy.string({ produce: () => 'lazy' }),
},
});

expect(() => app.synth()).toThrow(/Stack tags may not contain deploy-time values/);
});

test('Termination Protection is reflected in Cloud Assembly artifact', () => {
// if the root is an app, invoke "synth" to avoid double synthesis
const app = new App();
Expand Down

0 comments on commit 9facdb6

Please sign in to comment.