Skip to content

bug: CloudFormation deployment fails with circular dependency when accessing immediately available attributes #13334

@barbecuesteve

Description

@barbecuesteve

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

🐛 Bug Description

CloudFormation deployments fail with artificial circular dependency errors when templates reference attributes that are immediately available after resource creation, such as !GetAtt RestApi.RootResourceId for API Gateway resources.

🔄 Expected vs Actual Behavior

Expected: CloudFormation templates with !GetAtt references to immediately available attributes (like RootResourceId, Arn, etc.) should deploy successfully, matching AWS CloudFormation behavior.

Actual: Deployment fails with circular dependency errors, even though the referenced attributes are available immediately after resource creation.

📋 Reproduction Steps

Minimal Reproduction Case

Create a CloudFormation template with API Gateway resources:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "MyRestApi": {
      "Type": "AWS::ApiGateway::RestApi",
      "Properties": {
        "Name": "TestAPI",
        "Description": "Reproduction case for circular dependency bug"
      }
    },
    "MyResource": {
      "Type": "AWS::ApiGateway::Resource",
      "Properties": {
        "RestApiId": {"Ref": "MyRestApi"},
        "ParentId": {"Fn::GetAtt": ["MyRestApi", "RootResourceId"]},
        "PathPart": "items"
      }
    }
  }
}

Deploy with LocalStack:

awslocal cloudformation create-stack \
  --stack-name test-circular-dependency \
  --template-body file://test-template.json

Result: Deployment fails with dependency resolution errors.

Real-World Impact

This issue affects:

  • CDK-generated templates: CDK commonly generates templates with !GetAtt RestApi.RootResourceId patterns
  • S3 bucket references: Templates accessing Bucket.Arn immediately after creation
  • Lambda function ARNs: Templates referencing Function.Arn in dependent resources
  • Any resource with immediately available attributes

🔍 Root Cause Analysis

The issue occurs in get_attr_from_model_instance() function in template_deployer.py. The current implementation attempts dependency resolution for all !GetAtt references, even when:

  1. The target resource has already been created (has PhysicalResourceId)
  2. The requested attribute is already available in the resource's Properties
  3. The attribute was set by the resource provider during the create() method

Technical Details

When API Gateway RestApi is created:

# In aws_apigateway_restapi.py create() method:
result = api.create_rest_api(**params)           # AWS resource created
model["RestApiId"] = result["id"]                # PhysicalResourceId set
# ... later in same method ...
model["RootResourceId"] = res["id"]              # RootResourceId immediately available

However, the dependency resolver doesn't recognize that RootResourceId is immediately accessible and tries to resolve dependencies, creating artificial circular dependency loops.

💥 Impact

  • High: Prevents deployment of common CloudFormation patterns
  • Blocks CDK usage: Many CDK-generated templates fail to deploy
  • Affects multiple services: API Gateway, S3, Lambda, DynamoDB, IAM resources
  • Workaround complexity: Users must manually restructure templates to avoid the pattern

🌍 Environment

  • LocalStack version: 4.10.1.dev7 (affects current stable releases)
  • Platform: All platforms (Docker, local installation)
  • Services affected: CloudFormation engine (impacts all AWS services)

📚 Additional Context

AWS Behavior

In real AWS CloudFormation, these attributes are available immediately and don't cause circular dependencies because:

  • Resource providers set certain attributes synchronously during resource creation
  • The CloudFormation engine recognizes immediately available attributes
  • Dependency resolution is bypassed for attributes that don't require waiting

Similar Issues

This pattern appears in multiple scenarios:

  • API Gateway: RootResourceId, RestApiId
  • S3 Bucket: Arn, DomainName, WebsiteURL
  • Lambda Function: Arn
  • DynamoDB Table: Arn
  • IAM Role/User: Arn

🎯 Suggested Solution

The fix should:

  1. Detect when a resource has been created (has PhysicalResourceId)
  2. Check if the requested attribute exists in Properties (was set by resource provider)
  3. Return the attribute immediately without dependency resolution
  4. Be future-proof to handle new AWS services automatically

This approach leverages existing LocalStack patterns where resource providers set attributes in Properties during creation, requiring no hardcoded mappings or service-specific logic.

🔗 Related Resources

  • LocalStack file: localstack-core/localstack/services/cloudformation/engine/template_deployer.py
  • Function: get_attr_from_model_instance()
  • AWS CloudFormation documentation: Fn::GetAtt function
  • CDK patterns that generate affected templates

Labels: bug, cloudformation, high-priority, good-first-issue
Components: CloudFormation Engine, Template Deployer

Expected Behavior

No response

How are you starting LocalStack?

With a docker-compose file

Steps To Reproduce

How are you starting localstack (e.g., bin/localstack command, arguments, or docker-compose.yml)

docker run localstack/localstack

Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)

awslocal s3 mb s3://mybucket

Environment

- OS:
- LocalStack:
  LocalStack version:
  LocalStack Docker image sha:
  LocalStack build date:
  LocalStack build git hash:

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions