Carpe Diem

備忘録

Cloudflare の管理を Terraform に移行する

概要

手動運用している 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を使いました。

環境変数への設定

cf-terraformingは次の環境変数をサポートしているので、それに合わせてセットします。

  • CLOUDFLARE_API_TOKEN - API Token based authentication
  • CLOUDFLARE_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の一覧はこちらです。

GitHub - cloudflare/cf-terraforming: A command line utility to facilitate terraforming your existing Cloudflare resources.

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のように変更点が生まれてしまうケースがあるようです。

github.com

僕の場合はDNSレコードがそうでした。

既存のDNSレコードが上書きされた場合のリスクが高いため、今回は「既存はコメントアウトにしてimportせず、新規レコードは追記していく」といった方針としました。

まとめ

ネット上にある Cloudflare の管理を Terraform に移行する手順で、自分がハマった点を追記してまとめてみました。

同じようにハマった人の参考になれば嬉しいです。

参考