- AWS
Route 53レコードをAWS CLIで作成|A・CNAME・エイリアス例
Route 53 のレコードは、aws route53 change-resource-record-sets に「変更内容を書いた JSON(change-batch)」を渡して作成・更新・削除します。 レコードの種類ごとにコマンドが分かれているわけではなく、JSON の Action(CREATE / UPSERT / DELETE)と ResourceRecordSet の中身を書き分けるのがポイントです。
この記事では、ホストゾーンの作成から、A・CNAME・TXT・MX・エイリアスの各レコードをコピーして使える JSON と実行出力つきで並べます。CREATE と UPSERT の違い、DELETE を一度で通す書き方、InvalidChangeBatch の読み方、--query での絞り込み、エイリアス先ごとの HostedZoneId の調べ方までを 1 ページにまとめました。
確認環境:Ubuntu 24.04、AWS CLI 1.46.1、Python 3.11。AWS の API をローカルで再現するツール(moto 5.2.3)で実行しました。掲載するコマンドは、ローカル向けの接続先指定を外して AWS でそのまま使える形にしています。ホストゾーン ID や変更 ID は作成のたびに変わります。
Route 53 レコード操作の早見表
やりたいことと、使うコマンド・Action の対応は次のとおりです。
| やりたいこと | コマンド | ポイント |
|---|---|---|
| ホストゾーンを作る | aws route53 create-hosted-zone --name example.com --caller-reference 一意な文字列 |
--caller-reference は毎回違う値にする |
| ホストゾーン ID を調べる | aws route53 list-hosted-zones-by-name --dns-name example.com |
返る Id の /hostedzone/ より後ろを使う |
| レコードを新規作成する | change-resource-record-sets + "Action": "CREATE" |
同じ名前・タイプが既にあるとエラーで止まる |
| あれば更新、無ければ作成する | change-resource-record-sets + "Action": "UPSERT" |
繰り返し実行しても同じ結果になる |
| レコードを削除する | change-resource-record-sets + "Action": "DELETE" |
名前・タイプ・TTL・値を登録内容と一致させる |
| レコードを一覧する | aws route53 list-resource-record-sets --hosted-zone-id ID |
--query で名前やタイプを絞り込める |
| 反映を待つ | aws route53 wait resource-record-sets-changed --id 変更ID |
状態が INSYNC になるまで待つ |
| ホストゾーンを削除する | aws route53 delete-hosted-zone --id ID |
NS と SOA 以外のレコードを先に消す |
レコード種別ごとの ResourceRecordSet の書き方は次の表が目安です。
| 種別 | Type |
ResourceRecords の Value の例 |
メモ |
|---|---|---|---|
| A | A |
192.0.2.10 |
IPv4 アドレス。複数の値を並べられる |
| CNAME | CNAME |
www.example.com |
値は 1 つだけ。ゾーンの頂点(Zone Apex)には置けない |
| TXT | TXT |
"\"v=spf1 include:_spf.example.net ~all\"" |
値全体を二重引用符で囲み、JSON 上は \" と書く |
| MX | MX |
10 mail1.example.com |
「優先度 半角スペース ホスト名」 |
| エイリアス | A など |
ResourceRecords と TTL の代わりに AliasTarget を書く |
AWS リソース向け。Zone Apex にも置ける |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Route 53レコードをAWS CLIで作成|A・CNAME・エイリアス例」
ホストゾーンを作成して ID を変数に入れる
レコードを入れる箱が「ホストゾーン」です。 まずドメイン名を指定してパブリックホストゾーンを作ります。--caller-reference は同じリクエストの二重実行を見分けるための文字列で、日時など毎回違う値を渡します。
aws route53 create-hosted-zone \
--name example.com \
--caller-reference "zone-20261005-01"
{
"Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/4E37D9AD5AQP2YKRJ6ZEA0",
"HostedZone": {
"Id": "/hostedzone/4E37D9AD5AQP2YKRJ6ZEA0",
"Name": "example.com.",
"CallerReference": "zone-20261005-01",
"Config": {
"PrivateZone": false
},
"ResourceRecordSetCount": 2
},
…(省略)…
"DelegationSet": {
…(省略)…
"NameServers": [
"ns-2048.awsdns-64.com",
"ns-2049.awsdns-65.net",
"ns-2050.awsdns-66.org",
"ns-2051.awsdns-67.co.uk"
]
}
}
ResourceRecordSetCount が 2 なのは、NS レコードと SOA レコードが自動で作られるためです。NameServers の 4 つは、ドメインを取得したレジストラ側に登録するネームサーバーです。
以降のコマンドで何度も使うので、ホストゾーン ID をシェル変数に入れておきます。Id は /hostedzone/ つきで返るので、sed で取り除いています。
ZONE_ID=$(aws route53 list-hosted-zones-by-name --dns-name example.com \
--query 'HostedZones[0].Id' --output text | sed 's|/hostedzone/||')
echo $ZONE_ID
ネームサーバーだけを後から確認したいときは get-hosted-zone を使います。
aws route53 get-hosted-zone --id $ZONE_ID --query 'DelegationSet.NameServers'
[
"ns-2048.awsdns-64.com",
"ns-2049.awsdns-65.net",
"ns-2050.awsdns-66.org",
"ns-2051.awsdns-67.co.uk"
]
A レコードを作成する(CREATE)
change-batch は「Changes の配列に、Action と ResourceRecordSet の組を並べる」形です。 www.example.com を 192.0.2.10 に向ける A レコードの JSON を a-create.json として保存します。
{
"Comment": "www の A レコードを作成",
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [{ "Value": "192.0.2.10" }]
}
}
]
}
file:// をつけてファイルを渡します。変更 ID だけを変数に受け取り、反映を待ってから状態を確認する流れにすると、スクリプトに組み込みやすくなります。
CHANGE_ID=$(aws route53 change-resource-record-sets \
--hosted-zone-id $ZONE_ID \
--change-batch file://a-create.json \
--query 'ChangeInfo.Id' --output text)
echo "CHANGE_ID=$CHANGE_ID"
aws route53 wait resource-record-sets-changed --id $CHANGE_ID
aws route53 get-change --id $CHANGE_ID --query 'ChangeInfo.Status' --output text
CHANGE_ID=/change/C2682N5HXP0BZ4
INSYNC
AWS の API リファレンスによると、変更は受け付け直後は PENDING、すべてのネームサーバーに行き渡ると INSYNC になり、通常は 60 秒以内に反映されます。wait resource-record-sets-changed は INSYNC になるまで待つコマンドです。
登録できたかは、名前で絞り込んで確認します。Route 53 が返す Name は末尾にドットがつくので、--query の比較文字列も www.example.com. と書きます。
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?Name=='www.example.com.']"
CREATE・UPSERT・DELETE の違い
3 つの Action の違いは「既にあるレコードに対してどう動くか」です。
| Action | レコードが無いとき | 同じ名前・タイプが既にあるとき | 向いている場面 |
|---|---|---|---|
| CREATE | 作成する | InvalidChangeBatch で止まる |
既存を上書きしたくない新規登録 |
| UPSERT | 作成する | 渡した内容で置き換える | 値の変更、何度流しても同じ結果にしたい手順 |
| DELETE | エラーで止まる | 内容が一致すれば削除する | 不要になったレコードの整理 |
既にあるレコードに CREATE するとこうなる
先ほどの a-create.json をもう一度流すと、次のエラーで止まります。
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID \
--change-batch file://a-create.json
An error occurred (InvalidChangeBatch) when calling the ChangeResourceRecordSets operation: Tried to create resource record set [name='www.example.com.', type='A'] but it already exists
「but it already exists」は、同じ名前・タイプのレコードが登録済みという意味です。 値を変えたいなら UPSERT を使います。
UPSERT で値を置き換える
Action を UPSERT に、値を 192.0.2.20 に変えた a-upsert.json を用意します。
{
"Comment": "www の A レコードを更新",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [{ "Value": "192.0.2.20" }]
}
}
]
}
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID \
--change-batch file://a-upsert.json
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?Name=='www.example.com.']"
[
{
"Name": "www.example.com.",
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{
"Value": "192.0.2.20"
}
]
}
]
UPSERT は「値の追加」ではなくレコードセット全体の置き換えです。値を 2 つにしたいときは、残したい値も含めて全部書きます。
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID \
--change-batch '{"Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"api.example.com","Type":"A","TTL":60,"ResourceRecords":[{"Value":"192.0.2.40"},{"Value":"192.0.2.41"}]}}]}' \
--query 'ChangeInfo.Id' --output text
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?Name=='api.example.com.'].ResourceRecords[].Value" --output text
/change/C2682N5HXP0BZ4
192.0.2.40 192.0.2.41
短い変更なら、このように JSON を 1 行の文字列で直接渡すこともできます。
1 回の change-batch は「全部通るか、全部通らないか」
Changes に複数の変更を並べたとき、1 つでも検証を通らないものがあると、全体が取り消されます。 新しい new.example.com の CREATE と、既にある www.example.com の CREATE を 1 つの JSON に入れて試します。
{
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "new.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [{ "Value": "192.0.2.30" }]
}
},
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [{ "Value": "192.0.2.10" }]
}
}
]
}
An error occurred (InvalidChangeBatch) when calling the ChangeResourceRecordSets operation: Tried to create resource record set [name='www.example.com.', type='A'] but it already exists
直後に一覧を見ると、1 つ目の new.example.com も作られていません。
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query 'ResourceRecordSets[].[Name,Type]' --output text
example.com. NS
example.com. SOA
www.example.com. A
この性質は、CNAME からエイリアスへの切り替えのように「消して、すぐ作る」を途切れなく行いたいときに役立ちます。DELETE と CREATE を同じ change-batch に並べれば、片方だけが反映された状態になりません。
CNAME・TXT・MX をまとめて登録する
種別が違っても、Type と Value の書き方が変わるだけです。 3 種類を 1 つの multi.json にまとめます。繰り返し流せるよう、すべて UPSERT にしています。
{
"Comment": "CNAME・TXT・MX をまとめて登録",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "blog.example.com",
"Type": "CNAME",
"TTL": 300,
"ResourceRecords": [{ "Value": "www.example.com" }]
}
},
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "example.com",
"Type": "TXT",
"TTL": 300,
"ResourceRecords": [
{ "Value": "\"v=spf1 include:_spf.example.net ~all\"" },
{ "Value": "\"site-verification=abc123\"" }
]
}
},
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "example.com",
"Type": "MX",
"TTL": 3600,
"ResourceRecords": [
{ "Value": "10 mail1.example.com" },
{ "Value": "20 mail2.example.com" }
]
}
}
]
}
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID \
--change-batch file://multi.json
登録後に TXT と MX の値を取り出すと、次のように入っています。
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?Type=='TXT'].ResourceRecords[].Value"
[
"\"v=spf1 include:_spf.example.net ~all\"",
"\"site-verification=abc123\""
]
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?Type=='MX'].ResourceRecords[].Value" --output text
10 mail1.example.com 20 mail2.example.com
種別ごとの注意点を、AWS の開発者ガイド(サポートされる DNS レコードタイプ)の記載に沿って整理します。
| 種別 | 書き方の注意 |
|---|---|
| CNAME | 値は 1 つだけ。CNAME を作った名前には、ほかのレコードを作れない。Zone Apex(example.com そのもの)には作れない |
| TXT | 値を二重引用符で囲む。1 つの文字列は 255 文字まで。それより長い値は "文字列1" "文字列2" のように分けて 1 つの Value に書く。値全体は 4,000 文字まで |
| TXT(複数) | 同じ名前に用途の違う TXT を置くときは、1 つのレコードセットの ResourceRecords に並べる |
| MX | 「優先度 ホスト名」を半角スペースで区切る。数字が小さいほうが優先 |
同じ名前・同じタイプのレコードセットは 1 つだけです。example.com の TXT を後から追加するときは、既にある値も含めて UPSERT します。
エイリアスレコードを作成する(ALB・CloudFront・S3)
エイリアスレコードは、Route 53 独自の「AWS リソースを指すレコード」です。 TTL と ResourceRecords を書かず、代わりに AliasTarget を書きます。CNAME と違って Zone Apex にも置けます。
ALB を指すエイリアス
{
"Comment": "ALB へのエイリアス",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "app.example.com",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z14GRHDCWA56QT",
"DNSName": "my-alb-1234567890.ap-northeast-1.elb.amazonaws.com",
"EvaluateTargetHealth": true
}
}
}
]
}
CloudFront を指すエイリアス(Zone Apex)
{
"Comment": "CloudFront へのエイリアス(Zone Apex)",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "example.com",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z2FDTNDATAQYW2",
"DNSName": "d111111abcdef8.cloudfront.net",
"EvaluateTargetHealth": false
}
}
}
]
}
S3 のウェブサイトエンドポイントを指すエイリアス
{
"Comment": "S3 ウェブサイトエンドポイントへのエイリアス",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "static.example.com",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z2M4EHUR26P7ZW",
"DNSName": "s3-website-ap-northeast-1.amazonaws.com",
"EvaluateTargetHealth": false
}
}
}
]
}
3 つを順に流したあと、エイリアスだけを一覧すると次のようになります。
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?AliasTarget].[Name,AliasTarget.DNSName,AliasTarget.HostedZoneId]" \
--output table
-------------------------------------------------------------------------------------------------
| ListResourceRecordSets |
+---------------------+------------------------------------------------------+------------------+
| example.com. | d111111abcdef8.cloudfront.net | Z2FDTNDATAQYW2 |
| app.example.com. | my-alb-1234567890.ap-northeast-1.elb.amazonaws.com | Z14GRHDCWA56QT |
| static.example.com.| s3-website-ap-northeast-1.amazonaws.com | Z2M4EHUR26P7ZW |
+---------------------+------------------------------------------------------+------------------+
エイリアス先ごとの HostedZoneId 早見表
AliasTarget の HostedZoneId は、自分のホストゾーン ID ではなく「エイリアス先のサービス側」の ID です。 ここを取り違えるとエラーになります。AWS の API リファレンス(AliasTarget)と、General Reference のエンドポイント一覧に載っている値・調べ方をまとめます。
| エイリアス先 | HostedZoneId |
DNSName に書くもの |
EvaluateTargetHealth |
|---|---|---|---|
| CloudFront | Z2FDTNDATAQYW2(固定) |
ディストリビューションのドメイン名(dxxxx.cloudfront.net) |
false のみ |
| ALB(東京) | Z14GRHDCWA56QT |
ALB の DNSName |
true / false |
| ALB(大阪) | Z5LXEXXYW11ES |
同上 | true / false |
| ALB(バージニア北部) | Z35SXDOTRQ7X7K |
同上 | true / false |
| NLB(東京) | Z31USIVHYNEOWT |
NLB の DNSName |
true / false |
| NLB(大阪) | Z1GWIQ4HH19I5X |
同上 | true / false |
| S3 ウェブサイト(東京) | Z2M4EHUR26P7ZW |
s3-website-ap-northeast-1.amazonaws.com |
true / false |
| S3 ウェブサイト(大阪) | Z2YQB5RD63NC85 |
s3-website.ap-northeast-3.amazonaws.com |
true / false |
| S3 ウェブサイト(バージニア北部) | Z3AQBSTGFYJSTF |
s3-website-us-east-1.amazonaws.com |
true / false |
| Global Accelerator | Z2BJ6XQ5FK7U4H(固定) |
アクセラレーターの DNS 名 | true / false |
| 同じホストゾーン内の別レコード | 自分のホストゾーン ID | 指したいレコードの名前 | true / false |
表に無いリージョンやサービスは、コマンドで調べられます。
| エイリアス先 | 調べるコマンド | 見る項目 |
|---|---|---|
| ALB・NLB | aws elbv2 describe-load-balancers --names 名前 |
DNSName と CanonicalHostedZoneId |
| Classic Load Balancer | aws elb describe-load-balancers |
DNSName と CanonicalHostedZoneNameId |
| API Gateway(リージョン) | aws apigateway get-domain-names |
regionalDomainName と regionalHostedZoneId |
| API Gateway(エッジ最適化) | aws apigateway get-domain-names |
distributionDomainName と distributionHostedZoneId |
| VPC インターフェイスエンドポイント | aws ec2 describe-vpc-endpoints |
DnsName と HostedZoneId |
ALB なら、次の 1 行で 2 つの値をまとめて取り出せます。
aws elbv2 describe-load-balancers --names my-alb \
--query 'LoadBalancers[0].[DNSName,CanonicalHostedZoneId]' --output text
エイリアスと CNAME の選び方
| 比べる点 | エイリアス | CNAME |
|---|---|---|
| 向け先 | AWS リソース、または同じホストゾーン内のレコード | どの DNS 名でも指定できる |
| Zone Apex に置けるか | 置ける | 置けない |
| TTL | 指定しない(向け先に従う) | 指定する |
| AWS リソース向けクエリの料金 | かからない | 通常のクエリとして数えられる |
dig での見え方 |
A レコードなど指定したタイプで返る | CNAME として返る |
AWS リソースを指すならエイリアス、AWS の外のホスト名を指すなら CNAME、と覚えておくと選びやすくなります。
レコードを一覧して --query で絞り込む
list-resource-record-sets は全レコードを返すので、--query(JMESPath)で必要な部分だけを取り出します。
名前・タイプ・TTL を表で見る例です。エイリアスは TTL を持たないため None と表示されます。
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query 'ResourceRecordSets[].[Name,Type,TTL]' --output table
--------------------------------------------
| ListResourceRecordSets |
+----------------------+--------+----------+
| example.com. | A | None |
| example.com. | MX | 3600 |
| example.com. | NS | 172800 |
| example.com. | SOA | 900 |
| example.com. | TXT | 300 |
| app.example.com. | A | None |
| blog.example.com. | CNAME | 300 |
| static.example.com. | A | None |
| www.example.com. | A | 300 |
+----------------------+--------+----------+
よく使う絞り込みをまとめます。
| 取り出したいもの | --query の書き方 |
|---|---|
| 特定の名前のレコード | "ResourceRecordSets[?Name=='www.example.com.']" |
| 特定のタイプだけ | "ResourceRecordSets[?Type=='TXT']" |
| エイリアスだけ | "ResourceRecordSets[?AliasTarget]" |
| エイリアス以外の A レコードの名前と値 | "ResourceRecordSets[?Type=='A' && !AliasTarget].[Name,ResourceRecords[0].Value]" |
| 名前に文字列を含むもの | "ResourceRecordSets[?contains(Name,'app')]" |
| レコード数 | "length(ResourceRecordSets)" |
エイリアス以外の A レコードだけを取り出した結果です。
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?Type=='A' && !AliasTarget].[Name,ResourceRecords[0].Value]" \
--output text
www.example.com. 192.0.2.20
レコードが多いゾーンでは、開始位置と件数を指定して少しずつ読むこともできます。
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--start-record-name blog.example.com --start-record-type CNAME --max-items 1
{
"ResourceRecordSets": [
{
"Name": "blog.example.com.",
"Type": "CNAME",
"TTL": 300,
"ResourceRecords": [
{
"Value": "www.example.com"
}
]
}
],
"IsTruncated": true,
"NextRecordName": "static.example.com.",
"NextRecordType": "A",
"MaxItems": "1"
}
IsTruncated が true のときは、NextRecordName と NextRecordType を次の開始位置に指定すると続きを読めます。
レコードを削除する(DELETE)
DELETE は、名前とタイプだけでなく、TTL と値(エイリアスなら AliasTarget)まで登録内容と同じに書きます。 AWS の API リファレンスにも「作成時に指定した値と同じ値をすべて指定する」とあります。
いま www.example.com は 192.0.2.20・TTL 300 なので、a-delete.json は次のようになります。
{
"Comment": "www の A レコードを削除",
"Changes": [
{
"Action": "DELETE",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [{ "Value": "192.0.2.20" }]
}
}
]
}
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID \
--change-batch file://a-delete.json
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query "ResourceRecordSets[?Name=='www.example.com.']"
[]
登録内容をそのまま使って削除するスクリプト
値や TTL を手で写すと、1 文字の違いで DELETE が通らなくなります。いまの登録内容を list-resource-record-sets で取り出し、そのまま DELETE に渡すと写し間違いがなくなります。エイリアスレコードにも同じスクリプトが使えます。
#!/bin/bash
# 使い方: bash delete-record.sh ホストゾーンID レコード名(末尾ドットつき) タイプ
# 例: bash delete-record.sh Z0123456789ABC www.example.com. A
set -euo pipefail
ZONE_ID=$1; NAME=$2; TYPE=$3
# いまの登録内容をそのまま取り出す(値・TTL・エイリアス先を手で写さない)
RRSET=$(aws route53 list-resource-record-sets --hosted-zone-id "$ZONE_ID" \
--query "ResourceRecordSets[?Name=='$NAME' && Type=='$TYPE'] | [0]")
if [ "$RRSET" = "null" ]; then
echo "record not found: $NAME $TYPE"
exit 1
fi
aws route53 change-resource-record-sets --hosted-zone-id "$ZONE_ID" \
--change-batch "{\"Changes\":[{\"Action\":\"DELETE\",\"ResourceRecordSet\":$RRSET}]}" \
--query 'ChangeInfo.Id' --output text
echo "deleted: $NAME $TYPE"
A レコードとエイリアスレコードを順に削除し、最後にもう一度同じレコードを指定した結果です。
bash delete-record.sh $ZONE_ID www.example.com. A
bash delete-record.sh $ZONE_ID app.example.com. A
bash delete-record.sh $ZONE_ID www.example.com. A
/change/C2682N5HXP0BZ4
deleted: www.example.com. A
/change/C2682N5HXP0BZ4
deleted: app.example.com. A
record not found: www.example.com. A
ホストゾーンを削除する
ホストゾーンは、NS と SOA 以外のレコードが残っていると削除できません。
aws route53 delete-hosted-zone --id $ZONE_ID
An error occurred (HostedZoneNotEmpty) when calling the DeleteHostedZone operation: The hosted zone contains resource records that are not SOA or NS records.
残りのレコードを DELETE でまとめて消してから、もう一度実行します。複数の DELETE も 1 つの change-batch に並べられます。
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID \
--change-batch file://delete-rest.json
aws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID \
--query 'ResourceRecordSets[].[Name,Type]' --output text
example.com. NS
example.com. SOA
aws route53 delete-hosted-zone --id $ZONE_ID
aws route53 list-hosted-zones --query 'HostedZones[].Name'
[]
InvalidChangeBatch などのエラー文と直し方
エラー文の「Tried to …」以降を読むと、どのレコードの何が合っていないかがわかります。 まず、今回の確認環境で実際に出たものです。
| 操作 | エラー文 | 直し方 |
|---|---|---|
| 既にあるレコードに CREATE | (InvalidChangeBatch) … Tried to create resource record set [name='www.example.com.', type='A'] but it already exists |
UPSERT に変える。または既存を確認して名前を見直す |
| Action を小文字で書いた | (InvalidInput) … Invalid XML ; cvc-enumeration-valid: Value 'create' is not facet-valid with respect to enumeration '[CREATE, DELETE, UPSERT]'. |
CREATE UPSERT DELETE を大文字で書く |
| ホストゾーン ID が違う | (NoSuchHostedZone) … No hosted zone found with ID: Z0000000000 |
list-hosted-zones-by-name で ID を取り直す |
| レコードが残ったままゾーン削除 | (HostedZoneNotEmpty) … The hosted zone contains resource records that are not SOA or NS records. |
NS・SOA 以外を先に削除する |
次に、AWS 公式ナレッジセンター(Route 53 のレコード作成時のエラー対処、CNAME 作成時のエラー対処)に掲載されているエラー文です。ドメイン名の部分は掲載どおりの例です。
| 場面 | 掲載されているエラー文 | 原因と直し方 |
|---|---|---|
| DELETE の名前・タイプ・TTL が登録内容と違う | Tried to delete resource record set [name='a.domain.com.', type='A'] but the values provided do not match... |
JSON の Name・Type・TTL を現在の登録内容に合わせる |
| DELETE の値が登録内容と違う | Tried to delete resource record set [name='a.domain.com.', type='A'] but the rdata provided is invalid |
ResourceRecords の値を現在の登録内容に合わせる |
| 無いレコードを DELETE | Tried to delete resource record set [name='a.domain.com.', type='A'] but it was not found. |
ホストゾーン ID とレコード名を確認する |
| Zone Apex に CNAME | RRSet of type CNAME with DNS name example.com. is not permitted at apex in zone example.com |
Zone Apex はエイリアスレコードにする |
| ほかのレコードがある名前に CNAME | RRSet of type CNAME with DNS name test.example.com. is not permitted as it conflicts with other records with the same DNS name |
既存レコードを整理する。AWS リソース向けならエイリアスにする |
| CNAME がある名前に A・MX・TXT | RRSet of type MX with DNS name b.domain.com. is not permitted because a conflicting RRSet of type CNAME... |
CNAME と同じ名前には置けないので、別の名前にする |
| エイリアスの HostedZoneId が違う | Tried to create an alias that targets applicationenv.us-west-2.elasticbeanstalk.com., type A... |
HostedZoneId をエイリアス先のサービス・リージョンの ID にする |
DELETE に関する上の 3 つは、前の節の delete-record.sh のように現在の登録内容を取り出してそのまま渡すことでまとめて避けられます。
よくある質問
Q. CREATE と UPSERT はどちらを使えばよいですか? A. 手順書やスクリプトで繰り返し流すなら UPSERT が扱いやすいです。既にあっても無くても同じ結果になります。既存のレコードをうっかり置き換えたくない新規登録では CREATE にしておくと、同じ名前・タイプがあるときにエラーで知らせてくれます。
Q. レコード名の末尾のドットは必要ですか?
A. change-batch の Name はドットなしで書いても登録できました。一方、list-resource-record-sets が返す Name は末尾ドットつきなので、--query で名前を比較するときは www.example.com. と書きます。
Q. TXT レコードの値はなぜ \" と書くのですか?
A. TXT の値は二重引用符で囲む決まりがあり、その引用符を JSON の文字列の中に入れるために \" とエスケープします。結果として "\"v=spf1 ...\"" という形になります。
Q. 変更が反映されたかはどう確かめますか?
A. change-resource-record-sets が返す変更 ID を aws route53 get-change --id に渡すと状態が見られます。INSYNC なら Route 53 のネームサーバーへの反映が済んでいます。スクリプトでは aws route53 wait resource-record-sets-changed --id で待てます。
まとめ
- レコードの作成・更新・削除はすべて
change-resource-record-setsで行い、JSON のActionとResourceRecordSetを書き分ける - CREATE は既存があると止まり、UPSERT は置き換え、DELETE は登録内容との一致が必要
- 1 つの change-batch は全部通るか全部通らないか。DELETE と CREATE を並べると切り替えが途切れない
- TXT は
\"で囲み、MX は「優先度 ホスト名」、CNAME は値 1 つで Zone Apex には置けない - エイリアスは
AliasTargetを書き、HostedZoneIdはエイリアス先サービスの ID を使う - DELETE は現在の登録内容を取り出してそのまま渡すと写し間違いがなくなる
- 一覧は
--queryで絞り込み、比較する名前には末尾のドットをつける
参考・出典
- AWS Route 53 API リファレンス:ChangeResourceRecordSets、AliasTarget
- Amazon Route 53 開発者ガイド:サポートされる DNS レコードタイプ、エイリアスレコードと非エイリアスレコードの選択
- AWS General Reference:Elastic Load Balancing のエンドポイント、Amazon S3 ウェブサイトエンドポイント
- AWS ナレッジセンター:Troubleshoot errors when creating Route 53 resource record sets with the AWS CLI、Troubleshoot CNAME record creation errors in Route 53
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Route 53レコードをAWS CLIで作成|A・CNAME・エイリアス例」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。