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
.
The post CloudFormation protip: use !Sub instead of !Join appeared first on theburningmonk.com.
Top comments (0)