CloudFormation Nested Stacks

Learn to use CloudFormation nested stacks for complex apps in this AWS Cloud & DevOps with Python tutorial — hands-on steps and troubleshooting.

Focus: use cloudformation nested stacks for complex apps

Sponsored

You’ve mastered single CloudFormation templates, but as your application grows, a single template becomes a tangled web of resources, parameters, and outputs. Every small change risks breaking unrelated components, and team collaboration turns into merge conflicts. This lesson shows you how to use CloudFormation nested stacks for complex apps, breaking your infrastructure into modular, reusable building blocks that you can manage, test, and scale independently.

The problem this lesson solves

A monolithic CloudFormation template is manageable for a simple app, but for complex apps it quickly becomes a nightmare. Imagine you’re deploying a three-tier web application: a VPC, an Application Load Balancer (ALB), EC2 instances, an RDS database, and IAM roles. If all of that lives in one template, you face:

  • Long update cycles — every stack update re-evaluates all resources, even unchanged ones.
  • High blast radius — a syntax error in the database section can fail the entire stack, taking down your web servers.
  • Poor reusability — you can’t easily share the VPC or database setup across multiple environments or projects.
  • Difficult collaboration — multiple developers editing a single template inevitably create conflicts.

Nested stacks solve these problems by allowing you to compose a root stack that references other templates (called nested stacks). Each nested stack is managed as a separate CloudFormation stack, but the root stack treats them as a single unit. This modular approach gives you the best of both worlds: centralized orchestration and decentralized resource management.

Core concept / mental model

Think of nested stacks as infrastructure components built from LEGO bricks. Your root stack is the blueprint that defines how the bricks fit together, and each nested stack is a brick with a specific, well-defined function. You can swap a brick (update a nested stack) without rebuilding the entire castle.

Here’s the key idea: a nested stack is just a regular CloudFormation stack that is called by another stack. The root stack (the one you create or update) contains AWS::CloudFormation::Stack resources that point to the URLs of the child templates. When you deploy the root stack, CloudFormation creates each nested stack automatically, passing specified parameters and receiving outputs that you can use elsewhere in the root stack.

Key vocabulary

  • Root stack — the top-level stack you deploy; it orchestrates all nested stacks.
  • Nested stack — a child stack created by the root stack; it can also have its own nested stacks.
  • Template URL — the S3 URL of the nested template that CloudFormation downloads to create the stack.
  • Stack output — any output from a nested stack can be referenced in the root stack using Fn::GetAtt.

How it works step by step

Creating nested stacks involves a clear sequence of steps. Let’s walk through the process, from writing templates to deploying the root stack.

  1. Write modular templates — Create separate templates for each component (VPC, ALB, database, etc.). Each template defines its own parameters, resources, and outputs.
  2. Upload templates to S3 — Nested templates must be stored in an S3 bucket that CloudFormation can access. Use the same bucket for all nested templates, ideally versioned for rollback.
  3. Create the root template — The root template defines AWS::CloudFormation::Stack resources for each nested stack. For each, you specify the TemplateURL, any Parameters, and optionally TimeoutInMinutes.
  4. Wire up references — Use Fn::GetAtt with the logical name of the nested stack to retrieve outputs (e.g., SubnetIds, SecurityGroupId). Pass these as parameters to other nested stacks or use them for resources in the root stack.
  5. Deploy the root stack — Use the AWS CLI, SDK, or console to create the root stack. CloudFormation handles creating all nested stacks in the correct dependency order.
  6. Update and manage — To change a component, update its nested template and upload it to S3. Then update the root stack (or the nested stack directly) to apply changes.

Dependency handling

CloudFormation automatically resolves dependencies between nested stacks. If nested stack B depends on an output from nested stack A, CloudFormation waits until A is fully created before creating B. You can also use the DependsOn attribute to make an explicit dependency if needed.

Hands-on walkthrough

Let’s build a simple but realistic example: a root stack that sets up a VPC and an EC2 instance inside it. We’ll create two nested templates — one for the VPC and one for the EC2 instance — and orchestrate them with a root stack. We’ll use Python with the AWS SDK (boto3) to deploy the root stack, but you could also use the CLI.

Step 1: Create the VPC template

Save as vpc-template.yaml. This template creates a VPC with a public subnet and returns the subnet ID as an output.

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  VpcCidr:
    Type: String
    Default: '10.0.0.0/16'
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: '10.0.1.0/24'
Outputs:
  SubnetId:
    Value: !Ref PublicSubnet
    Export:
      Name: !Sub '${AWS::StackName}-SubnetId'

Step 2: Create the EC2 template

Save as ec2-template.yaml. This template creates an EC2 instance using the default Amazon Linux 2 AMI (we’ll hardcode the AMI ID for simplicity, but you’d use a parameter in production).

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  SubnetId:
    Type: String
Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: 'ami-0abcdef1234567890'  # replace with your region's AMI
      InstanceType: t2.micro
      SubnetId: !Ref SubnetId

Step 3: Create the root template

Save as root-template.yaml. It references both nested templates and passes the subnet ID from the VPC stack to the EC2 stack.

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  VPCStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: 'https://s3.amazonaws.com/my-bucket/vpc-template.yaml'
      Parameters:
        VpcCidr: '10.0.0.0/16'
  EC2Stack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: 'https://s3.amazonaws.com/my-bucket/ec2-template.yaml'
      Parameters:
        SubnetId: !GetAtt VPCStack.Outputs.SubnetId

Note: !GetAtt with a nested stack returns the output value specified in the nested stack's Outputs section.

Step 4: Deploy with boto3

Now, let’s deploy the root stack using Python and boto3. We’ll assume the templates are already in an S3 bucket.

import boto3
import time

cf = boto3.client('cloudformation')

stack_name = 'my-app-root'
template_url = 'https://s3.amazonaws.com/my-bucket/root-template.yaml'

# Create the root stack
cf.create_stack(
    StackName=stack_name,
    TemplateURL=template_url,
    Capabilities=['CAPABILITY_IAM']  # Needed if any nested stack creates IAM resources
)

print('Creating root stack...')

# Wait for the stack to reach CREATE_COMPLETE
waiter = cf.get_waiter('stack_create_complete')
waiter.wait(StackName=stack_name)

# Describe the stack and show status
stack = cf.describe_stacks(StackName=stack_name)['Stacks'][0]
print(f"Root stack status: {stack['StackStatus']}")

# List nested stacks
response = cf.list_stack_resources(StackName=stack_name)
for resource in response['StackResourceSummaries']:
    if resource['ResourceType'] == 'AWS::CloudFormation::Stack':
        print(f"Nested stack: {resource['LogicalResourceId']} -> {resource['PhysicalResourceId']}")

Expected output (abbreviated):

Creating root stack...
Root stack status: CREATE_COMPLETE
Nested stack: VPCStack -> arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-root-VPCStack-ABC123/...
Nested stack: EC2Stack -> arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-root-EC2Stack-DEF456/...

Pro tip: Use the list_stack_resources API to programmatically inspect your nested stacks. It’s invaluable for automated diagnostics.

Compare options / when to choose what

Nested stacks are not the only way to organize CloudFormation. Here’s how they stack up against common alternatives:

Approach Pros Cons Best for
Nested stacks Modular, reusable, isolated failures, central orchestration Harder to update individual stacks, S3 dependency, extra complexity Complex apps with clear component boundaries, team collaboration
Single stack Simplest, no extra setup Hard to maintain, large blast radius Prototypes, small apps
Stack sets Manage same template across multiple accounts/regions Limited use cases, complex Multi-account or multi-region deployments
Third-party IaC (Terraform, CDK) More features, easier testing Requires learning new tools, possible lock-in Teams already using those tools

When to choose nested stacks

Choose nested stacks when:

  • Your app has multiple distinct components (VPC, compute, database, etc.) that you want to version and review separately.
  • You need to reuse the same infrastructure (e.g., a VPC template) in multiple projects.
  • You want to reduce the impact of a single bad update.

Troubleshooting & edge cases

Working with nested stacks brings its own set of pitfalls. Here are the most common issues and how to fix them.

1. S3 bucket access denied

Error: S3 error: Access Denied when the root stack tries to fetch the nested template.

Fix: Ensure the S3 bucket policy allows CloudFormation to read the objects. You can either make the bucket public (not recommended) or add a bucket policy that grants read access to the service. Always check that the URL is correct — a typo is a common cause.

2. Outputs not available

Symptom: You get a Circular dependency or Unable to get parameter error when referencing a nested stack output.

Fix: Double-check the output name in the nested template and the syntax in the root stack. Use the exact logical name of the nested stack resource (e.g., VPCStack) and the output key. Also, verify that the output is exported? Actually, for Fn::GetAtt you don’t need to export the output; you reference it directly. If you use Export, you can use Fn::ImportValue instead.

3. Stack update drift

Symptom: Nested stacks become out of sync when you update them individually.

Fix: Always prefer updating the root stack, which triggers updates to child stacks as needed. If you must update a nested stack directly, be aware that the root stack still references the old version until the root stack is updated again.

4. Template size limits

Issue: Each template (including nested ones) must be under 51,200 bytes. If your nested template is larger, split it further.

Fix: Use the TemplateURL to reference the template from S3, but note that the root template itself also has a size limit. Break your components into smaller nested stacks if needed.

5. IAM permissions for nested stacks

Symptom: CreateStack fails with CAPABILITY_IAM error when a nested stack creates IAM resources.

Fix: You must add CAPABILITY_IAM (or CAPABILITY_NAMED_IAM) to the root stack creation call. This capability is required for any stack that creates IAM resources, including nested stacks.

What you learned & what's next

You now understand how to use CloudFormation nested stacks for complex apps. We covered:

  • The pain of monolithic templates and how nested stacks solve it.
  • The mental model of LEGO bricks — modular templates orchestrated by a root stack.
  • The step-by-step process of creating, uploading, and deploying nested stacks.
  • A hands-on example with VPC and EC2 nested stacks deployed via Python.
  • How to compare nested stacks with alternatives like single stacks, stack sets, and third-party IaC.
  • Common troubleshooting tips for S3 access, outputs, drift, and IAM.

You’ve achieved the learning objectives: explain the core idea behind nested stacks and complete a practical exercise.

Next, in the track, you’ll likely explore Change Sets, which let you preview changes before applying them — a natural companion to managing complex stacks. You’ll learn how to propose updates safely, review impact, and avoid accidental outages. Keep practicing with nested stacks to make modular infrastructure second nature.

Practice recap

To cement this lesson, create a nested stack structure for a simple web app: a nested stack for a VPC, one for a security group, and one for an EC2 instance. Write the templates, upload them to your S3 bucket, and deploy the root stack using boto3. Then update the EC2 instance type in the nested template and redeploy to see how the root stack handles it.

Common mistakes

  • Forgetting that nested templates must be in S3 — inline templates are not allowed, so you get a MalformedTemplate error.
  • Not adding CAPABILITY_IAM to the root stack when a nested stack creates IAM resources, causing a failure.
  • Using Fn::GetAtt with a nested stack logical name but misspelling the output key, leading to a 'Resource not found' error.
  • Updating nested stacks individually and then wondering why the root stack still shows old resource IDs.

Variations

  1. Use AWS CloudFormation StackSets to deploy the same nested stack structure across multiple accounts and regions automatically.
  2. Adopt AWS CDK (Python) to generate nested stacks programmatically, keeping templates as code but adding a higher-level language.
  3. Use Terraform modules instead of nested stacks if your team is already invested in HashiCorp tools.

Real-world use cases

  • E-commerce platform with separate VPC, database, and application tiers, versioned independently.
  • Microservices environment where each service has its own nested stack, sharing a common VPC stack.
  • Multi-environment setup (dev, staging, prod) reusing the same nested templates with different parameters.

Key takeaways

  • Nested stacks let you modularize CloudFormation templates, reducing blast radius and improving reusability.
  • A root stack orchestrates nested stacks via AWS::CloudFormation::Stack resources and can pass outputs as inputs.
  • Nested templates must be stored in S3 and referenced by URL — keep your bucket permissions tight.
  • Use Fn::GetAtt to extract outputs from nested stacks, and use parameters to pass values between them.
  • Always deploy through the root stack to keep nested stacks consistent and use CAPABILITY_IAM when needed.
  • Nested stacks are ideal for complex apps with clear component boundaries; for small apps, a single stack may suffice.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.