Skip to content

(core): exportValue doesn't respect overridelogicalId #14335

@liamfd

Description

@liamfd

If we create a stack like:

import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';
import * as iam from '@aws-cdk/aws-iam';

/**
 * Stack that defines the bucket
 */
class Producer extends cdk.Stack {
  public readonly myBucket: s3.Bucket;

  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const bucket = new s3.Bucket(this, 'MyBucket', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
    this.myBucket = bucket;

    this.exportValue(bucket.bucketArn);

    const cfnResource = bucket.node.defaultChild as s3.CfnBucket;
    cfnResource.overrideLogicalId('OVERRIDE_LOGICAL_ID');
  }
}

interface ConsumerProps extends cdk.StackProps {
  userBucket: s3.IBucket;
}

/**
 * Stack that consumes the bucket
 */
class Consumer extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props: ConsumerProps) {
    super(scope, id, props);

    const user = new iam.User(this, 'MyUser');
    props.userBucket.grantReadWrite(user);
  }
}

const app = new cdk.App();
const producer = new Producer(app, 'ProducerStack');
new Consumer(app, 'ConsumerStack', { userBucket: producer.myBucket });

If we synth that, we get this in Producer:

    "ExportsOutputFnGetAttMyBucketF68F3FF0Arn0F7E8E58": {
      "Value": {
        "Fn::GetAtt": [
          "OVERRIDE_LOGICAL_ID",
          "Arn"
        ]
      },
      "Export": {
        "Name": "ProducerStack:ExportsOutputFnGetAttMyBucketF68F3FF0Arn0F7E8E58"
      }
    },
    "ExportsOutputFnGetAttOVERRIDELOGICALIDArn49941B8D": {
      "Value": {
        "Fn::GetAtt": [
          "OVERRIDE_LOGICAL_ID",
          "Arn"
        ]
      },
      "Export": {
        "Name": "ProducerStack:ExportsOutputFnGetAttOVERRIDELOGICALIDArn49941B8D"
      }
    }

As you can see, both of these have the same value, but different names. Consumer uses the output with the overridden ID:

                {
                  "Fn::ImportValue": "ProducerStack:ExportsOutputFnGetAttOVERRIDELOGICALIDArn49941B8D"
                },

Some other cases:

  • If we comment out overrideLogicalId we get only ExportsOutputFnGetAttMyBucketF68F3FF0Arn0F7E8E58 in both stacks
  • If we keep the override but remove the reference in Consumer we get only theExportsOutputFnGetAttMyBucketF68F3FF0Arn0F7E8E58 Output in Producer
  • If we move the exportValue after the overrideLogicalId we get only ExportsOutputFnGetAttOVERRIDELOGICALIDArn49941B8D in both stacks

So, basically, it seems like exportValue creates the name for the initial logicalId and that doesn't change when it gets overridden. References in other stacks will create Outputs based on the new logicalId, ignoring the ones created by exportValue.

I believe this makes it a bit harder to use exportValue to avoid the deadly embrace, because the Outputs it generates in this case aren't the ones imported in a dependent's stack.

Reproduction Steps

I just created a file with the above and passed it to a cdk synth --app 'yarn babel-node my-file.ts'.

What did you expect to happen?

I'd expect just a single Output.

What actually happened?

Two Outputs get created, Consumer does not reference the Output created with createExports, it references a lazily created Output that includes the overridden logicalId in its name.

Environment

  • CDK CLI Version : 1.91.0 and 1.100.0
  • Framework Version: 1.91.0 and 1.100.0
  • Node.js Version: v10.19.0
  • OS : MacOS 10.15.7
  • Language (Version): TypeScript (4.0.2)

Other

It's worth noting that this is pretty easy to work around - you just have to call exportValue after overrideLogicalId.


This is 🐛 Bug Report

Metadata

Metadata

Assignees

Labels

@aws-cdk/coreRelated to core CDK functionalitybugThis issue is a bug.effort/mediumMedium work item – several days of effortp1

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions