概要
手動運用している Cloudflare の管理を Terraform に移行する手順についてです。
環境
- terraform 1.11
- cloudflare provider v4.52.0
- cf-terraforming v0.23.3
前提知識
APIキーの種類と使い分け
Cloudflare には Global API Key(legacy) と API Token があり、以下の様な違いがあります。
| 項目 | Global API Key | API Token |
|---|---|---|
| 認証方式 | メールアドレス + APIキー(1セット) | 単体(Bearer Token) |
| 発行数 | 1アカウントに1つだけ固定 | 無制限に発行可能 |
| スコープ制限 | なし(=全権限持ち) | あり(最小限の権限を選択して発行できる) |
| 使用用途 | 古いAPIや一部ツール(cf-terraformingなど)で使用 | 新しいツールやCI/CDでの安全な操作 |
| ローテーション | 手動のみ(漏洩時の対処が大変) | トークン単位で停止・削除が可能 |
通常はAPI Tokenが推奨されますが、今回Cloudflareの各リソースを全てTerraformに移行したいような場合はスコープ範囲の指定が大変なので、Global API Keyの方が手軽ではあります。
ただしセキュリティリスクは高いので厳重に扱ってください。
移行手順
cf-terraforming
cf-terraforming というimportツールを使用します。
インストール
次のようにインストールします。
$ brew tap cloudflare/cloudflare $ brew install cloudflare/cloudflare/cf-terraforming
ディレクトリの用意
Cloudflareはドメイン毎の設定と、全ドメインで利用できるグローバルな設定があるため、以下の様なディレクトリ構造をとると良いです。
. ├── domains # ドメイン毎の設定 │ └── jun06t-com │ ├── provider.tf │ ├── record.tf │ ├── ruleset.tf │ ├── terraform.tf │ └── variables.tf └── global # グローバルな設定 └── workers
cf-terraformingはterraform initとした環境でないと実行できないので、事前にこの準備が必要です。
APIキーの発行
以下の手順でトークンを発行します。
- Global API Keyの場合
- API Tokenの場合
僕の場合は前者のGlobal API Keyを使いました。
環境変数への設定
cf-terraformingは次の環境変数をサポートしているので、それに合わせてセットします。
CLOUDFLARE_API_TOKEN- API Token based authenticationCLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY- API Key based authentication
$ export CLOUDFLARE_EMAIL=xxxx $ export CLOUDFLARE_API_KEY=xxxx
ZoneIDの取得
ドメイン毎の設定の場合はZoneIDを指定する必要があります。
次のAPIを呼んで一覧取得できます。ドメインが複数ある場合はそれぞれメモしておきましょう。
$ curl -X GET "https://api.cloudflare.com/client/v4/zones" \ -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \ -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \ -H "Content-Type: application/json" { "id": "xxxx", # zone id "name": "jun06t.com", # ドメイン "status": "active", "paused": false, "type": "partial", "development_mode": 0, "verification_key": "xxxx-xxxx", "cname_suffix": "cdn.cloudflare.net", "original_name_servers": [ ...,
ちなみにそのドメインの詳細を取得したい場合は次のAPIを呼びます。
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID" \ -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \ -H "X-Auth-Key: $CLOUDFLARE_API_KEY" \ -H "Content-Type: application/json"
Terraform
cf-terraformingの準備が整ったのでterraformにimportしていきます。
terraform init
前述した様にcf-terraformingはterraform initした環境でないと使えないので最初にこれをします。
$ terraform init
インポート
手動運用のCloudflare設定をインポートするにはgenerateコマンドを使います。
$ cf-terraforming generate \ --resource-type cloudflare_record \ --zone $CLOUDFLARE_ZONE_ID > record.tf
import可能なresource-typeの一覧はこちらです。
terraform import
importコマンドを実行すると、terraform importするコマンドも用意してくれます。
$ cf-terraforming import \ --resource-type cloudflare_record \ --zone $CLOUDFLARE_ZONE_ID terraform import cloudflare_record.terraform_managed_resource_xxxx ZONE_ID/xxxx
tfstateの調整のため、上記のコマンドをローカルから実行します。
その他
エラーケース
セグフォエラー
次のエラーが出た場合です。
$ cf-terraforming generate \ --resource-type "cloudflare_record" \ --zone $CLOUDFLARE_ZONE_ID panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x2 addr=0x28 pc=0x104d4da64] goroutine 1 [running]: github.com/hashicorp/go-version.(*Version).String(0x0) /Users/jun06t/Library/Caches/Homebrew/go_mod_cache/pkg/mod/github.com/hashicorp/go-version@v1.7.0/version.go:386 +0x34 github.com/cloudflare/cf-terraforming/internal/app/cf-terraforming/cmd.init.generateResources.func2(0x105a3e3c0, {0x104fbeb33?, 0x4?, 0x104fbea67?}) internal/app/cf-terraforming/cmd/generate.go:91 +0x470 github.com/spf13/cobra.(*Command).execute(0x105a3e3c0, {0x1400009afc0, 0x7, 0x7})
terraform initした環境でないと発生します。
認証エラー
正しいGlobal API Keyであるものの、次のようなエラーが発生しました。
DEBU[0000] initializing cloudflare-go with API Token account_Id= zone_id=xxxx DEBU[0001] initializing Terraform in . DEBU[0001] detected provider version=4.52.0 DEBU[0001] reading Terraform schema for Cloudflare provider DEBU[0002] reading and building resource resource=cloudflare_record 2025/04/23 09:52:14 GET /client/v4/zones/xxxx/dns_records?page=1&per_page=100 HTTP/1.1 Host: api.cloudflare.com User-Agent: cloudflare-go/v4 Authorization: Bearer [redacted] Content-Type: application/json Accept-Encoding: gzip 2025/04/23 09:52:14 HTTP/2.0 400 Bad Request Connection: close Api-Version: 2025-04-23 Cf-Auditlog-Id: xxx Cf-Cache-Status: DYNAMIC Cf-Ray: xxx-NRT Content-Type: application/json Date: Wed, 23 Apr 2025 00:52:14 GMT Server: cloudflare Set-Cookie: __cflb=xxx; SameSite=Lax; path=/; expires=Wed, 23-Apr-25 03:22:15 GMT; HttpOnly; Secure; SameSite=None Vary: Accept-Encoding {"success":false,"errors":[{"code":10001,"message":"Unable to authenticate request"}]}
これはCLOUDFLARE_API_TOKENがセットされていると、cf-terraformingは優先的にそれをAPI Tokenとして使うためです。
なのでGlobal API Keyを使う場合は
$ unset CLOUDFLARE_API_TOKEN
をしておくと良いです。
DNSレコードはimportしても変更点が生まれた
特定バージョンなどだけかも知れませんが、importしても次のIssueのように変更点が生まれてしまうケースがあるようです。
僕の場合はDNSレコードがそうでした。
既存のDNSレコードが上書きされた場合のリスクが高いため、今回は「既存はコメントアウトにしてimportせず、新規レコードは追記していく」といった方針としました。
まとめ
ネット上にある Cloudflare の管理を Terraform に移行する手順で、自分がハマった点を追記してまとめてみました。
同じようにハマった人の参考になれば嬉しいです。