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