Route 53のサブドメインをTerraformで別ゾーンに切り出す設計|NSレコード委任と環境ごとの権限分離

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > Route 53のサブドメインをTerraformで別ゾーンに切り出す設計|NSレコード委任と環境ごとの権限分離
「Terraformでdev.example.comをexample.comとは別のゾーンに切り出したいが、NSレコードをどう親ゾーンに登録すればいいかわからない」
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サーバーに直接問い合わせて確認する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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_recordtype = "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.devaws_route53_record.dev_delegationの依存関係を認識して正しい順序で作成する。

親ゾーンと子ゾーンをAWSアカウントごとに分けて別々のstateで管理する場合は、子ゾーンのname_serverszone_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 }

親ゾーンのstateでは、このoutputを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 = "*" } ] }) }

Route 53のARNはarn:aws:route53:::hostedzone/ZONE_IDの形式で、リージョンとAWSアカウントIDを含まないことに注意する。aws_route53_zone.dev.zone_idでゾーンIDを参照できる。

route53:ListHostedZonesroute53: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.

ANSWER SECTIONに4本のNSレコードが返れば委任は成功している。@ns-1234.awsdns-28.orgは親ゾーン(example.com)のNSサーバーをコマンドラインで直接指定する書き方で、TTLキャッシュの影響を受けずに確認できる。

よくあるエラーと対処法

エラー: InvalidInput: Invalid XML
recordsに渡した値の末尾にドット(.)が重複して入っている場合に発生することがある。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"

Error: deleting Route53 Hosted Zone: HostedZoneNotEmpty
子ゾーンを削除しようとしたとき、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サーバー
NSレコード委任の実装で最も混乱しやすいのは、name_serversの取得元(子ゾーン)とNSレコードの登録先zone_id(親ゾーン)を混同するケースだ。子ゾーンのNSを、親ゾーンのIDを持つレコードとして登録するという方向を意識しながらHCLを書くと間違いが減る。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、TerraformでRoute 53のNSレコード委任と環境ごとのIAM権限分離を正しく設計するスキルを、セミナーで習得できます。
>> Terraform実践セミナーの詳細はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。