Simplifying CloudFormation deployments using Nested Stacks


Brett Gillett

A few weeks ago, I talked about the concept of using multiple small CloudFormation templates in a single project. If we keep templates small, they are 1) easier to manage, 2) easier to troubleshoot and 3) less likely to lead to failed deploys after making any updates - because they are easy to understand.

There are two methods of tying CloudFormation templates together - nesting and chaining. In this article, I’ll discuss how you can nest multiple templates to cover all the components of your project.

Here’s an example of a project we worked on recently that consisted of multiple small templates.

CloudFormation small Stacks

Nesting CloudFormation stacks is the process of relating templates using the AWS::CloudFormation::Stack resource type.

Here’s an example of a simple nested stack.

CloudFormation Nested Stacks

Your first step is to create the parent template. Think of this template as your starting point - this is the template you’ll refer to in the CloudFormation console or in your CI/CD pipeline to start the deployment.

The purpose of the parent template is to define all the stacks that you require for your solution.

Here’s a snippet of CloudFormation code that you may include in your parent template.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Resources:
    vpcStack:
        Type: AWS::CloudFormation::Stack
        Properties:
            Parameters:
                vpcCIDR: !Ref vpcCIDR
            TemplateURL: https://s3.amazonaws.com/${bucketName}/vpc.yml
            

    sgStack:
        Type: AWS::CloudFormation::Stack
        DependsOn:
            - vpcStack
        Properties:
            Parameters:
                vpcId: !GetAtt vpcStack.Outputs.vpcId
            TemplateURL: https://s3.amazonaws.com/${bucketName}/sg.yml
            

If you already have experience with CloudFormation, then the above code should look familiar.

We start by defining logical names - one for each stack. For example, I defined two logical resources in the above code snippet - vpcStack and sgStack. Once we’ve decided on the logical names for the resources, we can then determine the resource type - in this case, all the resources in the parent template are other CloudFormation stacks.

The AWS::CloudFormation::Stack resource is simple to define and only requires the TemplateURL property, which is how you tell CloudFormation to find the template for the logical resource.

In the above example, I’ve also included the Parameter property. Parameters are only required if child template you’d like to deploy requires input parameters. To deploy the VPC I refer to parameters I’ve defined in the parent template itself. Without information about the VPC, I cannot deploy the security groups.

You’ll notice a few slight differences in the second resource - sgStack. First, I’ve used the DependsOn attribute, which ensures the VPC is deployed successfully before creating any security groups.

In the Parameters section of the sgStack resource, you’ll notice that I use a CloudFormation intrinsic function - GetAtt.
The GetAtt function allows us to refer to an output we defined in another stack. In this instance, I need the VPC Id to associate any security groups successfully with the correct VPC. Here’s what the output looks like in the VPC stack.

1
2
3
4
5
Outputs:
    vpcId:
        Value: !Ref vpc
        Export:
            Name: !Sub ‘${AWS::StackName}-id’

In the sgStack resource definition, I refer to the output value using the GetAtt function to retrieve the information I need to deploy our security groups.

I hope it’s easy to see how flexible the nesting of CloudFormation templates is. For example, If I needed to add EC2 instances to our VPC, I could simply update the parent template and add a new logical resource. For example, the following would work.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Resource:
    
    ec2Stack:
        Type: AWS::CloudFormation::Stack
        DependsOn:
vpcStack
sgStack
        Properties:
            Parameters:
                securityGroupId: !GetAtt sgStack.Outputs.hostSecurityGroup
                subnetId: !GetAtt vpcStack.Outputs.privateSubnet0
            TemplateURL: !Sub ‘https://s3.amazonaws.com/${bucketName}/ec2.yml’
            

So there you have it, one of the methods you can use to tie many small CloudFormation templates together.

The one challenge that I have with nested stacks is that they can become complicated to manage, but this depends on the number of stacks you define and how they are related to one another.


Brett Gillett


Orbit

Like what you read? Why not subscribe to the weekly Orbit newsletter and get content before everyone else?