CDKのHostedZone.fromHostedZoneIdでzoneNameを参照するとエラーになる

  • CDK
  • Route 53
  • TypeScript

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

何が起きたか

HostedZone.fromHostedZoneId() でRoute53のホストゾーンをインポートして、zoneName を参照しようとしたら例外が飛んだ。

const hostedZone = route53.HostedZone.fromHostedZoneId(this, 'MyZone', 'Z123456ABCDEF');
hostedZone.zoneName; // Error: HostedZone.fromHostedZoneId doesn't support "zoneName"

IHostedZone インターフェース的には zoneName を持ってるように見えるので、TypeScriptの型チェックは普通に通る。でも実行時(cdk synthのタイミング)に落ちる。

原因

fromHostedZoneId の実装を見ると、zoneName のゲッターが最初からエラーをthrowするだけの実装になっている。CDKのソース上にも「Hosted zone name becomes unavailable through this query」というコメントがある。IDしか渡していないので、CDK側はそもそも zoneName の値を知りようがない、という設計。

対処

zoneNameも自分で知ってるなら fromHostedZoneAttributes

const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'MyZone', {
hostedZoneId: 'Z123456ABCDEF',
zoneName: 'example.com',
});

自分でid/nameを渡すだけなので、追加のAWS呼び出しは発生しない。

自動取得したいなら fromLookup

const hostedZone = route53.HostedZone.fromLookup(this, 'MyZone', {
domainName: 'example.com',
});

cdk synth 時にAWSへ問い合わせて、結果を cdk.context.json にキャッシュする。ローカルで単発デプロイするだけなら便利だけど、CDK Pipelinesみたいに複数アカウント・複数ステージを跨ぐ構成だと、キャッシュがデプロイ先アカウントとズレて事故る。pipeline運用では基本使わない方針にした。

使い分けの整理

flowchart TD
  A[ホストゾーンをインポートしたい] --> B{zoneNameが必要?}
  B -- 不要 --> C[fromHostedZoneId]
  B -- 必要 --> D{値を自分で持ってる?}
  D -- 持ってる --> E[fromHostedZoneAttributes]
  D -- 持ってない --> F[fromLookup]
  F --> G["pipeline運用では非推奨<br/>cdk.context.jsonがアカウントごとにズレる"]

CDKのfromXxx系メソッド全般に言えること

これはRoute53に限った話ではなく、CDK全体の設計パターンらしい。awslintのルールで fromXxx 系の静的メソッドは scope, id[, props] を受け取って IFoo を返す、という規約が決まっていて、大体どのリソースも同じ階層構造になっている。

  • fromXxxId / fromXxxArn / fromXxxName — 識別子1つだけの軽量インポート。他の属性は「わからない」扱い
  • fromXxxAttributes — 必要な属性を自分で全部渡す
  • fromLookup(一部のリソースのみ)— 実行時にAWSへ問い合わせて自動取得

「わからない属性」に触れたときの挙動はリソースによって違っていて、Route53みたいに例外を投げるものもあれば、単に型がoptionalになっていて undefined が返るだけのものもあるらしい。ここは実装依存なので、都度ドキュメントで確認するしかなさそう(実機未確認だけど、S3の fromBucketName やIAMの fromRoleArn も同じ思想で作られている)。