Roadworkerを使ってRoute 53上のホストゾーンを別アカウントに移行してみる

2023.03.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

初めに

AWS CLIを用いてアカウントにホストゾーンを移行をする場合、
公式のドキュメントに記載のAWS CLIを利用した方法は手間がかかります。

特にステップ4のレコードの編集は量が多いと手動はだいぶ厳しい印象です。

別の AWS アカウントへのホストゾーンの移行

RoadworkerというRoute 53の設定をコードで管理するためのツールを触る機会があり、
これを使うと現状の設定次第では簡単に移行できそうでしたので試してみます。

インストール

RoadworkerはRubyで作成されているツールでgemコマンド でインストールを行います。

% gem install roadwork
...
% roadwork --version
roadwork 0.5.15

テスト用データの準備

基本的な確認用のexample1.com、ゾーン分割確認用のsub.example1.com、プライベートホストゾーン確認用のexample.localを用意しました。

example1.com確認用に幾つかの種類のレコードを設定しています。
sub.example1.comは特にレコードを割り当てず、example.localはゾーンAPEXのAレコードに192.168.1.1の値を当てています。

Notice
Roadworker cannot update TTL of two or more same weighted A records (with different SetIdentifier) after creation.

なおREADMEに記載がありますが重み付きレコードのTTLの更新には制約があります。

エクスポート

移行用データの準備ができましたのでそのデータをエクスポートしてみます。

$ roadwork --profile accountA --export --output route53.dsl
Export Route53 to `route53.dsl`

AWS CLI等で設定したプロファイル名を--profileオプションで指定すればそれを使用して実行できます。

route53.dslの中身は以下のようになりました。

## -*- mode: ruby -*-
# vi: set ft=ruby :
hosted_zone "example1.com." do
  rrset "example1.com.", "A" do
    ttl 300
    resource_records(
      "192.168.1.1"
    )
  end

  rrset "example1.com.", "MX" do
    ttl 300
    resource_records(
      "10 192.168.0.1",
      "20 192.168.0.2"
    )
  end

  rrset "ip-base.example1.com.", "A" do
    set_identifier "from-class-c1-route"
    ttl 300
    resource_records(
      "192.168.1.1"
    )
  end

  rrset "ip-base.example1.com.", "A" do
    set_identifier "from-class-c2-route"
    ttl 300
    resource_records(
      "192.168.1.2"
    )
  end

  rrset "latency.example1.com.", "A" do
    set_identifier "test1"
    ttl 300
    region "ap-northeast-1"
    resource_records(
      "192.16.1.2"
    )
  end

  rrset "latency.example1.com.", "A" do
    set_identifier "test2"
    ttl 300
    region "ap-northeast-3"
    resource_records(
      "192.16.1.3"
    )
  end

  rrset "sub.example1.com.", "NS" do
    ttl 300
    resource_records(
      "ns-xxxxx.awsdns-22.co.uk.",
      "ns-xxxx.awsdns-11.net.",
      "ns-xxxx.awsdns-28.com.",
      "ns-xxxx.awsdns-52.org."
    )
  end
end

hosted_zone "sub.example1.com." do

end

hosted_zone "example.local.", "Z04xxxxx" do
  vpc "ap-northeast-1", "vpc-xxxxx"

  rrset "example.local.", "A" do
    ttl 300
    resource_records(
      "192.168.1.1"
    )
  end
end

接続元IPベースのルーティングやレイテンシルーティングも問題なくエクスポートされました。

ゾーン生成時に生成されるゾーンAPEXのSOAレコードやNSレコードは含まれませんが、
手動で作成したNSレコードについてはそのまま出力されるようです。

ちなみにエクスポートは全てのゾーンとなるようです。

Rubyはわからないので雰囲気でコードを感じる限りとなりますが--targetのオプションは
--apply実行時のオプションのようです。

特定のホストゾーンのみを移行したい場合は別途必要な部分だけ切り出したりする必要があります。

別アカウントへ取り込み

事前チェック

ドライランの機能があるので取り込み前に確認します。
出力先は先ほどと異なるアカウントを指定したいので別のプロファイルを指定します。

% roadwork --profile accountB --file route53.dsl --dry-run --apply
Apply `route53.dsl` to Route53 (dry-run)
Create Hostedzone nil: example1.com. (dry-run)
=== Change batch: example1.com. |  (dry-run)
Create ResourceRecordSet: example1.com. A (dry-run)
Create ResourceRecordSet: example1.com. MX (dry-run)
Create ResourceRecordSet: ip-base.example1.com. A (from-class-c1-route) (dry-run)
Create ResourceRecordSet: ip-base.example1.com. A (from-class-c2-route) (dry-run)
Create ResourceRecordSet: latency.example1.com. A (test1) (dry-run)
Create ResourceRecordSet: latency.example1.com. A (test2) (dry-run)
Create ResourceRecordSet: sub.example1.com. NS (dry-run)
---

Create Hostedzone nil: sub.example1.com. (dry-run)
Hosted zone not found: example.local. (Z04xxxxx) (dry-run)

メッセージ的にゾーンもまとめて作ってくれそうです。

適用

--dry-runオプションを外し実際に適用します。

% roadwork --profile accountB --file route53.dsl --apply
Create Hostedzone nil: example1.com.
=== Change batch: example1.com. | /hostedzone/Z09xxxx
Create ResourceRecordSet: example1.com. A
Create ResourceRecordSet: example1.com. MX
Create ResourceRecordSet: ip-base.example1.com. A (from-class-c1-route)
Create ResourceRecordSet: ip-base.example1.com. A (from-class-c2-route)
Create ResourceRecordSet: latency.example1.com. A (test1)
Create ResourceRecordSet: latency.example1.com. A (test2)
Create ResourceRecordSet: sub.example1.com. NS
[ERROR] Aws::Route53::Errors::InvalidInput: Invalid request: Expected exactly one of [Weight, Region, Failover, GeoLocation, MultiValueAnswer, GeoProximityLocation, or AvailabilityZone], but found none in Change with [Action=CREATE, Name=ip-base.example1.com., Type=A, SetIdentifier=from-class-c1-route]

ドライランで通ったので一瞬行けるかと期待してましたがダメでした。

Roadworkerの最終リリース日が2021年時点なので
流石に2022年にリリースされた種別のレコードは対応していないようです。
(今回の場合は接続元IPベースのルーティング)

接続元IPベースでのルーティングの設定をファイルから削除し再度実行します。

% roadwork --profile accountB --file route53.dsl --apply
Apply `route53.dsl` to Route53
=== Change batch: example1.com. | /hostedzone/Z09xxx
Create ResourceRecordSet: example1.com. A
Create ResourceRecordSet: example1.com. MX
Create ResourceRecordSet: latency.example1.com. A (test1)
Create ResourceRecordSet: latency.example1.com. A (test2)
Create ResourceRecordSet: sub.example1.com. NS
--> Change submitted: /change/C01xxx

Create Hostedzone nil: sub.example1.com.
Hosted zone not found: example.local. (Z04xxx)

実行に成功したのでマネコンに確認しに行きます。

ホストゾーンIDが明示的に指定されているexample.localは取り込まれないようです。

今回リソースがなく試していませんがエイリアスレコードも対応しています。

ゾーンAPEXのNSレコードとSOAレコードはDSLファイルの方に記載がなくとも
ゾーンが作成された時の値が使用されます。

ただし明示的に指定のあるexample1.comホストゾーン内のsub.example1.comのNSレコードについてはDSLファイルに記載された値が設定されます。

今回sub.example1.comのホストゾーンも移行しネームサーバが変更となるためこの部分は別途対応が必要になります。

なお適用されなかったプライベートホストゾーンについてはゾーンIDの値を削除し、
割り当てVPCのIDを使用できるVPCに変更すれば取り込み可能です。

#変更前
hosted_zone "example.local.", "Z0xxxx" do
  vpc "ap-northeast-1", "vpc-xxxxx"
#変更後
hosted_zone "example.local." do
  vpc "ap-northeast-1", "{{移行先で適用したいVPC}}"

変更し再度実行すると成功しプライベートホストゾーンとしてexample.localのホストゾーンが作成されます。

% roadwork --profile accountB --file route53.dsl --apply
Apply `route53.dsl` to Route53
Create Hostedzone #<struct Aws::Route53::Types::VPC vpc_region="ap-northeast-1", vpc_id="vpc-xxxx">: example.local.
=== Change batch: example.local. | /hostedzone/Z0xxxxx - private
Create ResourceRecordSet: example.local. A
--> Change submitted: /change/C01xxxx

まとめ

実際に移行に使ってみた印象としては以下の通りです

  • ゾーン分割が込み入ってなく新機能をゴリゴリに使ってなければ大抵の場合は対応できそう
    • 現時点で2021/06頃の更新が最終
    • そもそもNSレコードとゾーンIDはそもそも結びついて無いので他の手段でもちょっと組み込まないと厳しい印象
  • レコードの設定のでみなくゾーンの作成まで一括でやってくれるので数が多いと特に楽
  • 手動で触るにも(個人的に)ネストされたJSONよりDSLフォーマットの方が触りやすい

そもそもこのツール自体Route 53の設定をコードで管理するためのものであり、
本来の用途では無いであろう中これだけのことができますので非常に有り難く思います。