In past few posts, we always automated our stack with cloudformation. Cloudformation template(CFT) is pretty handy tool for engineer to recreate entire stack repetitively. As we wrote more and more cloudformation template for clients, we found a few drawbacks in our monolithic cloudformation template. AWS offer nested cloudformation template. As we converting our template into nested format, we encountered a few challenge like passing resources in nested cloudformation, intrinsic import function. We will walk you through them with comprehensive example as always.
- Body Size limit – cloudformation has a 51200 bytes body size limit.
- Duplication – there are some basic resource elements defined multiple times across templates such as roles, security groups etc.
- Monolithic – we’d like to break templates into small modules, and assemble them as necessary.
Passing Resources:
First we need to break monolithic cloudformation template into pieces, so that we can share resources between the templates without duplication. Hence, we need a way of passing resources in nested cloudformation templates. It took us a while to figure out how one stack traverse resource name from other stack and passing resources in nested cloudformation templates.
Export stack name:
It’s important to export stack name first, because every resource exists in cloudformation template. In order to export resource, we need the ability to tell other template the origin of the resource. We can’t wrap our head around that at the beginning, but it makes sense at the end.
In cloudformation , you can declare “Outputs” section that it allows to import into other stacks. The follow example demonstrates how to export a stack name.
Outputs:
StackName:
Value: !Ref AWS::StackName
Export resource:
Now we can export resource to share with other template with name convention like ${AWS::StackName}-ResourceName.
Outputs:
ResourceByRef:
Value: !Ref 'Resource1'
Description: xxxxx
Export:
Name: !Sub "${AWS::StackName}-ResourceByRef"
ResourceArn:
Value: !Ref 'Resource2'
Description: xxxxx
Export:
Name: !Sub "${AWS::StackName}-ResourceArn"
Import resource:
Import resource is a little bit tricky. You don’t have to pass each resource as parameters to other template, all you need to do is to pass that stack as parameter. And look it up by name convention from previous output. e.g. ${AWS::StackName}-ResourceName
You also need to utilize intrinsic function. e.g Fn::ImportValue: !Sub "${AWS::StackName}-ResourceByRef"
Example(put everything together):
Master template:
Description: >
A master template for demo https://blog.ascendingdc.com/passing-paramete…d-cloudformation/
Parameters:
S3Location:
Description: Your S3 location to store cloudformation template
Type: String
Default: "https://s3.amazonaws.com/tutorial-leyi/blog/"
MinLength: 1
Resources:
Bucket:
Type: "AWS::CloudFormation::Stack"
Properties:
TemplateURL: !Join ["",[!Ref S3Location,"template1.yml"]]
EC2Role:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
TemplateStack: !GetAtt Bucket.Outputs.StackName
TemplateURL: !Join ["",[!Ref S3Location,"template2.yml"]]
Template 1:
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Resource 1 template to create s3 bucket for export
Resources:
DataStorage:
Type: "AWS::S3::Bucket"
Properties:
Tags:
- Key: "product"
Value: "sample"
Outputs:
DataStorageArn:
Value: !GetAtt DataStorage.Arn
Description: S3 bucket arn
Export:
Name: !Sub "${AWS::StackName}-DataStorage"
StackName:
Value: !Ref AWS::StackName
Template 2:
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Resource 2 grant role for s3 bucket
Parameters:
TemplateStack:
Description: template1 stack name
Type: String
AllowedPattern : "^[a-zA-Z][-a-zA-Z0-9]*$"
MinLength: 1
MaxLength : 1024
Resources:
ec2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: log-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
- PolicyName: s3-policy
PolicyDocument:
Statement:
- Effect: Allow
Action: ['s3:*']
Resource:
- Fn::ImportValue: !Sub "${TemplateStack}-DataStorage"
Ryo Hang
Solution Architect @ASCENDING