Hello!
In CloudFormation, it’s common to construct strings with the !Join
function. Like this example from AWS’s cfn-init
docs:
UserData: !Base64
'Fn::Join':
- ''
- - |
#!/bin/bash -xe
- |
# Install the files and packages from the metadata
- '/opt/aws/bin/cfn-init -v '
- ' --stack '
- !Ref 'AWS::StackName'
- ' --resource WebServerInstance '
- ' --configsets InstallAndRun '
- ' --region '
- !Ref 'AWS::Region'
- |+
Here’s the script this renders into:
#!/bin/bash -xe
# Install the files and packages from the metadata
/opt/aws/bin/cfn-init -v --stack test --resource WebServerInstance --configsets InstallAndRun --region us-west-2
To me, both are messy and hard to read. It abuses multiline string declarations (|
) to create single line breaks. Spaces are all over the place. There are YAML -
and '
characters everywhere.
I use the !Sub
function with one multi-line string instead:
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
# Install the files and packages from the metadata
/opt/aws/bin/cfn-init -v \
--stack ${AWS::StackName} \
--resource WebServerInstance \
--configsets InstallAndRun \
--region ${AWS::Region}
Fewer lines, no YAML syntax scattered around the script. It reads like a normal shell script except we can use ${}
wherever we’d have used a !Ref
. It renders like this:
#!/bin/bash -xe
# Install the files and packages from the metadata
/opt/aws/bin/cfn-init -v \
--stack test \
--resource WebServerInstance \
--configsets InstallAndRun \
--region us-west-2
I think both are much easier to read.
Some details:
- I used
Fn::Base64
instead of!Base64
because you can’t mix the long and short form in this case. - If your string needs values from other functions, like
!GetAtt
or!ImportValue
, check out this. - Every new line in the sub version is a new line in the rendered script. Like any multiline shell command it has to break lines between the arguments with
\
. The join version renders thecfn-init
command into one long line with a ton of spaces between the arguments, and a side effect is they don’t need the multiline command syntax. - The
${thing}
syntax of!Sub
is also a type shell variable expansion. Make sure you only use it for CloudFormation references. No problem for me because I only use CloudFormation to render super simple scripts that basically just callcfn-init
. Shell’s$thing
syntax is all I need. If your script is complex enough that this isn’t enough, I recommend reconsidering your approach. It’s usually an anti-pattern to use CloudFormation for those cases.
I almost always use !Sub
instead of !Join
. It lets you write strings like the strings they are, rather than polluting them with a bunch of YAML syntax.
Happy automating!
Adam
Need more than just this article? We’re available to consult.
You might also want to check out these related articles: