Free — no signup required

Cross-Stack References

3 min read

Connecting the Pieces

Real infrastructure is never a single monolithic stack. You split it into layers: Networking (VPC, subnets, security groups), Data (RDS, ElastiCache), and Application (ECS services, Lambda functions). Each layer is a separate stack, deployed independently. But the Application stack needs the VPC ID from the Networking stack. How do you pass values between stacks?

This is the cross-stack reference problem, and CDK and Pulumi solve it differently.

CDK: Automatic Wiring

In CDK, if both stacks are in the same CDK app, you simply pass the object reference — the app entry point instantiates the networking stack, then instantiates the application stack passing the networking stack's VPC object directly as a prop. CDK figures out the rest. Behind the scenes, CDK:
1. Adds a CloudFormation Export to the Networking stack: Export Name: Networking-VpcId, Value: !Ref VPC
2. Adds an Fn::ImportValue to the Application stack: Fn::ImportValue: Networking-VpcId

You never write this boilerplate. CDK detects the cross-stack dependency from the object reference and generates the correct CloudFormation.

The catch: CloudFormation exports create a hard dependency. You cannot delete or rename the Networking stack while the Application stack is importing its values. This is a real operational constraint — plan your stack boundaries carefully.

Pulumi: Explicit Stack References

Pulumi uses an explicit StackReference object. You name the stack you want to read from, pull a specific output from it by key, and use that value as an input to a new resource. The value returned is a pulumi.Output<string> — a promise-like value that Pulumi resolves at deploy time by reading the networking stack's state. This is explicit and transparent: you can see exactly which stacks depend on which outputs.

The catch: The networking stack must be deployed before the application stack, and the output key ("vpcId") must match exactly. Typos in output names cause runtime errors, not compile-time errors — unless you use TypeScript's type system carefully.

Choosing Between the Approaches

Concern CDK Pulumi
Ease of use Automatic — just pass objects Explicit — must know output names
Visibility Hidden in generated CloudFormation Explicit in code
Cross-account Requires manual export/import Supported natively via StackReference
Decoupling Hard dependency via CloudFormation exports Looser — reads from state at deploy time

Key Point: CDK's automatic cross-stack wiring is convenient but creates tight CloudFormation-level coupling. Pulumi's explicit StackReference is more verbose but makes dependencies visible in code and supports cross-account scenarios more naturally. For large organizations with many teams, the explicitness of Pulumi's approach often wins — you can grep for StackReference to understand the full dependency graph of your infrastructure.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free