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" {
│
以下のように有効化する。
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に紐付けする、という構築の仕方を行う。
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,
]
}
Cloud Functionsで未認証の呼び出しを許可するためには、全ユーザーに対して呼び出しを許可するbindingを定義する必要がある。
Cloud Functionsへのデプロイがたまにタイムアウトで失敗する。日を改めると成功したりする
プッシュ通知は、Xcodeによる自動登録か、手動での登録により、DeveloperアカウントでIdentifierが登録されている必要がある。
プッシュ通知が登録できているかどうかは、CloudKit Console > Push Notifications から試せる。
新しく作成したインフラにアプリを繋げる際に必要な手順
-
.firebaserc
のプロジェクトIDを変更 - iOSの Bundle ID を変更
- Androidの applicationId を変更
- FlutterへのFirebaseアプリの構成ファイルを組みなおし
terraform plan の結果をPRにコメントする際には、以下が便利そう。
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せずに有効化しようとしても、特にエラーを吐かない。
Terraform管理により新たに発生するリソースはimportする必要がない。
まずはbackendをlocalに指定してapplyまで済ませ、tfstateを生成し、その後backendをGCPなどに指定するのがよさそう。