TerraformのバックエンドにCloudflare R2を指定する方法

# はじめに

こんにちは。ydogです。
Terraformの状態管理に **Cloudflare R2** を使用する手順について解説していきます。

# 手順

難しい手順は特になく、以下のCloudflare公式ドキュメントに従って進めていくことで、R2をバックエンドに設定することができます。

https://developers.cloudflare.com/terraform/advanced-topics/remote-backend/

1. R2 バケットを作成
1. R2 アクセス用のAPIキーを作成
1. Terraformコードに定義する
1. `terraform init -reconfigure`を実行

ここでは、特に**3. Terraformコードに定義する**の際の、認証情報の漏洩リスクを抑える方法について詳しく説明します。

# Backend block

Terraformの`backend`ブロックでは、`var`で始まる変数を利用することが出来ません。そのため、先程のリンク通りにコードを記述すると、認証情報をハードコードする必要があります。

```hcl:cloudflare.tf
terraform {
  backend "s3" {
    # 中略
    access_key = "<YOUR_R2_ACCESS_KEY>"
    secret_key = "<YOUR_R2_ACCESS_SECRET>"
    endpoints = { s3 = "https://<YOUR_ACCOUNT_ID>.r2.cloudflarestorage.com" }
  }
```

そのため、外部ファイルからバックエンドの情報を定義できる、`*.tfbackend`ファイルを使用しましょう。
[Backend block configuration overview#partial-configuration](https://developer.hashicorp.com/terraform/language/backend#partial-configuration)

## 実装

上記の`backend`ブロックを構成する場合、`*.tfbackend`ファイルは、以下の通りになります。

```text:config.s3.tfbackend
access_key = "<YOUR_R2_ACCESS_KEY>"
secret_key = "<YOUR_R2_ACCESS_SECRET>"
endpoints  = { s3 = "https://<YOUR_ACCOUNT_ID>.r2.cloudflarestorage.com" }
```

その後、`cloudflare.tf`ファイルから認証情報の引数を省略します。

```hcl:cloudflare.tf
terraform {
  backend "s3" {
    bucket = "<YOUR_BUCKET_NAME>"
    key    = "/some/key/terraform.tfstate"
    region                      = "auto"
    skip_credentials_validation = true
    skip_metadata_api_check     = true
    skip_region_validation      = true
    skip_requesting_account_id  = true
    skip_s3_checksum            = true
    use_path_style              = true
  }
  required_providers {
    cloudflare = {
      source = "cloudflare/cloudflare"
      version = "~> 4"
    }
  }
}
```

これで、`.gitignore`に`*.tfbackend`を追加すれば、認証情報をGitリポジトリで管理する必要が無くなり、セキュリティを高くすることができます。
また、`example.*.tfbackend`ファイルを作成し、Git管理することで、必要な認証情報を記録しておくのを推奨します。

# 感想

Cloudflare R2をバックエンドにしようと思ったきっかけである、[Future Tech Blog](https://future-architect.github.io/articles/20231016a/)では、`AWS`で始まる環境変数に、Cloudflareの認証情報を記載する方法が紹介されていたため、何となく違和感を感じていました。
しかし、そこから私なりに考え、この方法に辿り着きました。今回この記事を書くにあたって、特にTerraform、Cloudflare双方の公式ドキュメントを読むこととなり、今まで以上に知識が深まったのを実感しています。

このように、技術ブログのような二次情報で紹介されていない手法でも、一次情報を自分で集めて目的を達成できたことを忘れず、今後も学習を続けていきたいです。

# おまけ
ここではCloudflare R2をバックエンドに指定する方法を紹介しましたが、このままでは`state`ファイルをロックできません。
そこで、`Amazon DynamoDB`を使用しても良いのですが、Terraformのバージョン`1.10`以降から追加される、`use_lockfile`引数をご紹介します。

詳細はこちらの[Zenn記事](https://zenn.dev/terraform_jp/articles/terraform-s3-state-lock)で解説されていますが、`use_lockfile`を`true`にすることによって、DynamoDBが不要になります。

最後にその設定を追加したコードを紹介して終了します。

```hcl
terraform {
  backend "s3" {
    bucket = "<YOUR_BUCKET_NAME>"
    key    = "/some/key/terraform.tfstate"
    region                      = "auto"
    skip_credentials_validation = true
    skip_metadata_api_check     = true
    skip_region_validation      = true
    skip_requesting_account_id  = true
    skip_s3_checksum            = true
    use_path_style              = true
    use_lockfile                = true # State Lockを有効化
  }

  required_version = "~> 1.10.0" # Terraformバージョンを1.10以上に指定
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4"
    }
  }
}
```