2026年6月8日
2026年6月8日
DNSimple で複数ドメインを管理する方法|Terraform でレコードを一元管理する手順
DNSimple で複数のドメインを扱うなら、共通のレコード(Web サーバーの A、www の CNAME、メール関連の TXT など)を Terraform のコードにまとめ、ドメインの一覧をループで回す形にすると、ドメインが増えても設定のばらつきが出にくくなります。この記事では、DNSimple の Terraform プロバイダーの準備、for_each を使った複数ドメインの書き方、既存レコードの取り込み、手作業との混在で起きる問題とその防ぎ方を説明します。
管理画面での1件ずつの登録や、API を直接呼ぶ方法は別の記事の範囲とし、ここでは「数ドメイン〜数十ドメインを、同じ型で揃えて変更履歴も残したい」という場面を前提にします。Terraform の基本操作(init・plan・apply)を一度試したことがある方を想定しています。
なぜ Terraform で管理するのか
ドメインが数個のうちは管理画面で足ります。困るのは、ドメインが増えて次のようなことが起き始めたときです。
- あるドメインだけ SPF や DMARC の TXT が古いまま残っている
- サーバー移転で A レコードを書き換えたが、いくつかのドメインを忘れていた
- 誰がいつ、なぜレコードを変えたのか分からない
Terraform では、あるべき状態をコードに書き、terraform plan で現在との差分を確かめてから terraform apply で反映します。コードを Git で管理すれば、変更の理由をコミットやプルリクエストに残せます。DNSimple の管理画面にもレコードのテンプレート機能がありますが、テンプレートは「適用した時点のコピー」なので、後から全ドメインをまとめて揃え直す用途には、コードで管理するほうが向いています。
プロバイダーとトークンを準備する
DNSimple のプロバイダーは、Terraform Registry で dnsimple/dnsimple として公開されています(2026年10月現在)。版によってリソース名や引数が変わることがあるため、使う版のドキュメントを Registry で確かめてから書きます。
terraform {
required_version = ">= 1.5.0"
required_providers {
dnsimple = {
source = "dnsimple/dnsimple"
version = "~> 1.0" # Registry で現行の版を確認して合わせる
}
}
}
provider "dnsimple" {
# token と account は環境変数 DNSIMPLE_TOKEN / DNSIMPLE_ACCOUNT から読む
sandbox = false
}
認証には、DNSimple の管理画面で発行する API アクセストークンと、アカウント ID を使います。トークンはコードに直接書かず、環境変数で渡します。
export DNSIMPLE_TOKEN="your-api-token"
export DNSIMPLE_ACCOUNT="your-account-id"
トークンには、ユーザー単位のものとアカウント単位のものがあります。チームや CI から使う場合は、特定の個人に結びつかないアカウント単位のトークンにしておくと、担当者の異動で動かなくなる事態を避けられます。CI ではシークレット管理の機能に保存し、ログに表示されないようにします。
DNSimple には本番とは別のサンドボックス環境があり、sandbox = true にするとそちらを操作します。サンドボックスは別アカウント・別トークンなので、コードの書き方を試すときに使うと、本番のゾーンを誤って変える心配がありません。
複数ドメインを1つの定義で回す
ここでは、各ドメインに「頂点の A」「www の CNAME」「メールを送らないドメイン向けの SPF」の3件を作る例を示します。ドメインごとに違う値(ここでは IP アドレス)は、ドメイン名をキーにした map に持たせます。
locals {
sites = {
"example.com" = "203.0.113.10"
"example.net" = "203.0.113.20"
"example.org" = "203.0.113.20"
}
# ドメイン × 共通レコードを1つの map に展開する
records = merge([
for zone, ip in local.sites : {
"${zone}/apex-A" = { zone = zone, name = "", type = "A", value = ip, priority = null }
"${zone}/www-CNAME" = { zone = zone, name = "www", type = "CNAME", value = zone, priority = null }
"${zone}/apex-SPF" = { zone = zone, name = "", type = "TXT", value = "v=spf1 -all", priority = null }
}
]...)
}
resource "dnsimple_zone_record" "this" {
for_each = merge(local.records, local.extra_records)
zone_name = each.value.zone
name = each.value.name
type = each.value.type
value = each.value.value
priority = each.value.priority
ttl = 3600
}
ポイントは3つあります。
1つ目は、for_each のキーを「ドメイン名/用途」の形にしていることです。キーは Terraform がリソースを見分ける名前なので、count で番号を振るとドメインの並び順を変えただけで別のリソースと見なされ、削除と再作成が起きます。意味のある文字列キーにしておけば、ドメインを追加・削除しても他のドメインのレコードには影響しません。
2つ目は、頂点(ゾーンそのもの)のレコードは name = "" で表すことです。@ ではありません。
3つ目は、v=spf1 -all は「このドメインからはメールを送らない」という意味だということです。メールを送るドメインに当てはめると送信できなくなるため、送信するドメインは別の map で個別の値を持たせます。共通の型と個別の例外を分けておくと、コードを読んだ人にも意図が伝わります。
リソースの for_each では、共通レコードとドメインごとの追加レコード(extra_records)を merge でまとめています。追加レコードは次のように別の map で定義します。追加がなければ空の map(extra_records = {})にしておきます。
locals {
extra_records = {
"example.com/mail-MX" = { zone = "example.com", name = "", type = "MX", value = "mail.example.com", priority = 10 }
}
}
MX のように優先度が必要なレコードは、priority に値を入れます。A や CNAME では priority = null にして、すべての要素が同じ項目を持つように揃えています。項目の有無がばらばらだと、merge した結果を for_each に渡すときに型が合わずエラーになることがあるためです。
頂点を CDN やロードバランサーのホスト名に向けたい場合、DNSimple では ALIAS レコードが使えます。CNAME は頂点に置けないため、type = "ALIAS" を使い分けます。
既存のレコードを取り込む
すでに管理画面で作ったレコードがある状態で同じ内容を apply すると、重複して登録されることがあります。先に既存のレコードを Terraform の管理下に取り込みます。Terraform 1.5 以降では、import ブロックで宣言できます。
import {
to = dnsimple_zone_record.this["example.com/www-CNAME"]
id = "example.com_12345678"
}
ID は「ゾーン名_レコード ID」の形式です。レコード ID は API のレコード一覧で確認できます。
curl -s -H "Authorization: Bearer $DNSIMPLE_TOKEN" \
"https://api.dnsimple.com/v2/$DNSIMPLE_ACCOUNT/zones/example.com/records" \
| jq -r '.data[] | "\(.id)\t\(.type)\t\(.name)\t\(.content)"'
取り込んだあとに terraform plan を実行し、差分が出ないことを確かめます。TTL や値の表記(末尾のドット、TXT の引用符など)が食い違って差分が出る場合は、コード側を実際の値に合わせてから先に進みます。取り込みの作業中は、作業前にレコード一覧を JSON などで保存しておくと、誤って変えたときに元へ戻せます。
運用で守ること
手作業と混在させない
Terraform は、自分が作った(または取り込んだ)レコードだけを管理します。管理画面で追加したレコードは消されませんが、Terraform 管理下のレコードを画面で書き換えると、次の apply でコードの値に戻ります。この「戻された」がトラブルの原因になりやすいため、Terraform で管理するドメインでは画面からの変更をしない、と決めて共有します。緊急で画面から直した場合は、すぐにコードにも反映します。
plan を読んでから apply する
DNS の変更は、誤るとサイトやメールが止まります。terraform plan の出力で、意図しない destroy(削除)や replace(作り直し)が含まれていないかを見ます。CI でプルリクエストごとに plan の結果をコメントに出す形にしておくと、レビューする人も差分を確かめられます。
state を安全な場所に置く
state ファイルには、管理しているリソースの情報が入ります。複数人で扱うなら、ローカルではなくリモートのバックエンドに置き、同時実行を防ぐロックを有効にします。state をそのまま Git に入れるのは避けます。
反映後は実際の応答を確かめる
apply が成功しても、DNS の応答が変わったことは別に確かめます。DNSimple のネームサーバーに直接問い合わせると、キャッシュの影響を受けずに確認できます。
dig @ns1.dnsimple.com example.net A +short
dig @ns1.dnsimple.com www.example.net CNAME +short
TTL を長くしている場合、利用者側のキャッシュが切れるまでは古い値が返ることがあります。サーバー移転のように切り替えのタイミングが大事な変更では、数日前に TTL を短くする変更を先に apply しておきます。
まとめ
- 複数ドメインの共通レコードは、ドメイン名をキーにした map と
for_eachで1つの定義にまとめる for_eachのキーは「ドメイン名/用途」のような意味のある文字列にし、countの番号に頼らない- 頂点は
name = ""、頂点を別ホスト名に向けるときは ALIAS を使う - 既存レコードは
importで取り込み、planで差分が出ないことを確かめてから運用に入る - トークンは環境変数やシークレット管理で渡し、プロバイダーの版や画面の項目は Registry と公式ドキュメントで確認する(2026年10月現在の情報です)