What Are Stack References?
Why Stack References Matter
The Problem: Large infrastructure projects become unwieldy in a single stack. Teams need to split infrastructure into logical units while still sharing data between them.
The Solution: Stack references let one Pulumi stack read the outputs of another stack, enabling modular infrastructure with clear boundaries and dependencies.
Real Impact: Organizations can decompose monolithic infrastructure into micro-stacks managed by different teams, each deploying independently while staying connected.
Real-World Analogy
Think of stack references like building a city:
- Network Stack = The city's road system and utilities (built first)
- Database Stack = The data centers (needs road addresses from network)
- App Stack = The office buildings (needs roads and data center connections)
- Stack Outputs = The published addresses and connection points
- Stack References = Looking up addresses in the city directory
Stack Reference Benefits
Team Autonomy
Different teams can own and deploy their stacks independently without affecting other stacks.
Blast Radius
A failed deployment only affects one stack, not the entire infrastructure.
Faster Deployments
Smaller stacks deploy faster because Pulumi processes fewer resources.
Clear Contracts
Stack outputs define clear interfaces between infrastructure components.
Creating Outputs
import * as aws from "@pulumi/aws";
// Create networking infrastructure
const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
enableDnsHostnames: true,
});
const publicSubnet = new aws.ec2.Subnet("public", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
mapPublicIpOnLaunch: true,
});
const privateSubnet = new aws.ec2.Subnet("private", {
vpcId: vpc.id,
cidrBlock: "10.0.2.0/24",
});
// Export values for other stacks to consume
export const vpcId = vpc.id;
export const publicSubnetId = publicSubnet.id;
export const privateSubnetId = privateSubnet.id;
Consuming Stack References
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Reference the network stack
const stack = pulumi.getStack(); // "dev", "prod", etc.
const networkRef = new pulumi.StackReference(`org/network-stack/${stack}`);
// Read outputs from the network stack
const vpcId = networkRef.getOutput("vpcId");
const subnetId = networkRef.getOutput("privateSubnetId");
// Use the outputs as inputs to new resources
const securityGroup = new aws.ec2.SecurityGroup("app-sg", {
vpcId: vpcId,
ingress: [{
protocol: "tcp",
fromPort: 8080,
toPort: 8080,
cidrBlocks: ["10.0.0.0/16"],
}],
});
// Read a secret output (remains encrypted)
const dbPassword = networkRef.getOutput("dbPassword");
Cross-Stack Patterns
Common Micro-Stack Architecture
- Layer 1 - Network: VPC, subnets, NAT gateways, route tables
- Layer 2 - Security: IAM roles, security groups, KMS keys
- Layer 3 - Data: RDS databases, ElastiCache, S3 buckets
- Layer 4 - Compute: ECS/EKS clusters, Lambda functions, load balancers
- Layer 5 - App: Application services, API Gateway, CloudFront
Organizational Patterns
import * as pulumi from "@pulumi/pulumi";
const stack = pulumi.getStack();
const org = pulumi.getOrganization();
const config = new pulumi.Config();
// Pattern: Same environment across stacks
const networkRef = new pulumi.StackReference(
`${org}/network/${stack}`
);
const dataRef = new pulumi.StackReference(
`${org}/database/${stack}`
);
// Pattern: Shared services stack (always prod)
const sharedRef = new pulumi.StackReference(
`${org}/shared-services/prod`
);
// Use typed getOutput with requireOutput
const vpcId = networkRef.requireOutput("vpcId");
const dbEndpoint = dataRef.requireOutput("endpoint");
const sharedBucketArn = sharedRef.requireOutput("logBucketArn");
Common Pitfall
Problem: Circular dependencies between stacks (Stack A references Stack B, which references Stack A).
Solution: Design your stack architecture as a directed acyclic graph (DAG). Lower-level stacks (network) should never reference higher-level stacks (app). If two stacks need to share data, extract the shared part into a lower-level stack.
Quick Reference
| Method | Description | Example |
|---|---|---|
new StackReference() | Create a stack reference | new pulumi.StackReference("org/project/stack") |
.getOutput() | Get an output (optional) | ref.getOutput("vpcId") |
.requireOutput() | Get required output (throws) | ref.requireOutput("vpcId") |
.getOutputDetails() | Get output with secret info | ref.getOutputDetails("password") |
export const | Export value for other stacks | export const vpcId = vpc.id |
pulumi.getStack() | Get current stack name | const env = pulumi.getStack() |
pulumi.getOrganization() | Get current organization | const org = pulumi.getOrganization() |