Route 53のホストゾーンをサブドメインで分割する実装は、HCLの書き方さえ押さえれば作業自体は単純だ。ただしname_serversの受け渡しとNSレコードの登録先zone_idを混同すると委任が機能せず、名前解決が切れたまま原因を特定しにくい状況になる。
この記事では、
aws_route53_zoneで子ゾーンを作成し、aws_route53_recordでNSレコードを親ゾーンに登録する実装を解説する。ゾーンをどの単位で分けるべきかという設計判断論には踏み込まない。HCLでのNSレコード委任の書き方と、ゾーンIDで絞ったIAM権限の設定という実装に絞って説明する。この記事のポイント
・ 子ゾーンのname_servers属性をそのまま親ゾーンのNSレコードのrecordsに渡せる
・ NSレコードはtype="NS"+records=aws_route53_zone.子.name_serversで書く
・ IAMポリシーはARNにゾーンIDを含めてゾーン単位で権限を絞れる
・ 委任後はdigコマンドで親ゾーンのNSサーバーに直接問い合わせて確認する
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
NSレコード委任でRoute 53ゾーンを分離する仕組み
Route 53のホストゾーンはドメインだけでなく任意のサブドメインに対しても独立して作成できる。example.comのゾーンとdev.example.comのゾーンをそれぞれ別に作れば、異なるAWSアカウントや別のTerraformモジュールで管理できるようになる。DNSリゾルバがdev.example.comを問い合わせたとき、example.comのゾーンに
dev.example.com NS ns-xxx.awsdns-xx.com.というNSレコードがあれば、リゾルバはそのNSサーバーへ次の問い合わせを向ける。この「委任」をTerraformで宣言するのが今回の実装だ。DNS ゾーン設定の参考HCLでNSレコード委任を実装する手順
1. 親ゾーンをデータソースで参照する
すでにRoute 53に存在するexample.comのゾーンはdata "aws_route53_zone"で参照する。Terraformで管理していないゾーンにも使える。# 親ゾーン(例: example.com)をデータソースで参照 data "aws_route53_zone" "root" { name = "example.com" private_zone = false }
private_zone = falseはパブリックゾーンを明示する指定だ。同名のゾーンがプライベートとパブリックで重複している場合にTerraformの検索が曖昧になるため、明示的に書いておくとよい。2. 子ゾーンを作成する
aws_route53_zoneリソースで子ゾーンを作成する。terraform apply後にRoute 53が自動で4本のNSサーバーをゾーンに割り当てる。# 子ゾーン(例: dev.example.com)を作成 resource "aws_route53_zone" "dev" { name = "dev.example.com" tags = { Environment = "dev" ManagedBy = "terraform" } }
aws_route53_zone.dev.name_serversという属性に4本のNSサーバー名がlist(string)型で格納される。次のステップでこの値を親ゾーンへ渡す。3. 親ゾーンにNSレコードを登録する
aws_route53_recordでtype = "NS"を指定し、recordsに子ゾーンのname_servers属性をそのまま渡す。# 親ゾーンにNSレコード委任を登録 resource "aws_route53_record" "dev_delegation" { zone_id = data.aws_route53_zone.root.zone_id name = "dev.example.com" type = "NS" ttl = 300 records = aws_route53_zone.dev.name_servers }
recordsはリストを受け取るため、name_serversをそのまま代入できる。tolist()やtoset()に変換する必要はない。TTLは300秒(5分)が初回構築時の標準的な設定だ。委任が確認できて運用が安定したら3600秒(1時間)程度に伸ばしてDNSサーバーへの問い合わせ回数を減らせる。
stateを分けて管理する場合のname_servers受け渡し
両ゾーンを同一のTerraformstate内で管理している場合は、Terraformが自動的にaws_route53_zone.dev→aws_route53_record.dev_delegationの依存関係を認識して正しい順序で作成する。親ゾーンと子ゾーンをAWSアカウントごとに分けて別々のstateで管理する場合は、子ゾーンの
name_serversとzone_idをoutputとして出力しておく設計にする。# 子ゾーンstateでname_serversをoutputとして公開 output "dev_zone_name_servers" { value = aws_route53_zone.dev.name_servers } output "dev_zone_id" { value = aws_route53_zone.dev.zone_id }
data "terraform_remote_state"またはSSMパラメータ経由で受け取り、recordsに渡す。terraform_remote_stateを使う場合はS3バックエンドとIAMロールのread権限が必要になる。ゾーンIDで絞ったIAMポリシーを定義する
ゾーンを環境ごとに分離する目的のひとつはIAM権限の分離だ。Route 53のリソースARNはホストゾーンIDを含むため、ゾーン単位でアクセスを制限できる。# devゾーンのみ変更できるIAMポリシー resource "aws_iam_policy" "route53_dev_zone" { name = "route53-dev-zone-policy" description = "dev.example.com ゾーンへの変更権限" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "AllowDevZoneChange" Effect = "Allow" Action = [ "route53:ChangeResourceRecordSets", "route53:ListResourceRecordSets", "route53:GetHostedZone" ] Resource = "arn:aws:route53:::hostedzone/${aws_route53_zone.dev.zone_id}" }, { Sid = "AllowListZones" Effect = "Allow" Action = [ "route53:ListHostedZones", "route53:GetChange" ] Resource = "*" } ] }) }
arn:aws:route53:::hostedzone/ZONE_IDの形式で、リージョンとAWSアカウントIDを含まないことに注意する。aws_route53_zone.dev.zone_idでゾーンIDを参照できる。route53:ListHostedZonesとroute53:GetChangeはRoute 53の仕様上リソース指定ができないためResource = "*"で書く。ChangeResourceRecordSetsと同一のStatementに書くと意図した絞り込みが機能しなくなるため、必ず別ステートメントに分ける。digコマンドで委任を確認する
terraform apply後、NSレコードが正しく委任されているかをdigで確認する。dig コマンドで DNS を調べる# 親ゾーンのNSサーバーに直接問い合わせてNSレコードを確認 $ dig NS dev.example.com @ns-1234.awsdns-28.org ; <<>> DiG 9.16.23-RH <<>> NS dev.example.com @ns-1234.awsdns-28.org ;; ANSWER SECTION: dev.example.com. 300 IN NS ns-100.awsdns-12.com. dev.example.com. 300 IN NS ns-200.awsdns-25.net. dev.example.com. 300 IN NS ns-300.awsdns-37.co.uk. dev.example.com. 300 IN NS ns-400.awsdns-00.org.
@ns-1234.awsdns-28.orgは親ゾーン(example.com)のNSサーバーをコマンドラインで直接指定する書き方で、TTLキャッシュの影響を受けずに確認できる。よくあるエラーと対処法
エラー: InvalidInput: Invalid XMLrecordsに渡した値の末尾にドット(.)が重複して入っている場合に発生することがある。aws_route53_zone.dev.name_serversは末尾ドットなしで返るため通常は問題ないが、recordsを文字列リテラルで手書きした場合に混入しやすい。NSレコードがANSWER SECTIONに返らない
NSレコード登録先の
zone_idが親ゾーンではなく子ゾーンになっているケースが多い。terraform state showで登録先を確認する。# stateでzone_idを確認 $ terraform state show 'aws_route53_record.dev_delegation' # aws_route53_record.dev_delegation: # resource "aws_route53_record" "dev_delegation" { # zone_id = "Z1234567890ABC" ← ここが親ゾーンのIDであることを確認 # name = "dev.example.com" # type = "NS"
子ゾーンを削除しようとしたとき、NSレコードやSOAレコード以外のレコードが残っている場合に発生する。Terraformが管理していないレコードが子ゾーンに手動追加されていないか確認する。
本記事のまとめ
TerraformでRoute 53のNSレコード委任を実装するポイントをまとめる。| やりたいこと | HCLの書き方 |
|---|---|
| 子ゾーンを作成する | resource "aws_route53_zone" "dev" { name = "dev.example.com" } |
| 子ゾーンのNSサーバーを取得する | aws_route53_zone.dev.name_servers |
| 親ゾーンにNSレコードを登録する | type = "NS" + records = aws_route53_zone.dev.name_servers |
| IAMをゾーン単位で絞る | Resource = "arn:aws:route53:::hostedzone/${zone_id}" |
| 委任をdigで確認する | dig NS dev.example.com @親ゾーンNSサーバー |
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:TerraformのstateロックをDynamoDBからS3ネイティブロックへ移行する判断|use_lockfileの併用期間とロック競合の解消
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます