Overview
CloudFormation has a limited number of intrinsic functions that can be combined in some interesting ways.
One of the intrinsic functions I wish existed is Fn::StartsWith
. As the name implies, the objective is to check if a string begins with another string. While this operation doesn't exist natively, it can be constructed using the existing intrinsic functions.
Part 1 - Fn::Split
Fn::Split
is an intrinsic function used to split a string into an array using a delimiter string. It's most commonly used to split on a punctuation mark, such a comma (,
). One of the side effects of applying this function, is that the resulting array does NOT contain the delimiter string, which we can leverage.
Consider the following example:
!Split [ "world", "hello world!" ]
Which produces this result.
[ "hello ", "!" ]
As you can see, the word "world" is gone from the result.
Let's do this again using "hello" instead:
!Split [ "hello", "hello world!" ]
We get:
[ "", " world!" ]
Now the lightbulb is turning on. Indeed, when the first string of a Fn::Split
operation is empty, it means the split operation found an occurrence of the delimiter string at the beginning. Let's use this!
Part 2 - Fn::Select and Fn::Equals
Fortunately, Fn::Split
always returns an array of at least one element. This makes it safe to always access the first item in the returned array. We can use that to compare the value to an empty string.
!Equals [ !Select [ 0, !Split [ PREFIX, VALUE ], "" ]]
Part 3 - Beware of empty strings
An empty string, split on any delimiter string, always produces an array with a single empty string in it. This gives us a false positive if we don't check for it.
!Not [ !Equals [ VALUE, "" ]]
Final - Putting it all together
Combining the empty string check and the Fn::Split
function gives us an implementation of Fn::StartsWith
.
!And [
!Not [ !Equals [ VALUE, "" ]],
!Equals [ !Select [ 0, !Split [ PREFIX, VALUE ]], "" ]
]
I hope you found this useful. If you know of other patterns to emulate missing functionality in CloudFormation, please leave a comment!
Happy Hacking!
Top comments (4)
I tried the above, but found there was an issue with the !Equals clause as one of the closing brackets was in the wrong spot. I was able to get it to work as the following:
!Equals [!Select [ 0, !Split [ PREFIX, VALUE ]], ""]
Thanks for letting me know. I corrected the article.
Hey there. Was trying to see if it is also possible to construct a Fn::EndsWith. Couldn't work one out because the !Select needs the last item instead of the first. Do you happen to know a similar trick for that?
It might be possible to approximate it if you allow for an upper-bound and you add a magic marker at the end. That way, you could incrementally do an Fn:::Contains operation (similar to Fn::StartsWith) and see if you reach the magic marker by itself.