Nesting Stacks in AWS CDK

This article describes how to nest Stacks when deploying with AWS CDK.

Introduction

When I tried to nest Stacks in AWS CDK, I discovered that simply nesting them wouldn't deploy successfully.

Nesting Stacks requires a specific approach, so I'm documenting the method here for future reference.

The code used in this article is also available on GitHub: github.com

Note: This article was translated from my original post.

Environment

Here's the environment used for this implementation:

CDK version:

$ cdk --version
1.31.0 (build 8f3ac79)


I used Python as the programming language. Python version:

$ python --version
Python 3.6.10


This was performed on Cloud9 with Amazon Linux.

$ cat /etc/system-release
Amazon Linux AMI release 2018.03

Nesting Stacks in CDK

Problem: Stacks Cannot Be Simply Nested

The issue is that Stacks cannot be nested directly.

For example, let's say you create a simple nested Stack structure like this:

from aws_cdk import core
import aws_cdk.aws_s3 as s3


class ParentStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        ChildStack01(self, "ChildStack01")
        ChildStack02(self, "ChildStack02")
        
        
class ChildStack01(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        s3.Bucket(self, "ChildBucket01", bucket_name="child-bucket-01")
        
        
class ChildStack02(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        s3.Bucket(self, "ChildBucket02", bucket_name="child-bucket-02")


app = core.App()
ParentStack(app, "ParentStack")
app.synth()


If we visualize this CDK code, it creates the following parent-child relationship: The expected behavior is that deploying the parent Stack ParentStack would also deploy the child Stacks simultaneously.

However, when I deployed the parent Stack in this state, neither the child Stacks nor the S3 resources were actually deployed.


At first glance, running cdk synth and cdk deploy from the CDK command appears to work:

$ cdk synth
Successfully synthesized to /home/ec2-user/environment/cdk-test/cdk.out
Supply a stack id (ParentStack, ParentStackChildStack01796333D2, ParentStackChildStack0287B3CF12) to display its template.

$ cdk deploy ParentStack
ParentStack: deploying...
ParentStack: creating CloudFormation changeset...
 0/2 | 11:49:11 PM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata 
 0/2 | 11:49:13 PM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata Resource creation Initiated
 1/2 | 11:49:13 PM | CREATE_COMPLETE      | AWS::CDK::Metadata | CDKMetadata 
 2/2 | 11:49:15 PM | CREATE_COMPLETE      | AWS::CloudFormation::Stack | ParentStack 

   ParentStack


However, examining the contents of the created CloudFormation Stack ParentStack reveals that no resources were actually deployed.

The CloudFormation console shows that ParentStack was created
The only deployed resource was metadata; everything else was empty

As you can see, simply nesting Stacks doesn't produce the expected behavior.

Solution: Use NestedStack

The solution to this problem is simpler than expected.

Instead of having child Stacks inherit from core.Stack, you should have them inherit from NestedStack in aws_cloudformation.

The necessary changes are these two:

  • Add import aws_cdk.aws_cloudformation as cfn
  • Change child Stack inheritance from core.Stack to cfn.NestedStack

※If you haven't installed the CDK cloudformation package yet, install it with pip install aws-cdk.aws-cloudformation.

Here's the rewritten CDK code:

from aws_cdk import core
import aws_cdk.aws_s3 as s3
import aws_cdk.aws_cloudformation as cfn # Import cloudformation


class ParentStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        ChildStack01(self, "ChildStack01")
        ChildStack02(self, "ChildStack02")
        
        
class ChildStack01(cfn.NestedStack): # Inherit from NestedStack

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
        
        s3.Bucket(self, "ChildBucket01", bucket_name="child-bucket-01")
        
        
class ChildStack02(cfn.NestedStack): # Inherit from NestedStack

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        s3.Bucket(self, "ChildBucket02", bucket_name="child-bucket-02")


app = core.App()
ParentStack(app, "ParentStack")
app.synth()


Now let's deploy the rewritten CDK code with cdk deploy and check the CloudFormation console again.

As you can see, in addition to the parent Stack, the child Stacks were deployed as Nested Stacks.

These two child Stacks are deployed as resources of the parent Stack.

Child Stacks are deployed from the parent Stack

This way, we successfully deployed nested CDK Stacks.

Conclusion

In this article, I documented how to nest CDK Stacks.

Since the solution was straightforward, I found NestedStack to be quite useful.

The more I learn about CDK, the more I appreciate its convenience.

[Related Articles]

en.bioerrorlog.work

References