TerraformでIaCデビューしてCloudflareをいじろう

## IaCってなんだ？
最近IaCという言葉をよく聞くようになり、それと同時にTerraformというものもよく聞くようになった。

IaCってなんだ？と思い調べてみると[AWSのドキュメント](https://aws.amazon.com/jp/what-is/iac/)を発見。

https://aws.amazon.com/jp/what-is/iac/

> Infrastructure as Code (IaC) とは、手動のプロセスや設定の代わりにコードを使用してコンピューティングインフラストラクチャをプロビジョニングおよびサポートできることをいいます。

とのことらしい。

要するに今までGCPやAWSなどのインフラに関する設定をコンソール画面で行っていたが、それをコードでできるようにしようっていうものらしい。

それにより以下のメリットがあるとのこと。

1. 環境を簡単に複製できる
2. 設定エラーの低減ができる
3. 設定がまとまるので管理がしやすくなる

企業のような大きいサービスをいくつも提供していたり、複数の環境が必要な場合であればIaCによるメリットはかなり大きいかなと思うが、個人の趣味開発程度でIaCが必要になるのかがイマイチイメージがわかない。

ということで実際にやってみて判断してみる。

## IaCにはTerraformらしい
IaCツールとは簡単に調べてみたところ主に以下の2つが出てきた。

1つ目は「Terraform」で、2つ目が「Cloudformation」というものである。

上記の比較については少し前の話ではあるが、Zennに[IaCツールの比較](https://zenn.dev/maniizu3110/articles/65d641519f14a5)という記事があったので読んでみた。

https://zenn.dev/maniizu3110/articles/65d641519f14a5

とりあえずTerraformを選択しておくのが良いとのことで、「Terraform 入門」みたいな感じでGoogle検索をするとたくさん記事が出てきたので、インフラ知識があまりない僕でもできるかもしれないということでTerraformを使ってIaCデビューしてみることにした。

## Terraformを使ってみよう
兎にも角にもまずはインストールをする。

今回はmacOSで使用するので、[Terraform公式ドキュメント](https://developer.hashicorp.com/terraform/tutorials/gcp-get-started/install-cli)を参考にインストールする。

https://developer.hashicorp.com/terraform/tutorials/gcp-get-started/install-cli

インストールは以下の通り。

```zsh
$ brew tap hashicorp/tap
$ brew install hashicorp/tap/terraform

$ terraform -v
Terraform v1.6.6
on darwin_amd64
```

これでインストールが完了した。

インストール自体はめっちゃ簡単でとても良い。

## とりあえずお試しでCloudflareをいじってみる
### Terraformのインストール
今個人的にGCPとかAWSにおいているプロジェクトがないので、お試しということもありとりあえず自身のドメインのDNSをCloudflareで管理しているのでその部分をTerraformで管理できるようにしてみる。

こちらに関しても[Cloudflareのドキュメント](https://developers.cloudflare.com/terraform/)があるのでこれのチュートリアルを参考に進めていく。

https://developers.cloudflare.com/terraform/

ひとまず各種ファイルを準備する。

```zsh
$ touch main.tf provider.tf variables.tf terraform.tfvars

$ tree
.
├── dns.tf
├── provider.tf
├── terraform.tfvars
└── variables.tf
```

このファイル構成が正解なのかは分からないが、ドキュメントを見る限りこんな感じが良いらしい。

それぞれのファイルの役割として、ふんわりと自分なりに理解しているのは以下の通りである。

- provider.tf
  - Terraformでインフラを操作するためには、Providerと呼ばれるインフラとやり取りするためのプラグインを設定する必要があり、その設定を記述する。
- dns.tf
  - 実際にDNSに関する設定を記述するファイル。
- variables.tf
  - このディレクトリにあるTerraformのファイルの中で使用する変数について記述するファイル。
- terraform.tfvars
  - 変数を記述するファイル。

### Providerの設定と変数の設定
まずはCloudflareとやり取りをするためのプラグインのようなものであるProviderを記述する。

```hcl:provider.tf
terraform {
  required_providers {
    cloudflare = {
      source = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
  }
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}
```

これでとりあえずProviderの設定はOK。

次にProviderに記述している `var.cloudflare_api_token` について設定する。

```hcl:variables.tf
variable "cloudflare_api_token" {
  description = "value of cloudflare api token"
  type = string
}

variable "cloudflare_zone_id" {
  description = "value of cloudflare zone id"
  type = string
}
```

これで `cloudflare_api_token` と `cloudflare_zone_id` という変数が使えるようになったので、実際の値を `terraform.tfvars` に記述する。

```hcl:terraform.tfvars
cloudflare_api_token = "YOUR_API_TOKEN"
cloudflare_zone_id = "YOUR_ZONE_ID"
```

このトークンはCloudflareのダッシュボードから発行して、操作したいゾーンに対する編集権限を付与しておく。

### Terraformの初期化
ここまで来たら以下のコマンドでProviderのインストールと初期化を行う。

```zsh
$ terraform init
```

これによりいくつかのファイルやフォルダが生成される。

### 実際にDNSの設定を追加してみる
さぁここまでくればあとはTerraformでいじり放題。

実際に適当なサブドメインを追加するようなものを作ってみよう。

ひとまずCloudflare PagesにデプロイしているAstro製のサイトの独自ドメインとして設定する。

```hcl:dns.tf
resource "cloudflare_record" "tf-test-record" {
  name    = "tf-test-record"
  proxied = false
  ttl     = 1
  type    = "CNAME"
  value   = "portfolio-tf.pages.dev"
  zone_id = var.cloudflare_zone_id
}
```

`cloudflare_record` というのが実際に操作するリソースになり、それに必要なプロパティを渡すことで記述する。

記述が完了したら、このコードをいきなり適用するのではなく、ドライランしてどのような操作が行われるか確認することができる。

`terraform plan` をしてみると、以下のようにドライランした結果が出てくる。

```zsh
$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudflare_record.tf-test-record will be created
  + resource "cloudflare_record" "tf-test-record" {
      + allow_overwrite = false
      + created_on      = (known after apply)
      + hostname        = (known after apply)
      + id              = (known after apply)
      + metadata        = (known after apply)
      + modified_on     = (known after apply)
      + name            = "tf-test-record"
      + proxiable       = (known after apply)
      + proxied         = false
      + ttl             = 1
      + type            = "CNAME"
      + value           = "portfolio-tf.pages.dev"
      + zone_id         = "YOUR_ZONE_ID"
    }

Plan: 1 to add, 0 to change, 0 to destroy.
```

これにより設定した1つの設定が追加されることがわかる。

もしここで意図していない作成や削除、変更があった際には何かがおかしいと適用する前に気づくことができるので、かなり安心だ。

今回は1つの設定を追加することで間違いないので、実際に本番反映する。

本番反映時は、 `terraform apply` をする。

```zsh
$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudflare_record.tf-test-record will be created
  + resource "cloudflare_record" "tf-test-record" {
      + allow_overwrite = false
      + created_on      = (known after apply)
      + hostname        = (known after apply)
      + id              = (known after apply)
      + metadata        = (known after apply)
      + modified_on     = (known after apply)
      + name            = "tf-test-record"
      + proxiable       = (known after apply)
      + proxied         = false
      + ttl             = 1
      + type            = "CNAME"
      + value           = "portfolio-tf.pages.dev"
      + zone_id         = "YOUR_ZONE_ID"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:
```

先程の `terraform plan` と同じように作成される設定について表示されて、Approveするように言われるのでApproveする。

```zsh
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

loudflare_record.tf-test-record: Creating...
cloudflare_record.tf-test-record: Creation complete after 1s [id=6dca4e8d3a044c9883a035ce0fef2d2e]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

これで無事に追加されたようだ。

### Cloudflareのダッシュボードで確認してみる
![](https://raw.githubusercontent.com/Gardenia-710/articles/main/images/3789b872226e79_2024-01-14-11-15-02.png)

実際に追加されているのが確認できた！

本当にコードとコマンドラインだけでDNSの操作をすることができたので、今回追加したものは削除をする。

削除自体は `dns.tf` に記述したコードを削除して `terraform plan` で確認して、 `terraform apply` をすることで削除される。

```zsh
$ terraform apply
cloudflare_record.tf-test-record: Refreshing state... [id=6dca4e8d3a044c9883a035ce0fef2d2e]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # cloudflare_record.tf-test-record will be destroyed
  # (because cloudflare_record.tf-test-record is not in configuration)
  - resource "cloudflare_record" "tf-test-record" {
      - allow_overwrite = false -> null
      - created_on      = "2024-01-14T02:13:19.862411Z" -> null
      - hostname        = "tf-test-record.gardenia-710.com" -> null
      - id              = "6dca4e8d3a044c9883a035ce0fef2d2e" -> null
      - metadata        = {
          - "auto_added"             = "false"
          - "managed_by_apps"        = "false"
          - "managed_by_argo_tunnel" = "false"
          - "source"                 = "primary"
        } -> null
      - modified_on     = "2024-01-14T02:13:19.862411Z" -> null
      - name            = "tf-test-record" -> null
      - proxiable       = true -> null
      - proxied         = false -> null
      - tags            = [] -> null
      - ttl             = 1 -> null
      - type            = "CNAME" -> null
      - value           = "portfolio-tf.pages.dev" -> null
      - zone_id         = "YOUR_ZONE_ID" -> null
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

cloudflare_record.tf-test-record: Destroying... [id=6dca4e8d3a044c9883a035ce0fef2d2e]
cloudflare_record.tf-test-record: Destruction complete after 0s

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
```

これで掃除も完了！

## その他のリソースの操作
[CloudflareのProviderのドキュメント](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs)を読むと今回使用した `cloudflare_record` 以外のリソースに関する情報も記載されている。

https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs

PagesやWorkersに関するものや、R2やD1についてもTerraformで管理できるようなので今度試してみたい。

## まとめ
IaCデビューということでTerraformを触ってみたが、予想以上に簡単で驚いた。

もちろん実際にGCPやAWS等を使用して1つのアプリケーションを運用しようとなったら様々なリソースを連携させる必要があるためもっと複雑にはなるため、今回とはまた違うと思うが、だからこそ一度型を作ってしまえば同じような環境の複製は簡単にできると感じた。

今度クラウドに何かをデプロイする必要があるときはTerraformで管理できるようにしてみたい。

ありがとうございました。