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.

Want structured learning?

Take the full Cloudformation course →