You can reuse CloudFormation template fragments across stacks, but it’s not through direct inclusion or inheritance like in some programming languages. Instead, you leverage CloudFormation’s AWS::CloudFormation::Stack resource to embed one template within another.
Let’s see this in action. Imagine you have a common set of security group rules you want to apply to multiple EC2 instances. Instead of copying and pasting that AWS::EC2::SecurityGroup definition into every main template, you can create a separate, smaller template file for just that security group.
security-group-template.yaml:
Resources:
MyCommonSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow SSH and HTTP access
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Now, in your main application stack template, you can include this fragment using the AWS::CloudFormation::Stack resource.
main-app-stack.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Description: Main application stack that includes a common security group.
Resources:
# This resource creates a nested stack from our fragment template
MySecurityGroupStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://your-s3-bucket.s3.amazonaws.com/security-group-template.yaml # URL where your fragment is uploaded
Parameters: # You can pass parameters to the nested stack if it defines them
# Example: if security-group-template.yaml had a parameter for CidrIp
# CidrIp: !Ref MyCustomCidr
pass # No parameters defined in our simple example
# Example of another resource in the main stack that uses the nested stack's output
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0abcdef1234567890 # Replace with a valid AMI ID
InstanceType: t2.micro
SecurityGroups:
- !GetAtt MySecurityGroupStack.Outputs.MyCommonSecurityGroup # Referencing the Security Group created by the nested stack
To make this work, you first need to upload your security-group-template.yaml file to an S3 bucket that CloudFormation can access (typically in the same region or a globally accessible one). The TemplateURL property in the AWS::CloudFormation::Stack resource points to this S3 object.
When CloudFormation processes main-app-stack.yaml, it sees the AWS::CloudFormation::Stack resource. It then fetches the template from the TemplateURL, creates a new, independent stack based on that fragment, and associates it with your main stack. The resources defined in the fragment (in this case, the security group) are created as part of the main stack’s deployment. You can then reference outputs from this nested stack, just like you would with any other CloudFormation resource, using !GetAtt or !Ref.
This approach solves the problem of repetitive resource definitions and promotes a modular, DRY (Don’t Repeat Yourself) architecture for your infrastructure. You can manage and update the common security group logic in one place (security-group-template.yaml), and any stack that includes it via AWS::CloudFormation::Stack will automatically pick up the changes on its next deployment.
What’s often overlooked is that each AWS::CloudFormation::Stack resource creates a completely separate CloudFormation stack. This means it has its own stack ID, its own events, and its own lifecycle. If you delete the main stack, CloudFormation will prompt you to confirm whether to delete the nested stacks too. This isolation is powerful for managing dependencies and rollbacks independently, but it also means you can’t directly modify a resource within a nested stack from the parent template; you must update the nested template itself and redeploy the parent.
The next step is understanding how to pass parameters into these nested stacks from the parent template, allowing for more dynamic and reusable fragments.