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
- Identify which stacks import/export using
list-importsanddescribe-stacks - To remove an export: update all importing stacks first, then update the exporting stack
- Use unique, descriptive export names to avoid conflicts
- For complex dependencies, replace exports with SSM Parameter Store
- 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.