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.
Project structure
Section titled “Project structure”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 outputsDeclaring outputs in the child
Section titled “Declaring outputs in the child”Use stackOutput() to mark values that the parent can reference. Each stackOutput() becomes an entry in the child template’s Outputs section:
/** * 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:
chant build src/network/ -o network.json# Produces a standalone, valid CloudFormation template with OutputsReferencing from the parent
Section titled “Referencing from the parent”Use nestedStack() in the parent to reference a child project directory. It returns an object with an outputs proxy for cross-stack references:
/** * 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 directoryconst 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 entityexport { network };network.outputs.subnetId produces a NestedStackOutputRef that serializes to { "Fn::GetAtt": ["Network", "Outputs.SubnetId"] }.
Build output
Section titled “Build output”chant build produces multiple template files:
chant build -o template.json# Produces:# template.json — parent template# network.template.json — child templateThe 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" } }}TemplateBasePath parameter
Section titled “TemplateBasePath parameter”Every parent template gets a TemplateBasePath parameter (default ".") that controls where CloudFormation looks for child templates:
# Local dev — default "." works with rain and similar toolschant build -o template.json
# Production — override with S3 URLaws cloudformation deploy \ --template-file template.json \ --stack-name my-stack \ --parameter-overrides TemplateBasePath=https://my-bucket.s3.amazonaws.com/templatesChild 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).
Explicit parameters
Section titled “Explicit parameters”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" },});Recursive nesting
Section titled “Recursive nesting”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.tsThe build pipeline detects circular references and reports an error if child A references child B which references child A.
Lint rules
Section titled “Lint rules”Three lint rules help catch common nested stack issues:
| Rule | Severity | Description |
|---|---|---|
| WAW013 | error | Child project has no stackOutput() exports — parent can’t reference anything |
| WAW014 | warning | nestedStack() outputs never referenced from parent — could be a separate build |
| WAW015 | error | Circular project references |
When to use nested stacks
Section titled “When to use nested stacks”Most splitting use cases are better served by other mechanisms:
- Splitting a large project into separately-deployed pieces → Multi-Stack Projects. Organize
src/into subdirectories — each becomes an independent stack, deployed on its own, with cross-stack references resolved via standard CloudFormationExport/ImportValue. No parent template, no S3 upload of children, noTemplateBasePathparameter. - Reusing resource patterns within a single template → Composites. Composites expand into the same template and deploy atomically — they’re about DRY, not splitting.
Use nested stacks only when:
- A single deployable unit exceeds CloudFormation’s 500-resource limit
- You’re packaging reusable infrastructure for other teams to consume as a black box (the parent-child relationship is load-bearing)
- You need an orchestrator parent that coordinates child lifecycles via CloudFormation Parameters and Outputs specifically