I was refactoring a CloudFormation setup where multiple stacks imported outputs from a shared VPC stack. Everything worked until I tried to update the VPC stack—CloudFormation refused, saying the export was in use by other stacks and couldn’t be deleted. Later, I discovered the imported value had become stale. In this post, I’ll walk through exactly what causes this and how to fix it.

The Problem

CloudFormation cross-stack reference operations fail with various errors:

Export [ExportName] cannot be deleted as it is in use by [StackName]
Output [OutputName] is not exported and cannot be imported
Fn::ImportValue returned an unexpected value
Stack update failed due to import dependency

Common cross-stack reference errors:

Error Type Description
Export in use Trying to delete/rename export that another stack imports
Export name conflict Two stacks exporting the same name in same region
Stale import value Exporting stack updated but importing stack doesn’t reflect new value
Circular dependency Stack A imports from B, B imports from A (via indirect reference)
Nested stack import issue Nested stack tries to import from parent or sibling stack

Why Does This Happen?

  • Trying to delete an export while it’s imported — You update the exporting stack to remove an output, but the importing stack still references it via Fn::ImportValue.
  • Export name collision — Two different stacks export the same name in the same region. Export names must be globally unique per region.
  • Imported value becomes stale — The exporting stack updated the output value, but the importing stack hasn’t been updated to re-evaluate the import.
  • Circular cross-stack dependency — Stack A imports from B (direct or indirect), and B imports from A, creating an impossible update order.
  • Nested stack import limitations — Nested stacks have restrictions on importing from parent/sibling stacks or exporting across boundaries.

The Fix

Option 1: Update All Importing Stacks Before Changing Export

To safely update or remove an export, first update all stacks that import it:

# First, find all stacks that import the export
aws cloudformation list-imports \
  --export-name MyVpcId \
  --query "Imports[].StackName" \
  --output text

# Update each importing stack to stop using the import
# (Replace the Fn::ImportValue with a hardcoded value or parameter)
# Then update the importing stack
aws cloudformation update-stack \
  --stack-name importing-stack \
  --template-body file://updated-template-without-import.yaml

# Wait for update to complete
aws cloudformation wait stack-update-complete --stack-name importing-stack

# Only after all imports are removed, update the exporting stack
aws cloudformation update-stack \
  --stack-name exporting-stack \
  --template-body file://template-without-export.yaml

Option 2: Use Unique Export Names to Avoid Conflicts

If you have multiple stacks exporting similar values, use unique, descriptive names:

Exporting stack:

Outputs:
  VpcId:
    Description: VPC ID for production
    Value: !Ref MyVpc
    Export:
      Name: prod-vpc-id-us-east-1  # Unique name with environment/region

  SubnetId:
    Description: Private subnet
    Value: !Ref PrivateSubnet
    Export:
      Name: prod-private-subnet-us-east-1  # Unique, descriptive name

Importing stack:

Parameters:
  VpcId:
    Type: String
    Default: !Sub
      - "{{resolve:cloudformation:${StackName}:Outputs.VpcId}}"
      - StackName: prod-vpc-stack

Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !ImportValue prod-vpc-id-us-east-1

Option 3: Replace Cross-Stack Exports with SSM Parameter Store

For complex dependencies, use SSM Parameter Store instead of cross-stack exports to avoid hard dependencies:

Exporting stack writes to SSM:

Resources:
  VpcIdParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /prod/vpc/id
      Type: String
      Value: !Ref MyVpc
      Description: Production VPC ID

Importing stack reads from SSM:

Parameters:
  VpcId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /prod/vpc/id
    Description: VPC ID from SSM Parameter Store

Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VpcId

This approach eliminates hard cross-stack dependencies—you can update either stack independently.

Option 4: Audit and Fix Circular Dependencies

If you suspect circular imports, trace the dependency chain:

# List all stacks
aws cloudformation list-stacks \
  --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE \
  --query "StackSummaries[].StackName" \
  --output text

# For each stack, check what it imports and exports
for stack in stack1 stack2 stack3; do
  echo "=== $stack ==="
  aws cloudformation describe-stacks --stack-name $stack \
    --query "Stacks[0].Outputs[?Export].{Export:Export.Name,Value:OutputValue}"
done

Document dependencies and manually verify no cycles exist. If you find a cycle, break it by:

  • Converting one cross-stack export to a parameter
  • Using SSM Parameter Store for one dependency
  • Restructuring stacks into a non-circular hierarchy

Option 5: Refresh Stale Import Values

If an import value is stale, update the importing stack to re-evaluate it:

# Update the importing stack without changing the template
# This forces re-evaluation of all Fn::ImportValue references
aws cloudformation update-stack \
  --stack-name importing-stack \
  --use-previous-template

How to Run This

  1. Identify which stacks import/export using list-imports and describe-stacks
  2. To remove an export: update all importing stacks first, then update the exporting stack
  3. Use unique, descriptive export names to avoid conflicts
  4. For complex dependencies, replace exports with SSM Parameter Store
  5. Audit circular dependencies and break cycles before they cause deployment issues

Is This Safe?

Yes. The key is updating importing stacks before removing exports. Using SSM Parameter Store is actually safer than cross-stack exports because it eliminates hard dependencies.

Key Takeaway

Cross-stack reference failures occur when exports are deleted while imports exist, export names collide, or circular dependencies form. Update importing stacks first before changing exports, use unique export names, and consider SSM Parameter Store for complex multi-stack setups to avoid hard dependencies.


Have questions or ran into a different CloudFormation issue? Connect with me on LinkedIn or X.