diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts index 46cb62b27c218..686968298475c 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/_shared.ts @@ -26,15 +26,22 @@ export function addStackArtifactToAssembly( // nested stack tags are applied at the AWS::CloudFormation::Stack resource // level and are not needed in the cloud assembly. if (Object.entries(stackTags).length > 0) { - 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 resolvedTags = Object.entries(stackTags).filter(([k, v]) => !(Token.isUnresolved(k) || Token.isUnresolved(v))); + const unresolvedTags = Object.entries(stackTags).filter(([k, v]) => Token.isUnresolved(k) || Token.isUnresolved(v)); + + if (unresolvedTags.length > 0) { + const rendered = unresolvedTags.map(([k, v]) => `${Token.isUnresolved(k) ? '': k}=${Token.isUnresolved(v) ? '' : v}`).join(', '); + stack.node.addMetadata( + cxschema.ArtifactMetadataEntryType.WARN, + `Ignoring stack tags that contain deploy-time values (found: ${rendered}). Apply tags containing deploy-time values to resources only, avoid tagging stacks (for example using { excludeResourceTypes: ['aws:cdk:stack'] }).`, + ); } - stack.node.addMetadata( - cxschema.ArtifactMetadataEntryType.STACK_TAGS, - Object.entries(stackTags).map(([key, value]) => ({ Key: key, Value: value }))); + if (resolvedTags.length > 0) { + stack.node.addMetadata( + cxschema.ArtifactMetadataEntryType.STACK_TAGS, + resolvedTags.map(([key, value]) => ({ Key: key, Value: value }))); + } } const deps = [ diff --git a/packages/aws-cdk-lib/core/test/stack.test.ts b/packages/aws-cdk-lib/core/test/stack.test.ts index 0f67d1ad6ac7b..399e355063dfb 100644 --- a/packages/aws-cdk-lib/core/test/stack.test.ts +++ b/packages/aws-cdk-lib/core/test/stack.test.ts @@ -2075,7 +2075,7 @@ describe('stack', () => { expect(asm.getStackArtifact(stack2.artifactId).tags).toEqual(expected); }); - test('stack tags may not contain tokens', () => { + test('warning when stack tags contain tokens', () => { // GIVEN const app = new App({ stackTraces: false, @@ -2087,7 +2087,14 @@ describe('stack', () => { }, }); - expect(() => app.synth()).toThrow(/Stack tags may not contain deploy-time values/); + const asm = app.synth(); + const stackArtifact = asm.stacks[0]; + expect(stackArtifact.manifest.metadata?.['/stack1']).toEqual([ + { + type: 'aws:cdk:warning', + data: expect.stringContaining('Ignoring stack tags that contain deploy-time values'), + }, + ]); }); test('stack notification arns are reflected in the stack artifact properties', () => {