CDK で VPC Endpoint など作成した時に、プロビジョニングが完了したら作成した VPC Endpoint の DNS 名を CfnOutput で出力したい、というのはよくあると思うのですが、雑に書いていたら出力できなかったので正しい出力方法を備忘録的にメモっておきます。

CDK での VPC Endpoint の作成

はじめに CDK で VPC Endpoint を作成する時のコードを簡単に紹介します。今回は IoT Core の VPC Endpoint を作成したかったので以下のようにしています。

    const vpce = new ec2.InterfaceVpcEndpoint(this, 'iot-core-vpe', {
        vpc: vpc,
        service: ec2.InterfaceVpcEndpointAwsService.IOT_CORE,
        lookupSupportedAzs: false,
        privateDnsEnabled: false,
        subnets: {
            subnets: [
                privateSubnetA, privateSubnetB, privateSubnetC, // それぞれ ec2.ISubnet
            ]
        },
        securityGroups: [
            securityGroup
        ],
    });

DNS 名の出力に使えそうなプロパティ

作成した VPC Endpoint (上記の変数 vpce) には、vpcEndpointDnsEntries というプロパティがあります。 CDK のドキュメント でも以下のように記載されています。

vpcEndpointDnsEntries
Type: string[]

The DNS entries for the interface VPC endpoint.
Each entry is a combination of the hosted zone ID and the DNS name. The entries are ordered as follows: regional public DNS, zonal public DNS, private DNS, and wildcard DNS. This order is not enforced for AWS Marketplace services.

The following is an example. In the first entry, the hosted zone ID is Z1HUB23UULQXV and the DNS name is vpce-01abc23456de78f9g-12abccd3.ec2.us-east-1.vpce.amazonaws.com.

[“Z1HUB23UULQXV:vpce-01abc23456de78f9g-12abccd3.ec2.us-east-1.vpce.amazonaws.com”, “Z1HUB23UULQXV:vpce-01abc23456de78f9g-12abccd3-us-east-1a.ec2.us-east-1.vpce.amazonaws.com”, “Z1C12344VYDITB0:ec2.us-east-1.amazonaws.com”]

(以下略)

文字列の配列のようなので、雑ですがとりあえず以下のようにすれば出力できそうです。

    new cdk.CfnOutput(this, 'output-vpce-dns', {
        description: 'VPC endpoint dns entries',
        value: vpce.vpcEndpointDnsEntries.join(','),
    });

ところがデプロイしてみると下記のエラーが出力されます。

Error: Found an encoded list token string in a scalar string context. Use 'Fn.select(0, list)' (not 'list[0]') to extract elements from token lists.

TypeScript の配列として扱うのではなく、Fn.select() を使えと出てきます。なるほど。

実際、console.log() とかで出力してみると以下のように Token(?) が出力されてました。

// console.log(vpce.vpcEndpointDnsEntries[0]); で出力
#{Token[TOKEN.660]}

正しい出力方法

Fn.select() で配列の中のエントリを取得しつつ、さらにドキュメントによるとエントリは <ホストゾーンID>:<VPC Endpoint DNS 名> という形になっているようなので、エントリをさらに : で区切ってあげれば良さそうです。

    const entry = cdk.Fn.select(0, vpce.vpcEndpointDnsEntries);
    const hostedIdAndDnsName = cdk.Fn.split(':', entry);
    const dnsName = cdk.Fn.select(1, hostedIdAndDnsName);

    new cdk.CfnOutput(this, 'output-vpce-dns', {
        description: 'VPC endpoint dns name',
        value: dnsName,
    });

一気に書いてしまうのであれば、以下になります。

    new cdk.CfnOutput(this, 'output-vpce-dns', {
        description: 'VPC endpoint dns name',
        value: cdk.Fn.select(1, cdk.Fn.split(':', cdk.Fn.select(0, vpce.vpcEndpointDnsEntries))),
    });

上記だと、AZ に依存しない DNS 名だけの出力になります。AZ 毎の名前も出力する場合は、以下のようにしてあげれば出力できるでしょう。(azNum は VPC Endpoint 作成時に指定した AZ の数です。)

    for (let i = 0; i < azNum + 1; i++) {
        new cdk.CfnOutput(this, `output-vpce-dns${i}`, {
            description: 'VPC endpoint dns name',
            value: cdk.Fn.select(1, cdk.Fn.split(':', cdk.Fn.select(i, vpce.vpcEndpointDnsEntries))),
        });
    }

// 出力イメージ
IoTCore-VpcEndpoint.outputvpcedns0 = vpce-xxxxx-xxxxx.data.iot.us-east-1.vpce.amazonaws.com
IoTCore-VpcEndpoint.outputvpcedns1 = vpce-xxxxx-xxxxx-us-east-1a.data.iot.us-east-1.vpce.amazonaws.com
IoTCore-VpcEndpoint.outputvpcedns2 = vpce-xxxxx-xxxxx-us-east-1c.data.iot.us-east-1.vpce.amazonaws.com
IoTCore-VpcEndpoint.outputvpcedns3 = vpce-xxxxx-xxxxx-us-east-1b.data.iot.us-east-1.vpce.amazonaws.com

あまり見た目気にしないのであればホストゾーンの ID が残ってしまいますが、シンプルに Fn.join() を使って改行(\n)で繋げてしまうだけでもとりあえず表示はさせられます。

    new cdk.CfnOutput(this, `output-vpce-dns-all`, {
        description: 'VPC endpoint dns name',
        value: cdk.Fn.join('\n', vpce.vpcEndpointDnsEntries),
    });

// 出力イメージ
IoTCore-VpcEndpoint.outputvpcednsall = HOSTEDZONEID:vpce-xxxxx-xxxxx.data.iot.us-east-1.vpce.amazonaws.com
HOSTEDZONEID:vpce-xxxxx-xxxxx-us-east-1a.data.iot.us-east-1.vpce.amazonaws.com
HOSTEDZONEID:vpce-xxxxx-xxxxx-us-east-1c.data.iot.us-east-1.vpce.amazonaws.com
HOSTEDZONEID:vpce-xxxxx-xxxxx-us-east-1b.data.iot.us-east-1.vpce.amazonaws.com

最後に・・・

この投稿は個人的なものであり、所属組織を代表するものではありません。ご了承ください。