CloudFormation’s built-in functions Fn::Sub vs Fn::Join

2021.12.23

Many individuals use the Fn::Join command to combine information in their CloudFormation templates, such as static text and variables. While this works, as things grow complicated, it may get chaotic and difficult to understand. Fn::Sub aids in the simplification of our template definitions.

First let's see how Fn::Join works.

Using Join function we can connect text and variables. The syntax may be expressed in a variety of ways, including JSON and YAML.

Below are the examples for Syntax of Join.

Using Fn::Join for Json.

{
  "Fn::Join": 
    [ "",
         [
           "arn:aws:s3:::",
           {"Ref": "ImageBucketName","/*"}
         ]
    ]
}

Using Fn::Join for YAML

Resource:
  - Fn::Join:
      - ""
      - - "arn:aws:s3:::"
        - !Ref ImageBucketName
        - "/*"

We can also write Fn::Join for YAML like this.

  !Join [ "", [ "arn:aws:s3::", !Ref ImageBucketName, "/*" ] ]

As we can see, YMAL syntax for Fn::Join doesn't make a huge difference when it comes to readability, it's still the same clumsy syntax, one have to put some efforts in order to understand what it's doing, and for me it feels like I am reading a Regular Expression.

Coming to Fn::Sub

Even though it does the same exact thing, but it does it in a more straightforward manner, where Fn::Join produces a string using the supplied values, Fn::Sub Substitutes the values in the supplied string.

Let's se what I am talking about with a example

Using Fn:Sub

Resource: !Sub "arn:aws:s3:::${ImageBucketName}/*"

Simple isn't it? It's the same exact thing which we can see above using Fn::Join but this one is much simple and easy to read, In the preceding example, we have a string with ${} in it, and all ${} does is replace the value of the variable within it to the string, but there is more to Sub.

Sub allows you to provide your own parameters. For Example:

!Sub
 - 'arn:aws:s3:::${ImageBucketName}/*'
 - { ImageBucketName: Ref MyBucket }

The !Sub technique is much easier to understand, and when troubleshooting resource generation difficulties, it is much easier to do variable replacement to discover whether there is a mistake.

But there is a catch when it comes to Serverless Framework.

Fn:Sub in Serverless Framework

Using Fn::Sub in Serverless is a little troublesome, as Serverless and Fn::Sub have same syntax for ${}, to fix this issue we need to install a plugin from "npm".

npm install serverless-cloudformation-sub-variables

Once installed add this plugin to the plugin section to your serverless.yml file.

plugins:
  - serverless-cloudformation-sub-variables

What this plugin will do is basically replace ${} to #{} for Fn::Sub, so instead of using ${VarName} which causes issues in Serverless you can use #{VarName}.

Conclusion

They both do the same thing, and they both have up's and down's, "!Sub" is inconvenient when it comes to Serverless and "Fn::Join" is hard to read and work with, But I prefer that inconvenient nature of Fn::Sub as it not that big of a deal when compared to the Fn::Join's complexity.

Reference

  1. https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html
  2. https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html
  3. https://www.serverless.com/plugins/serverless-cloudformation-sub-variables