title: How to get logical ID of resource with CDK?
body: <p>I'm attempting to write some <a href="https://docs.aws.amazon.com/cdk/latest/guide/testing.html" rel="noreferrer">tests</a> for a CDK Construct that validates security group rules defined as part of the construct.</p>
  
  <p>The Construct looks something like the following.</p>
  
  <pre class="lang-js prettyprint-override"><code>export interface SampleConstructProps extends StackProps {
    srcSecurityGroupId: string;
  }
  
  export class SampleConstruct extends Construct {
    securityGroup: SecurityGroup;
  
    constructor(scope: Construct, id: string, props: SampleConstructProps) {
      super(scope, id, props);
  
      // const vpc = Vpc.fromLookup(...);
      this.securityGroup = new SecurityGroup(this, "SecurityGroup", {
        vpc: vpc,
        allowAllOutbound: true,
      });
  
      const srcSecurityGroupId = SecurityGroup.fromSecurityGroupId(stack, "SrcSecurityGroup", props.srcSecurityGroupId);
  
      this.securityGroup.addIngressRule(srcSecurityGroup, Port.tcp(22));
    }
  }
  </code></pre>
  
  <p>And I want to write a test that looks something like the following.</p>
  
  <pre class="lang-js prettyprint-override"><code>test("Security group config is correct", () =&gt; {
    const stack = new Stack();
    const srcSecurityGroupId = "id-123";
    const testConstruct = new SampleConstruct(stack, "TestConstruct", {
      srcSecurityGroupId: srcSecurityGroupId
    });
  
    expect(stack).to(
      haveResource(
        "AWS::EC2::SecurityGroupIngress",
        {
          IpProtocol: "tcp",
          FromPort: 22,
          ToPort: 22,
          SourceSecurityGroupId: srcSecurityGroupId,
          GroupId: {
            "Fn::GetAtt": [testConstruct.securityGroup.logicalId, "GroupId"], // Can't do this
          },
        },
        undefined,
        true
      )
    );
  });
  
  </code></pre>
  
  <p>The issue here is that the test is validated against the synthesized CloudFormation template, so if you want to verify that the security group created by this construct has a rule allowing access from <code>srcSecurityGroup</code>, you need the <a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html" rel="noreferrer">Logical ID</a> of the security group that was created as part of the Construct.</p>
  
  <p>You can see this in the generated CloudFormation template here.</p>
  
  <pre><code>{
    "Type": "AWS::EC2::SecurityGroupIngress",
    "Properties": {
      "IpProtocol": "tcp",
      "FromPort": 22,
      "GroupId": {
        "Fn::GetAtt": [
          "TestConstructSecurityGroup95EF3F0F", &lt;-- This
          "GroupId"
        ]
      },
      "SourceSecurityGroupId": "id-123",
      "ToPort": 22
    }
  }
  </code></pre>
  
  <p>That <code>Fn::GetAtt</code> is the crux of this issue. Since these tests really just do an object comparison, you need to be able to replicate the <code>Fn::Get</code> invocation, which requires the CloudFormation Logical ID.</p>
  
  <hr>
  
  <p>Note that the CDK <em>does</em> provide a <a href="https://docs.aws.amazon.com/cdk/latest/guide/identifiers.html" rel="noreferrer">handful of identifiers</a> for you.</p>
  
  <ul>
  <li>Unique ID provides something very close, but it's not same identifier used in the CloudFormation stack. For example, <code>securityGroup.uniqueId</code> returns <code>TestStackTestConstructSecurityGroup10D493A7</code> whereas the CloudFormation template displays <code>TestConstructSecurityGroup95EF3F0F</code>. You can note the differences are the <code>uniqueId</code> prepends the Construct ID to the logical identifier and the appended hash is different in each.</li>
  <li>Construct ID is just the identifier that you provide when instantiating a construct. It is not the logical ID either, though it is used as part of the logical ID. I also have not seen a way of programmatically retrieving this ID from the construct directly. You can of course define the ID somewhere and just reuse it, but this still doesn't solve the problem of it not fully matching the logical ID. In this case it's a difference of <code>SecurityGroup</code> as the construct ID and <code>TestConstructSecurityGroup95EF3F0F</code> as the logical ID in the synthesized template.</li>
  </ul>
  
  <p>Is there a straightforward way getting the logical ID of CDK resources?</p>