如何使用CDK获取资源的逻辑ID?

时间:2020-05-14 16:59:57

标签: amazon-web-services amazon-cloudformation aws-cdk

我正在尝试为CDK结构编写一些tests,以验证作为该结构的一部分定义的安全组规则。

该构造看起来类似于以下内容。

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));
  }
}

我想编写一个类似于以下内容的测试。

test("Security group config is correct", () => {
  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
    )
  );
});

这里的问题是针对合成的CloudFormation模板对测试进行了验证,因此,如果您要验证此构造创建的安全组是否具有允许从srcSecurityGroup访问的规则,则需要{{3 }},该安全组是在Construct中创建的。

您可以在此处生成的CloudFormation模板中看到它。

{
  "Type": "AWS::EC2::SecurityGroupIngress",
  "Properties": {
    "IpProtocol": "tcp",
    "FromPort": 22,
    "GroupId": {
      "Fn::GetAtt": [
        "TestConstructSecurityGroup95EF3F0F", <-- This
        "GroupId"
      ]
    },
    "SourceSecurityGroupId": "id-123",
    "ToPort": 22
  }
}

Fn::GetAtt是此问题的症结所在。由于这些测试实际上只是在进行对象比较,因此您需要能够复制Fn::Get调用,该调用需要CloudFormation逻辑ID。


请注意,CDK 确实为您提供了Logical ID

  • 唯一ID提供的内容非常接近,但与CloudFormation堆栈中使用的标识符不同。例如,securityGroup.uniqueId返回TestStackTestConstructSecurityGroup10D493A7,而CloudFormation模板显示TestConstructSecurityGroup95EF3F0F。您可以注意到不同之处在于uniqueId将Construct ID附加到逻辑标识符上,并且每个附加的哈希值也不同。
  • 构造ID只是您在实例化构造时提供的标识符。尽管它被用作逻辑ID的一部分,但它也不是逻辑ID。我也没有看到直接从构造中以编程方式检索此ID的方法。您当然可以在某个位置定义ID并重用它,但这仍然不能解决与逻辑ID不完全匹配的问题。在这种情况下,在合成模板中,SecurityGroup作为构造ID与TestConstructSecurityGroup95EF3F0F作为逻辑ID有所不同。

是否有一种直接的方法来获取CDK资源的逻辑ID?

3 个答案:

答案 0 :(得分:5)

在撰写了整篇文章并仔细研究了CDK代码之后,我偶然发现了所需的答案。如果有人有更好的方法从更高级别的CDK构造中获取逻辑ID,那么我们将不胜感激。

如果需要获取CDK资源的逻辑ID,则可以执行以下操作:

const stack = new Stack();
const construct = new SampleConstruct(stack, "SampleConstruct");
const logicalId = stack.getLogicalId(construct.securityGroup.node.defaultChild as CfnSecurityGroup);

请注意,您已经拥有CloudFormation资源(例如,以Cfn开头的资源),这样会容易一些。

// Pretend construct.securityGroup is of type CfnSecurityGroup
const logicalId = stack.getLogicalId(construct.securityGroup);

答案 1 :(得分:2)

除了jaredready的出色答案外,您还可以使用resource.node.default_child.overrideLogicalId("AnyStringHere")

显式设置逻辑ID。

这可能会更容易,因为您只需设置一次并使用硬编码的字符串,而不是为每个测试查找值。

答案 2 :(得分:0)

从我的测试来看,似乎 stack.getLogicalId 将始终返回原始的、CDK 分配的 logicalId,如果您调用 overrideLogicalId,它不会改变,因此它不会总是与合成输出匹配。

这对我有用,即使设置了 logicalId 覆盖:

stack.resolve((construct.node.defaultChild as cdk.CfnElement).logicalId)

stack.resolve 是必需的,因为 .logicalId 是一个令牌。