モチベ

インフラの学習のため何か良い題材がないかと探していたが、そういえばCloude Flareで管理しているDNSをTerraformに吸収させていないなではないかと思いやることにした。

今後はAWSなどインフラで遊んで作った成果物はTerraformに吸収させるようにしよう。

API Tokneの作成

Cloudflareの公式にあるTerraformを用いた管理のチュートリアルにAPI Tokneを使用する箇所があるので、作成する。

https://developers.cloudflare.com/terraform/tutorial/track-history/

Claudeflareの公式が出しているAPI Tokenの作成チュートリアルを読みながら作成していく。

https://developers.cloudflare.com/fundamentals/api/get-started/create-token/

  1. profile を選択する

toke-create-image-1

  1. API Tokensを選択する

toke-create-image-2

  1. Create Tokenを選択する

toke-create-image-3

  1. 今回はDNSを取得したいのでEdit zone DNSを選択する

toke-create-image-4

  1. Zoneリソースは特定のDNSのzone(今回はshooonng.com)に絞る。

toke-create-image-5

  1. 指定したZoneで問題がなければCreate Tokenをクリックする

toke-create-image-6

  1. Tokenが払い出される

toke-create-image-7

  1. Curlが通るか確認
on ☸ admin on kubernetes-the-hard-way in default () dotfile on  master
♥ ❯ curl "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer AmO_qsxxxxxxxxxxxxxxx" | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   184    0   184    0     0    892      0 --:--:-- --:--:-- --:--:--   888
{
  "result": {
    "id": "f8a950762362ca1866ab89d675bca82e",
    "status": "active"
  },
  "success": true,
  "errors": [],
  "messages": [
    {
      "code": 10000,
      "message": "This API Token is valid and active",
      "type": null
    }
  ]
}

on ☸ admin on kubernetes-the-hard-way in default () dotfile on  master
♥

importしてみる

以下の2つを実施する。

  1. state反映用のファイルを作成する
  2. stateに反映してimportする

先に以下3つをexportして変数として登録しておく。

  • CLOUDFLARE_EMAIL - Cloudflareに登録しているメールアドレス
  • CLOUDFLARE_API_KEY - 上記で取得したAPI Token
  • CLOUDFLARE_ZONE_ID - CloudflareのZone ID

state反映用のファイルを作成する

ファイルを作成するにはcf-terraformingというツールを用いる

cf-terraformingのGitHub 公式のチュートリアル

フューチャーさんの記事がわかりやすかった

公式のチュートリアルだとcf-tarraformingのver5.0のプロバイダーは cloudflare_record を定義していないためエラーが発生する。

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default
♥ ❯ cf-terraforming generate --email $CLOUDFLARE_EMAIL --token $CLOUDFLARE_API_TOKEN -z d2ff8f58d8813dd4470dfa55f09540b3 --resource-type cloudflare_record > importing-example.tf
INFO[0000] terraform-binary-path flag not set
INFO[0000] Using explicit Terraform install path         terraform-install-path=.
INFO[0004] detected provider                             registry=registry.terraform.io/cloudflare/cloudflare version=5.15.0
WARN[0004] Unsupported terraform v5 provider resource    resource=cloudflare_record

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default took 5s
♥

cloudflare_dns_record に変更すると動くようになる 最終的に以下のようなコマンドになる

cf-terraforming generate --email $CLOUDFLARE_EMAIL --token $CLOUDFLARE_API_TOKEN -z d2ff8f58d8813dd4470dfa55f09540b3 --resource-type cloudflare_dns_record > importing-example.tf

出来上がったファイルは以下のようになっている。

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default took 3s
♥ ❯ cat importing-example.tf
resource "cloudflare_dns_record" "terraform_managed_resource_c4e7f3d4b45b022a456c762a04868488_0" {
  content = "_1e50acc97e023be2f97dc14014edac0b.jkddzztszm.acm-validations.aws"
  name    = "_6e2510b7de5c2f6a659fa878d7fe59f2.shooonng.com"
  proxied = false
  tags    = []
  ttl     = 1
  type    = "CNAME"
  zone_id = "d2ff8f58d8813dd4470dfa55f09540b3"
  settings = {
    flatten_cname = false
  }
}

resource "cloudflare_dns_record" "terraform_managed_resource_54b5da91eeb0ec9e09d924fc84c2cb5c_1" {
  content = "d2474kqaq8cosu.cloudfront.net"
  name    = "myblog.shooonng.com"
  proxied = false
  tags    = []
  ttl     = 1
  type    = "CNAME"
  zone_id = "d2ff8f58d8813dd4470dfa55f09540b3"
  settings = {
    flatten_cname = false
  }
}


on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default
♥

stateに反映してimportする

まだtfstateに反映できていないので、 terraform plan して反映する。

ローカルの設定をimportするため再び cf-terraformingを使用する。コマンドは以下を使用する。

cf-terraforming import --resource-type "cloudflare_dns_record" --email $CLOUDFLARE_EMAIL --key $CLOUDFLARE_API_KEY --zone $CLOUDFLARE_ZONE_ID

terraform import 用のコマンドが作成されるので実行してimportする。

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 default
♥ ❯ tf import cloudflare_dns_record.terraform_managed_resource_c4e7f3d4b45b022a456c762a04868488_0 d2ff8f58d8813dd4470dfa55f09540b3/c4e7f3d4b45b022a456c762a04868488

cloudflare_dns_record.terraform_managed_resource_c4e7f3d4b45b022a456c762a04868488_0: Importing from ID "d2ff8f58d8813dd4470dfa55f09540b3/c4e7f3d4b45b022a456c762a04868488"...
cloudflare_dns_record.terraform_managed_resource_c4e7f3d4b45b022a456c762a04868488_0: Import prepared!
  Prepared cloudflare_dns_record for import
cloudflare_dns_record.terraform_managed_resource_c4e7f3d4b45b022a456c762a04868488_0: Refreshing state... [id=c4e7f3d4b45b022a456c762a04868488]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.


on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default took 2s
♥ ❯ tf import cloudflare_dns_record.terraform_managed_resource_54b5da91eeb0ec9e09d924fc84c2cb5c_1 d2ff8f58d8813dd4470dfa55f09540b3/54b5da91eeb0ec9e09d924fc84c2cb5c

cloudflare_dns_record.terraform_managed_resource_54b5da91eeb0ec9e09d924fc84c2cb5c_1: Importing from ID "d2ff8f58d8813dd4470dfa55f09540b3/54b5da91eeb0ec9e09d924fc84c2cb5c"...
cloudflare_dns_record.terraform_managed_resource_54b5da91eeb0ec9e09d924fc84c2cb5c_1: Import prepared!
  Prepared cloudflare_dns_record for import
cloudflare_dns_record.terraform_managed_resource_54b5da91eeb0ec9e09d924fc84c2cb5c_1: Refreshing state... [id=54b5da91eeb0ec9e09d924fc84c2cb5c]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.


on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default took 2s
♥

terraform plan をして差分がないことを確認する。

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default
♥ ❯ tf plan
cloudflare_dns_record.terraform_managed_resource_54b5da91eeb0ec9e09d924fc84c2cb5c_1: Refreshing state... [id=54b5da91eeb0ec9e09d924fc84c2cb5c]
cloudflare_dns_record.terraform_managed_resource_c4e7f3d4b45b022a456c762a04868488_0: Refreshing state... [id=c4e7f3d4b45b022a456c762a04868488]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform/cloudflare on  master [!?] 🏎💨 v1.14.3default took 2s
♥

最終的なファイル構成や中身

ファイル構成は以下のようになる。

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform on  master [!?]
♥ ❯ tree cloudflare
cloudflare
├── backend.tf
├── dns.tf
├── env.sh
├── provider.tf
├── terraform.tfvars
└── variables.tf

1 directory, 6 files

on ☸ admin on kubernetes-the-hard-way in default () infra-terraform on  master [!?]
♥

backend.tfはs3を指定して、そこにtfstateを保管している。

terraform {
  backend "s3" {
    region       = "ap-northeast-1"
    bucket       = "shooonng-iac-tfstate"
    key          = "cloudflare/terraform.tfstate"
    use_lockfile = true
  }
}

cloudflareのDNSは以下のようにしている。名前が変更できないのがちょっと残念だ。

resource "cloudflare_dns_record" "terraform_managed_resource_c4e7f3d4b45b022a456c762a04868488_0" {
  content = "_1e50acc97e023be2f97dc14014edac0b.jkddzztszm.acm-validations.aws"
  name    = "_6e2510b7de5c2f6a659fa878d7fe59f2.shooonng.com"
  proxied = false
  tags    = []
  ttl     = 1
  type    = "CNAME"
  zone_id = var.zone_id
  settings = {
    flatten_cname = false
  }
}

resource "cloudflare_dns_record" "terraform_managed_resource_54b5da91eeb0ec9e09d924fc84c2cb5c_1" {
  content = "d2474kqaq8cosu.cloudfront.net"
  name    = "myblog.shooonng.com"
  proxied = false
  tags    = []
  ttl     = 1
  type    = "CNAME"
  zone_id = var.zone_id
  settings = {
    flatten_cname = false
  }
}

variables.tfは以下のように記載した。詳しい値はtfvarsに書いている。 もう少し上手く使う方法を模索したいな。

variable "zone_id" {
  description = "Cloudflare Zone ID"
  type        = string
  sensitive   = true
}

variable "account_id" {
  description = "Cloudflare Account ID"
  type        = string
  sensitive   = true
}

variable "domain" {
  description = "Domain name"
  type        = string
  default     = "example.com"
}

provider.tfは以下のように記載した。 ただ変数をexportすることが多かったのであまり上手い使い方ができてないかもなと思っている。

terraform {
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 5"
    }
  }
}

provider "cloudflare" {
  # Cloudflare email saved in $CLOUDFLARE_EMAIL
  # Cloudflare API token saved in $CLOUDFLARE_API_TOKEN
}

まとめ

チュートリアルを読めばcloudflareをterraformに吸収するのはそこまで難しくなかった。 これからは何か変更がある時はterraformをいじれば良いので管理が楽になったと思う。

また何か外部でサービスを使っていてterraformで構成を吸収できるならやりたいな。