Skip to content

Nested Stacks

CloudFormation nested stacks (AWS::CloudFormation::Stack) let you decompose large templates into smaller, reusable child templates. The AWS lexicon’s nestedStack() function references a child project directory — a subdirectory that builds independently to a valid CloudFormation template.

A nested stack is a child project — a subdirectory with its own resource files and explicit stackOutput() declarations:

src/
app.ts # parent resources
network/ # ← child project (nested stack)
vpc.ts # VPC, subnet, internet gateway, routing
security.ts # security group for Lambda
outputs.ts # declares cross-stack outputs

Use stackOutput() to mark values that the parent can reference. Each stackOutput() becomes an entry in the child template’s Outputs section:

outputs.ts
/**
* Cross-stack outputs — values the parent can reference
*/
import { stackOutput } from "@intentius/chant";
import { vpc, subnet } from "./vpc";
import { lambdaSg } from "./security";
export const vpcId = stackOutput(vpc.VpcId, {
description: "VPC ID",
});
export const subnetId = stackOutput(subnet.SubnetId, {
description: "Public subnet ID",
});
export const lambdaSgId = stackOutput(lambdaSg.GroupId, {
description: "Lambda security group ID",
});

The child can be built independently:

Terminal window
chant build src/network/ -o network.json
# Produces a standalone, valid CloudFormation template with Outputs

Use nestedStack() in the parent to reference a child project directory. It returns an object with an outputs proxy for cross-stack references:

app.ts
/**
* App layer — Lambda function in the parent template that references
* the network nested stack's outputs via cross-stack references
*/
import { Function, Sub, AWS, Ref, nestedStack } from "@intentius/chant-lexicon-aws";
// nestedStack() references a child project directory
const network = nestedStack("network", import.meta.dirname + "/network", {
parameters: { Environment: "prod" },
});
export const handler = new Function({
FunctionName: Sub`${AWS.StackName}-handler`,
Runtime: "nodejs20.x",
Handler: "index.handler",
Role: Ref("LambdaExecutionRole"),
Code: { ZipFile: "exports.handler = async () => ({ statusCode: 200 });" },
VpcConfig: {
SubnetIds: [network.outputs.subnetId],
SecurityGroupIds: [network.outputs.lambdaSgId],
},
});
// Re-export so discovery picks it up as an entity
export { network };

network.outputs.subnetId produces a NestedStackOutputRef that serializes to { "Fn::GetAtt": ["Network", "Outputs.SubnetId"] }.

chant build produces multiple template files:

Terminal window
chant build -o template.json
# Produces:
# template.json — parent template
# network.template.json — child template

The parent template includes an AWS::CloudFormation::Stack resource pointing to the child:

"Network": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": { "Fn::Sub": "${TemplateBasePath}/network.template.json" }
}
}

Every parent template gets a TemplateBasePath parameter (default ".") that controls where CloudFormation looks for child templates:

Terminal window
# Local dev — default "." works with rain and similar tools
chant build -o template.json
# Production — override with S3 URL
aws cloudformation deploy \
--template-file template.json \
--stack-name my-stack \
--parameter-overrides TemplateBasePath=https://my-bucket.s3.amazonaws.com/templates

Child templates also receive the TemplateBasePath parameter so it propagates through all nesting levels.

All child template files must be uploaded alongside the parent template (or to the S3 path specified by TemplateBasePath).

Pass CloudFormation Parameters to child stacks with the parameters option:

import { nestedStack } from "@intentius/chant-lexicon-aws";
const network = nestedStack("network", import.meta.dirname + "/network", {
parameters: { Environment: "prod", CidrBlock: "10.0.0.0/16" },
});

Child projects can themselves reference grandchild projects. Each level produces its own template file:

src/
app.ts
infra/
network/
vpc.ts
outputs.ts
database/
cluster.ts
outputs.ts

The build pipeline detects circular references and reports an error if child A references child B which references child A.

Three lint rules help catch common nested stack issues:

RuleSeverityDescription
WAW013errorChild project has no stackOutput() exports — parent can’t reference anything
WAW014warningnestedStack() outputs never referenced from parent — could be a separate build
WAW015errorCircular project references

Prefer flat composites for most projects. Composites expand into a single template, deploy atomically, and are simpler to debug.

Use nested stacks only when:

  • Your template exceeds CloudFormation’s 500-resource limit
  • You’re packaging reusable infrastructure for other teams to deploy as a black box
  • You need independent update/rollback boundaries (rare — this usually means the resources should be separate stacks entirely)

See Composites for the flat composite approach.