Exporting CloudFormation outputs is how you make values produced by one stack available for other stacks to use.
Let’s see it in action. Imagine you have a VPC stack that creates a VPC and a subnet. You want to launch an EC2 instance in that subnet, but the instance stack needs to know the subnet’s ID.
Here’s the VPCStack.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Description: Creates a VPC and a subnet
Resources:
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: MyCrossStackVPC
MySubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref MyVPC
CidrBlock: 10.0.0.0/24
Tags:
- Key: Name
Value: MyCrossStackSubnet
Outputs:
SubnetId:
Description: The ID of the created subnet
Value: !Ref MySubnet
Export:
Name: MyCrossStackSubnetId # This is the key for cross-stack reference
And here’s EC2InstanceStack.yaml that consumes that output:
AWSTemplateFormatVersion: '2010-09-09'
Description: Launches an EC2 instance in a specific subnet
Parameters:
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: The ID of the subnet to launch the instance into
# No Default here, we'll get it from the export
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0abcdef1234567890 # Replace with a valid AMI ID for your region
InstanceType: t2.micro
SubnetId: !Ref SubnetId
Tags:
- Key: Name
Value: MyCrossStackInstance
When you create the VPCStack, CloudFormation will output SubnetId with the value subnet-0123456789abcdef0. Crucially, the Export block tells CloudFormation to make this value available globally within your AWS account under the name MyCrossStackSubnetId.
Then, when you create the EC2InstanceStack, you don’t provide a default value for the SubnetId parameter. Instead, you can reference the exported output directly within the ec2-stack-create.sh script:
# First, create the VPC stack
aws cloudformation create-stack --stack-name vpc-stack --template-body file://VPCStack.yaml
# Wait for vpc-stack to complete, then get the exported output
VPC_STACK_OUTPUT=$(aws cloudformation describe-stacks --stack-name vpc-stack --query "Stacks[0].Outputs[?OutputKey=='SubnetId'].OutputValue" --output text)
# Create the EC2 instance stack, referencing the output
aws cloudformation create-stack --stack-name ec2-instance-stack \
--template-body file://EC2InstanceStack.yaml \
--parameters ParameterKey=SubnetId,ParameterValue=$VPC_STACK_OUTPUT
Notice how ParameterValue=$VPC_STACK_OUTPUT in the second create-stack command directly injects the subnet ID obtained from the first stack’s export.
Alternatively, and often more cleanly, you can reference the export directly in the template:
AWSTemplateFormatVersion: '2010-09-09'
Description: Launches an EC2 instance in a specific subnet
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0abcdef1234567890 # Replace with a valid AMI ID for your region
InstanceType: t2.micro
SubnetId: !ImportValue MyCrossStackSubnetId # Direct import
Tags:
- Key: Name
Value: MyCrossStackInstance
In this version of EC2InstanceStack.yaml, the SubnetId property uses !ImportValue MyCrossStackSubnetId. When you deploy this stack, CloudFormation looks up the export named MyCrossStackSubnetId from any other stack in your account and uses that value. This is the most common and recommended way to handle cross-stack references as it decouples the stack’s deployment from the specific stack name that produced the output.
The magic here is that the Export name (MyCrossStackSubnetId) is global within your AWS account and region. This allows any stack to import it, not just the one that explicitly created it. This is incredibly powerful for building modular infrastructure where common resources like VPCs, security groups, or RDS instances can be managed in dedicated stacks and then reused by many application-specific stacks.
The Export name must be unique within your AWS account and region. If you try to export a value with a name that’s already in use, CloudFormation will throw an error. This is a safety mechanism to prevent accidental overwrites of shared resources.
When a stack that exports a value is deleted, its exported values are also deleted. This means any stack that imports that value will fail to create or update until the dependency is resolved. You can prevent this by using the NoEcho property on the output, but that’s generally not recommended for cross-stack references as it makes debugging harder. A better approach is to ensure that stacks depending on an export are updated or deleted before the exporting stack.
The one thing most people don’t know is that the Export name is case-sensitive and must be a valid CloudFormation logical ID. You can’t use spaces or special characters that aren’t allowed in logical IDs. This is a subtle constraint that can trip you up if you’re not careful.
The next concept you’ll run into is managing the lifecycle of stacks that depend on exported values, especially during stack deletion.