CloudFormation protip: use !Sub instead of !Join
CloudFormation supports a number of intrinsic functions and Fn::Join
(or !Join
) is often used to construct parameterised names and paths.
The Serverless framework, for instance, uses it extensively. A quick look in a CloudFormation it generates I can see Fn::Join
used for:
- IAM policy names
- IAM role names
- IAM principals
- API Gateway URIs
- Resource ARNs
and many more.
But it’s not just the frameworks that are using Fn::Join
heavily. They also show up in our own code all the time as well. For example, to construct the ARN for a resource, or the URI for an API Gateway endpoint.
I find these very hard to comprehend, and my protip for you today is to use Fn::Sub
(or the !Sub
shorthand) instead.
Many folks would use Fn::Sub
when they need to reference pseudo parameters such as AWS::Region
and AWS::AccountId
, for example:
!Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${vpc}'
But Fn::Sub
also lets you provide your own parameters. For example:
!Sub
- 'arn:aws:s3:::${Bucket}/*'
- { Bucket: Ref MyBucket }
The advantage over Fn::Join
is that you can see the pattern of the interpolated string. Whereas with Fn::Join
you'll have to construct the pattern in your mind, which requires far more cognitive energy.
!Join
- ''
- - 'arn:aws:s3:::'
- !Ref MyBucket
- '/*'
Here are a few side-by-side comparisons to drive home the message.
Example 1: IAM role name
RoleName: # hello-world-dev-{region}-lambdaRole
!Join
- '-'
- - 'hello-world'
- 'dev'
- !Ref 'AWS::Region'
- 'lambdaRole'
with Fn::Sub
:
PolicyName:
!Sub 'hello-world-dev-${AWS::Region}-lambdaRole
Example 2: API Gateway integration URI
Uri: # arn:{partition}:apigateway:{region}:.../{lambda}/invocations
!Join
- ''
- - 'arn:'
- Ref: AWS::Partition
- ':apigateway:'
- Ref: AWS::Region
- ':lambda:path/2015-03-31/functions/'
- !GetAtt 'HelloLambdaFunction.Arn'
- '/invocations'
with Fn::Sub
:
Uri:
!Sub
- 'arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015/03/31/functions/${Function}/invocations'
- { Function: !GetAtt 'HelloLambdaFunction.Arn' }
Example 3: Lambda permission for API Gateway
SourceArn: # arn:{partition}:execute-api:{region}:.../*/*
!Join:
- ''
- - 'arn:'
- Ref: AWS::Partition
- ':execute-api:'
- Ref: AWS::Region
- ':'
- Ref: AWS::AccountId
- ':'
- Ref: ApiGatewayRestApi
- '/*/*'
with Fn::Sub
:
SourceArn:
!Sub
- 'arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*'
- { RestApi: Ref: ApiGatewayRestApi }
Suffice to say that the Fn::Sub
version is easier to understand in every case! Now that you have seen what Fn::Sub
can do, I hope you will prefer it to Fn::Join
going forward.
If you’re using the Serverless framework, keep in mind that Fn::Sub
is not natively supported. This is because the Serverless framework also uses the ${}
syntax to support its own variables system. However, as is often the case with the Serverless framework, you can work around this issue with a plugin. Check out the serverless-cloudformation-sub-variables plugin which lets you use Fn::Sub
in the serverless.yml
. You just need to use #{VariableName}
instead of ${VariableName}
.
Finally, if you’re using the Serverless framework and need more expressive power than the intrinsic functions can offer, then check out this plugin. It lets you use a number of “extrinsic” functions such as Fn::Substring
or Fn::StartsWith
anywhere in your serverless.yml
.
Hi, my name is Yan Cui. I’m an AWS Serverless Hero and the author of Production-Ready Serverless. I have run production workload at scale in AWS for nearly 10 years and I have been an architect or principal engineer with a variety of industries ranging from banking, e-commerce, sports streaming to mobile gaming. I currently work as an independent consultant focused on AWS and serverless.
Check out my new course, Complete Guide to AWS Step Functions.
In this course, we’ll cover everything you need to know to use AWS Step Functions service effectively. Including basic concepts, HTTP and event triggers, activities, design patterns and best practices.
Get your copy here.
Come learn about operational BEST PRACTICES for AWS Lambda: CI/CD, testing & debugging functions locally, logging, monitoring, distributed tracing, canary deployments, config management, authentication & authorization, VPC, security, error handling, and more.
You can also get 40% off the face price with the code ytcui.
Get your copy here.
Originally published at https://theburningmonk.com on May 12, 2019.