Open18

TerraformでFirebaseを管理してみる

ころむにーころむにー

すでに構築済みのFirebaseプロジェクトからtfstateを生成し、Terraform管理できるようにする。

前提として、Terraformの定義はある程度構築済み。

PROJECT_ID="colomney-my-pet-melody-dev"
terraform import google_project.default "$PROJECT_ID"
terraform import google_project_service.default "$PROJECT_ID"
terraform import google_firebase_project.default "$PROJECT_ID"
terraform import google_project_service.default "${PROJECT_ID}/cloudbilling.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/cloudresourcemanager.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/cloudtasks.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/firebase.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/firebaserules.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/firebasestorage.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/firestore.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/identitytoolkit.googleapis.com"
terraform import google_project_service.default "${PROJECT_ID}/serviceusage.googleapis.com"
import google_firebase_apple_app.default "projects/${PROJECT_ID}/iosApps/1:409733918350:ios:e1fc95560330d3dbfea3df"
terraform import google_firebase_android_app.default "projects/${PROJECT_ID}/androidApps/1:409733918350:android:6e0ef3847f6f80a7fea3df"
terraform import google_identity_platform_config.auth "$PROJECT_ID"

# 以下のコマンドは、うまくいっていない
terraform import google_identity_platform_project_default_config.auth "$PROJECT_ID"
terraform import google_firestore_database.default "projects/${PROJECT_ID}/databases/(default)"
terraform import google_firebaserules_ruleset.firestore "projects/${PROJECT_ID}/rulesets/886253d9-bdb6-434b-aca3-29e55d1debb0"
terraform import google_firebaserules_release.firestore "projects/${PROJECT_ID}/releases/firebase.storage/${PROJECT_ID}.appspot.com"
terraform import google_app_engine_application.default "$PROJECT_ID"

terraform import google_storage_bucket.default "projects/${PROJECT_ID}.appspot.com"

terraform import google_firebase_storage_bucket.default-bucket "projects/${PROJECT_ID}/buckets/${PROJECT_ID}.appspot.com"
terraform import google_firebaserules_ruleset.storage "projects/${PROJECT_ID}/rulesets/886253d9-bdb6-434b-aca3-29e55d1debb0"
terraform import google_firebaserules_release.default-bucket "projects/${PROJECT_ID}/releases/firebase.storage/${PROJECT_ID}.appspot.com"
terraform import google_cloud_tasks_queue.advanced_configuration "projects/${PROJECT_ID}/locations/asia-east1/queues/${PROJECT_ID}-service"
LOCALE="asia-east1"
terraform import google_cloudfunctions_function.detect "projects/${PROJECT_ID}/locations/${LOCALE}/functions/detect"
terraform import google_cloudfunctions_function.submit "projects/${PROJECT_ID}/locations/${LOCALE}/functions/submit"
terraform import google_cloudfunctions_function.piece "projects/${PROJECT_ID}/locations/${LOCALE}/functions/piece"
terraform import google_service_account.firebase_admin "firebase-adminsdk-zlkov@${PROJECT_ID}.iam.gserviceaccount.com"
terraform import google_service_account.cloud_tasks "cloud-tasks@${PROJECT_ID}.iam.gserviceaccount.com"
ころむにーころむにー

以下はTerraformに対応していないため、自前でセットアップする必要がある。

  • Firebase Cloud Messaging
  • Firebase Remote Config
  • Firebase Crashlytics
  • Firebase Analytics
  • GCPにおけるサービスアカウントのキー発行
ころむにーころむにー

手動で作成済みのFirebaseプロジェクト&GCPプロジェクトを、terraform import によりインポートしtfstateを手元に生成して、terraform planしてみる。

かなり差分が出ているものの、差分を*.tfファイルに適用してもそのキーは指定できないとエラーが出たりする。
Firebaseのプロバイダーがベータ版だからかなぁ

ころむにーころむにー

初回のterraform applyを行う際、以下に気を付ける。

  • プロジェクトIDが過去に作成し最近削除したものと重複していないか
  • 請求先アカウントの紐付け最大数を超えていないか
ころむにーころむにー

google_storage_bucketを末尾が .appspot.com で終わる名前で作成しようとすると以下のエラーで失敗する。-defaultなどに変更しておく。

│ Error: googleapi: Error 403: You must verify site or domain ownership at https://search.google.com/search-console/welcome?new_url_prefix=colomney-my-pet-melody-tf001.appspot.com, forbidden
│ 
│   with google_storage_bucket.default,
│   on storage.tf line 11, in resource "google_storage_bucket" "default":11: resource "google_storage_bucket" "default" {
ころむにーころむにー

GCPプロジェクトで必要なAPIが有効化されていない場合は以下のようなエラーが出る。

│ Error: Error waiting for Creating CloudFunctions Function: Error code 7, message: Build failed: Cloud Build API has not been used in project colomney-my-pet-melody-tf001 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudbuild.googleapis.com/overview?project=colomney-my-pet-melody-tf001 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
│ 
│   with google_cloudfunctions_function.detect,
│   on function.tf line 23, in resource "google_cloudfunctions_function" "detect":23: resource "google_cloudfunctions_function" "detect" {

以下のように有効化する。

main.tf
resource "google_project_service" "default" {
  provider = google-beta.no_user_project_override
  project  = google_project.default.project_id
  for_each = toset([
    "cloudbilling.googleapis.com",
    "cloudbuild.googleapis.com",
+    "cloudfunctions.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "cloudtasks.googleapis.com",
    "firebase.googleapis.com",
    "firebaserules.googleapis.com",
    "firebasestorage.googleapis.com",
    "firestore.googleapis.com",
    "identitytoolkit.googleapis.com",
    "serviceusage.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}
ころむにーころむにー

Firebaseのプロジェクトを作ると以下は自動で生成されるため、Terraformで定義する必要はない。

  • Firebaseの管理者サービスアカウント
ころむにーころむにー

以下は手動で実施する必要がある。

  • Firebaseの管理者サービスアカウントのキーを発行し、ファンクションに組み込む
  • その他サービスアカウントキーを発行し、ファンクションに組み込む
  • Firebaseで以下を有効にする
    • Authにおける、匿名認証以外のもの
    • Cloud Messaging
      • APNs認証キーの登録
    • Crashlytics
    • Analytics
    • Remote Config
  • Cloud Storageのライフサイクル
ころむにーころむにー

Firebase Storageは、GCPでApp Engineを有効化してデフォルトでつくられたCloud Storageのバケットを用いて、Firebaseに紐付けする、という構築の仕方を行う。

storage.tf
resource "google_app_engine_application" "default" {
  provider    = google-beta
  project     = google_project.default.project_id
  location_id = var.google_project_location

  depends_on = [
    google_firestore_database.default
  ]
}

resource "google_firebase_storage_bucket" "default-bucket" {
  provider  = google-beta
  project   = google_project.default.project_id
  bucket_id = google_app_engine_application.default.default_bucket

  depends_on = [
    google_app_engine_application.default,
  ]
}

https://zenn.dev/cloud_ace/articles/b791cce386d523

ころむにーころむにー

Cloud Functionsへのデプロイがたまにタイムアウトで失敗する。日を改めると成功したりする

ころむにーころむにー

プッシュ通知は、Xcodeによる自動登録か、手動での登録により、DeveloperアカウントでIdentifierが登録されている必要がある。

プッシュ通知が登録できているかどうかは、CloudKit Console > Push Notifications から試せる。

https://developer.apple.com/jp/icloud/cloudkit/

ころむにーころむにー

新しく作成したインフラにアプリを繋げる際に必要な手順

  • .firebaserc のプロジェクトIDを変更
  • iOSの Bundle ID を変更
  • Androidの applicationId を変更
  • FlutterへのFirebaseアプリの構成ファイルを組みなおし
ころむにーころむにー

google_firebaserules_rulesetのimportに利用するIDがわからないので、google_firebaserules_releaseをimportすることで間接的にIDを知ることができる。

-/+ resource "google_firebaserules_release" "firestore" {
      ~ create_time  = "2023-06-11T08:37:49.526314Z" -> (known after apply)
      ~ disabled     = false -> (known after apply)
      ~ id           = "projects/colomney-my-pet-melody/releases/cloud.firestore" -> (known after apply)
        name         = "cloud.firestore"
        project      = "colomney-my-pet-melody"
      ~ ruleset_name = "projects/colomney-my-pet-melody/rulesets/481a204f-d6b1-497d-82c2-2dd0cf4f5e69" # forces replacement -> (known after apply) # forces replacement
      ~ update_time  = "2024-06-08T13:56:42.972842Z" -> (known after apply)
    }

  # google_firebaserules_release.storage must be replaced
  # (imported from "projects/colomney-my-pet-melody/releases/firebase.storage/colomney-my-pet-melody.appspot.com")
  # Warning: this will destroy the imported resource
-/+ resource "google_firebaserules_release" "storage" {
      ~ create_time  = "2023-06-11T08:38:37.392268Z" -> (known after apply)
      ~ disabled     = false -> (known after apply)
      ~ id           = "projects/colomney-my-pet-melody/releases/firebase.storage/colomney-my-pet-melody.appspot.com" -> (known after apply)
        name         = "firebase.storage/colomney-my-pet-melody.appspot.com"
        project      = "colomney-my-pet-melody"
      ~ ruleset_name = "projects/colomney-my-pet-melody/rulesets/d3e60290-77a1-4882-9f64-4b25e83729eb" # forces replacement -> (known after apply) # forces replacement
      ~ update_time  = "2024-01-08T11:54:49.108147Z" -> (known after apply)
    }
ころむにーころむにー

google_project_service に関しては、すでに有効になっている状態で、Terraformからはimportせずに有効化しようとしても、特にエラーを吐かない。

ころむにーころむにー

まずはbackendをlocalに指定してapplyまで済ませ、tfstateを生成し、その後backendをGCPなどに指定するのがよさそう。