diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/fn-select-with-novalue.json b/packages/@aws-cdk/cloudformation-include/test/test-templates/fn-select-with-novalue.json new file mode 100644 index 0000000000000..861387e330ee7 --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/fn-select-with-novalue.json @@ -0,0 +1,23 @@ +{ + "Parameters": { + "DoIt": { + "Type": "String" + } + }, + "Conditions": { + "MyCondition": { + "Fn::Equals": [{ "Ref": "DoIt" }, "Yes"] + } + }, + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": { "Fn::Select": [0, [ + { "Fn::If": ["MyCondition", "doing-it", { "Ref": "AWS::NoValue" }] }, + "not-doingit" + ]]} + } + } + } +} diff --git a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts index 65cd7e981cc81..eec714ac5d7d6 100644 --- a/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/valid-templates.test.ts @@ -1081,6 +1081,14 @@ describe('CDK Include', () => { loadTestFileToJsObject('properties-not-in-cfn-spec.json'), ); }); + + test('roundtrip a fn-select with a fn-if/ref-novalue in it', () => { + includeTestTemplate(stack, 'fn-select-with-novalue.json'); + + Template.fromStack(stack).templateMatches( + loadTestFileToJsObject('fn-select-with-novalue.json'), + ); + }); }); interface IncludeTestTemplateProps { diff --git a/packages/@aws-cdk/core/lib/cfn-fn.ts b/packages/@aws-cdk/core/lib/cfn-fn.ts index 3ef1c265654bd..673784e0e2a5b 100644 --- a/packages/@aws-cdk/core/lib/cfn-fn.ts +++ b/packages/@aws-cdk/core/lib/cfn-fn.ts @@ -127,7 +127,7 @@ export class Fn { * @returns a token represented as a string */ public static select(index: number, array: string[]): string { - if (!Token.isUnresolved(array)) { + if (!Token.isUnresolved(index) && !Token.isUnresolved(array) && !array.some(Token.isUnresolved)) { return array[index]; } diff --git a/packages/@aws-cdk/core/test/fn.test.ts b/packages/@aws-cdk/core/test/fn.test.ts index 221a7b6e811a1..343c3e0ea0422 100644 --- a/packages/@aws-cdk/core/test/fn.test.ts +++ b/packages/@aws-cdk/core/test/fn.test.ts @@ -1,6 +1,6 @@ import * as fc from 'fast-check'; import * as _ from 'lodash'; -import { App, CfnOutput, Fn, Stack, Token } from '../lib'; +import { App, Aws, CfnOutput, Fn, Stack, Token } from '../lib'; import { Intrinsic } from '../lib/private/intrinsic'; function asyncTest(cb: () => Promise): () => void { @@ -27,8 +27,24 @@ describe('fn', () => { describe('eager resolution for non-tokens', () => { test('Fn.select', () => { expect(Fn.select(2, ['hello', 'you', 'dude'])).toEqual('dude'); + }); + + test('Fn.select does not short-circuit if there are tokens in the array', () => { + const stack = new Stack(); + expect(stack.resolve(Fn.select(2, [ + Fn.conditionIf('xyz', 'yep', Aws.NO_VALUE).toString(), + 'you', + 'dude', + ]))).toEqual({ + 'Fn::Select': [2, [ + { 'Fn::If': ['xyz', 'yep', { Ref: 'AWS::NoValue' }] }, + 'you', + 'dude', + ]], + }); }); + test('Fn.split', () => { expect(Fn.split(':', 'hello:world:yeah')).toEqual(['hello', 'world', 'yeah']);