Passing values between CloudFormation stacks: Export/Import, Nested Stacks, SSM Parameter Store
Once infrastructure is split across multiple CloudFormation stacks, you always run into the same question: how does one stack reference another stack’s resource ID or ARN? This article covers the three common approaches — Export/Fn::ImportValue, Nested Stacks, and SSM Parameter Store — how each works, and when to reach for it.
1. Export / Fn::ImportValue
The producing stack defines an Export under Outputs; the consuming stack reads it with Fn::ImportValue.
# Producing stack
Outputs:
VpcId:
Value: !Ref Vpc
Export:
Name: shared-network-vpc-id
# Consuming stack
Resources:
WebSg:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !ImportValue shared-network-vpc-id
Characteristics
- Native CloudFormation functionality — no extra resources needed.
- An exported value cannot be updated or the producing stack deleted while even one other stack still imports it (this also blocks updates that would change the value itself). This tight coupling is its biggest weakness.
- Export names must be globally unique within the region, so naming collisions need care.
Good fit: distributing rarely-changing foundational values (a VPC ID, a base domain name) to many stacks.
2. Nested Stacks
The parent stack invokes a child template as an AWS::CloudFormation::Stack resource, and reads the child’s Outputs directly in the parent template via !GetAtt ChildStack.Outputs.XXX.
Resources:
NetworkStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://.../network.yaml
AppStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://.../app.yaml
Parameters:
VpcId: !GetAtt NetworkStack.Outputs.VpcId
Characteristics
- Parent and children are managed as a single deployment unit —
update-stack/delete-stackon the parent cascades to the children. - Avoids the “can’t delete because something else still references it” problem that Export/Import has, since everything is scoped to the parent’s lifecycle.
- The trade-off: child templates must be uploaded to S3 first, adding a build/deploy step.
- Parent-stack events, drift detection and rollbacks cascade into the children too, widening the blast radius of any deploy. Too many stacks nested too deep also gets hard to reason about.
Good fit: tightly-coupled resource groups that are always deployed and torn down together (e.g. the Lambda + DynamoDB + API Gateway set that makes up one microservice), packaged as a single template.
3. SSM Parameter Store
Values live outside any stack, in Systems Manager Parameter Store; both the producing and consuming stacks read/write through it.
# Producer: write the value as a parameter
Resources:
VpcIdParam:
Type: AWS::SSM::Parameter
Properties:
Name: /shared/network/vpc-id
Type: String
Value: !Ref Vpc
# Consumer: read it via a dynamic reference (resolved at deploy time)
Resources:
WebSg:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: "{{resolve:ssm:/shared/network/vpc-id}}"
Characteristics
- Stacks have no direct CloudFormation-level dependency on each other — none of Export/Import’s “can’t delete because it’s referenced” constraint.
{{resolve:ssm:...}}resolves the value at deploy time, so updating the parameter does not automatically propagate to stacks that reference it — they need an explicit redeploy. This constraint is similar to Export/Import.- Values can be shared across stacks, repos and even teams, and read from outside CloudFormation entirely (CLI, Lambda, other IaC tools) — much more general-purpose.
- Parameters can carry their own IAM policies, making it easy to control who can read or write which value.
Good fit: loose coupling across teams, or when tools other than CloudFormation (CDK, Terraform, application code) need to read the same value.
Comparison
| Approach | Coupling | Deletion constraint | Scope | Extra cost |
|---|---|---|---|---|
| Export / Fn::ImportValue | Tightly coupled between stacks | Can’t update/delete the producer while any importer still exists | CloudFormation stacks in the same region only | None |
| Nested Stack | Tightly coupled parent/child (one deploy unit) | Deleting the parent deletes the children; well isolated from other stacks | Scoped under the parent template | Child templates must live in S3 |
| SSM Parameter Store | Loosely coupled | No constraint — parameters exist independently | Readable from outside CloudFormation, across teams | Scales with parameter count (Standard tier has a free allowance) |
When to reach for each
- Low-churn foundational values owned by one team (a VPC ID, a shared KMS key) shared across a handful of stacks → plain Export/Import is usually enough, as long as you’re aware updates/deletes get harder over time.
- Resource groups that are always deployed/destroyed together and worth packaging as a template → Nested Stacks, keeping an eye on nesting depth.
- Cross-team/cross-repo collaboration, or values that need to be read by tools other than CloudFormation → SSM Parameter Store is the most flexible, and increasingly the default for teams that prioritize loose coupling.
All three trade off the same axis: tightly coupled and simple but harder to change, versus loosely coupled and flexible but more moving parts to manage. Pick based on how the stacks are split, team structure, and how often the shared value actually changes.