Passing Resources in Nested Cloudformation

Read Time 4 mins | Written by: Ryo Hang

passing resources in nested cloudformationIn 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.

  1. Body Size limit – cloudformation has a 51200 bytes body size limit.
  2. Duplication – there are some basic resource elements defined multiple times across templates such as roles, security groups etc.
  3. 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):

example Passing Resources in Nested Cloudformation

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