Firebase 治理:Terraform 管結構、CLI/CI 管部署、config 改 secret 注入
Context
本專案的 Firebase 與 GCP 是兩個獨立 project:GCP project 為 yibi-mvp(承載 Cloud Run /
Cloud SQL / GCS / Terraform state),Firebase project 為 myyibi(project number 238382742724,
承載 App Distribution 與規劃中的 FCM)。
目前 Firebase 的治理處於半手動、無紀錄、單一環境未分離狀態,缺一份決策文件把「什麼歸誰管」攤開:
| 面向 | 現狀 | 問題 |
|---|---|---|
| Terraform 覆蓋 | 僅 1 條 roles/firebase.admin IAM binding(infra/terraform/modules/iam/main.tf:52,綁在 myyibi) | Firebase project 啟用、App 註冊全靠 Console 手動,無版本紀錄、新人難重建 |
| Provider | infra/terraform/environments/*/versions.tf 只宣告 google ~> 7.35,無 google-beta | 多數 google_firebase_* resource 需要 google-beta,現在連 plan 都跑不起來 |
| Android config | mobile/android/app/google-services.json 已 gitignore(mobile/.gitignore:67-70,未進 git)+ CI 用 base64 secret GOOGLE_SERVICES_JSON 解碼注入(.github/workflows/cd-mobile.yml:95) | Android 其實已正確落地 L4(gitignore + secret 注入);無雙軌問題 |
| iOS config | mobile/ios/Runner/GoogleService-Info.plist 已 gitignore(mobile/.gitignore:70)但無對應 secret / 注入步驟(docs/runbook/deployment/05-firebase-verify.sh:53 期望其存在) | iOS 路徑不完整:缺 secret + CI 注入,與 Android 不對稱 |
| 環境隔離 | 單一 Firebase project myyibi 通吃所有環境(cd-mobile.yml:143 --project myyibi;beta build 指向 staging backend 卻共用同一 Firebase project) | 未對齊 GCP 端早已分離的 infra/terraform/environments/{staging,prod};Analytics / Crashlytics / FCM sender / API key 全環境混用 |
| App Distribution | cd-mobile.yml 用 firebase CLI firebase appdistribution:distribute(非 fastlane),target beta-families tester group | 可接受;Terraform 本就管不到 release 動作 |
| FCM | 推通知合約已定(docs/features/010-mobile-app/contracts/push-notification-events.yaml),後端發送實作未落地;mobile pubspec.yaml 無任何 firebase_* 依賴 | 規劃中,roadmap 需釐清何時、用什麼方式納管 |
目標:讓 Firebase 結構性設定可版本控制(config-as-code)、把 Web Console 的人為操作降到最低、 新人能從 repo + CI 重建環境。本 ADR 不變更任何程式碼,只確立治理分層原則,實作待裁決後另開 change/PR。
範圍備註:Firebase Auth(identity broker)的啟用與 provider 設計另由 ADR-0016(PR #573, spectra change 0032)獨立裁決。本 ADR 只定義 Auth 一旦啟用後「歸哪一層治理」(L1 Terraform), 不重複決定是否採用 Firebase Auth。
Decision
本節為提案(proposed),非既定事實。 推薦方案為 B;最終方向待 howie 於 review 裁決後轉
accepted。
結論(提案):採業界主流的 Firebase 三層分工治理。核心原則是 Firebase CLI 與 Terraform 互補、不重疊——「資源的存在性」歸 Terraform(宣告式、有 state),「發佈/部署動作」歸 CLI/CI (命令式、無 state)。Firebase CLI 本身不是 Terraform 的管理對象,而是被 CI script 呼叫的工具。
| 層 | 工具 | 性質 | 管什麼 | 本專案落地 |
|---|---|---|---|---|
| L1 基礎設施 | Terraform(google-beta) | 宣告式 / 有 state | Firebase project 啟用、App 註冊(Android/iOS)、Auth providers(見 ADR-0016)、Firestore/Storage、Security Rules、IAM | 引入 google-beta provider、provider 指向 myyibi、terraform import 現有 Android/iOS app |
| L2 部署 | Firebase CLI(CI script 呼叫) | 命令式 / 無 state | Cloud Functions、Security Rules deploy、Firestore indexes | 維持由 CI 呼叫,不被 Terraform 管理 |
| L3 行動發佈 | firebase CLI / fastlane(CI) | 命令式 / 無 state | App Distribution release、tester group | 維持現有 cd-mobile.yml(firebase CLI) |
| L4 config 檔 | GCP Secret Manager + CI 注入 | — | google-services.json / GoogleService-Info.plist | Android 已 gitignore + secret 注入;已裁決改存 GCP Secret Manager(per-env,取代散落的 GitHub Secret);補齊 iOS plist 對應 secret |
直接回答常見疑問:
- 「Firebase CLI 是用 Terraform 還是 script 管理?」 — 用 script(CI)。CLI 是執行動作的命令式工具, 與宣告式的 Terraform 互補;兩者不互相管理。
- Terraform 管不到的項目(必須留在 CLI/CI 或 Console):App Distribution release (Terraform open feature request #16604)、 Remote Config(#10886)、 Firebase 服務條款接受(僅 Console,一次性)、Analytics 設定(多數僅 Console)。
Per-environment 原則(提案)
Firebase 應對齊 GCP 端早已存在的多環境分離(infra/terraform/environments/{staging,prod})。
Firebase 最佳實踐是一環境一 project(隔離 Analytics / Crashlytics / FCM sender / API key,
避免 dev/test 流量污染 prod 數據、避免共用憑證):
- L1 以
for_eachover environment 在 Terraform 管多個 Firebase project(如yibi-staging、yibi-prod), 比照既有environments/模式。 - 每環境各一份
google-services.json/GoogleService-Info.plist,由 L4 的 secret 機制按環境注入。 - 是否新增獨立
dev環境、或保留單一 project + build flavor 區隔,列為 Open Question 待裁決。
config 檔的性質與保管(已裁決:GCP Secret Manager)
重點澄清:
google-services.json/GoogleService-Info.plist是環境設定、非 server secret。 其中的api_key是客戶端識別碼,會被打包進 APK / IPA、可被反編譯取出。Firebase 的安全不靠隱藏此檔, 而靠 Security Rules + App Check + GCP Console 的 API key 限制(綁 package name / SHA、限定 scope)。 gitignore 它是衛生(避免散落、環境混淆),而非保密。
真正必須嚴格保管的 secret(皆已正確處理):
| 真正的 secret | 現況保管 |
|---|---|
| Deployer SA / WIF | GitHub Secret(WIF,無下載式金鑰) |
| Android keystore + 密碼 | GitHub Secret ANDROID_KEYSTORE_BASE64 等 |
| Terraform origin CA cert/key | GCP Secret Manager |
裁決(howie & keith):google-services.json 與 GoogleService-Info.plist 一律存 GCP Secret Manager
(貼合既有 infra——origin CA cert 已在此、Terraform 可讀、IAM 可稽核),CI 由 Secret Manager 取出注入,
取代目前散落的 GitHub Secret GOOGLE_SERVICES_JSON;per-env 各一份(如 firebase-google-services-staging / -prod)。
不採 1Password——它不是真 secret,且 CI 端不如 Secret Manager 原生(1Password 至多作為開發者本機取用的便利來源)。
Local 開發 Firebase 策略(已裁決:不新增 dev project)
裁決(howie):不為 local 開發新增獨立 dev Firebase project;本地需求用 emulator + stub +(必要時)直連 staging 覆蓋。各服務分流如下:
| 服務 | Local 策略 | 說明 |
|---|---|---|
| App Distribution | 不涉及 | 本地 flutter run 直接跑,不經過 App Distribution |
| Firebase Auth(ADR-0016) | Emulator 優先 | firebase emulators:start --only auth(localhost:9099),mobile SDK 與 backend Admin SDK 皆指向 emulator,離線、免真 project、可造假用戶。整合測試時才直連 staging |
| Firebase Auth — bypass | 可選 | 因 ADR-0016 為 token-exchange 架構(Firebase token → 自家 JWT),backend 可開 dev-only 登入路徑直接發 YiBi JWT,mobile 以 --dart-define 切 fake-login,完全不碰 Firebase |
| FCM 推播 | 本地 stub | Emulator Suite 不模擬真實推播投遞;本地把發送 stub 成 log,需驗實機收訊時才連 staging |
要點:Firebase Emulator Suite 支援 Auth/Firestore/Functions/Storage/RTDB,但不支援 FCM 推播投遞、App Distribution、Analytics、Crashlytics。因此 local 策略是「Auth 走 emulator、FCM 走 stub、App Distribution 無關」,不需要第三個 Firebase 環境。
Alternatives Considered
- 方案 A:維持現狀(最小 Terraform + Console 手動) — 考慮過,因為
零遷移成本;但 App 註冊與 Firebase project 啟用全無紀錄、持續依賴 Console、新人重建困難、且環境未分離(單一myyibi通吃), 不符「減少 Console、config-as-code、環境隔離」目標,沒有選。 - 方案 B:三層分工 + 多環境(推薦) — 結構性資源 IaC、Console 操作降到僅剩一次性/不可管項目、release 維持
輕量 CLI 不過度工程化、Firebase project 按環境分離;代價是引入並維護
google-betaprovider、需terraform import既有 app、per-env project 的初始設定成本。 - 方案 C:全面 IaC + fastlane 化 — 考慮過,在方案 B 之上把 App Distribution 從 firebase CLI 換成
fastlane plugin(
firebase_app_distribution)並 code 化 tester group;但 release 本質是 imperative 動作、code 化收益有限,且 fastlane 引入/維護成本高(現行 CLI 流程已可運作),沒有選為預設。
Consequences
好:
- Firebase 結構性設定(project、app 註冊、Auth provider、Rules)可進版本控制與 PR review。
- Web Console 的人為操作降到僅剩一次性(條款接受)與 Terraform 不可管項目(Remote Config / Analytics)。
- 新人可從 Terraform + CI 重建 Firebase 環境,不需口耳相傳的 Console 步驟。
- config 檔保管收斂為單一真相(GCP Secret Manager,per-env);環境間 Analytics / FCM / API key 互不污染。
壞 / 代償:
- 需在
versions.tf引入並長期維護google-betaprovider,跨myyibiproject 的 provider 設定增加複雜度。 - 既有 Android/iOS app 必須
terraform import接管,否則apply會試圖重建既有資源。 - 改用 per-env Firebase project 需一次性建立 staging/prod project 並逐一註冊 app、產生對應 config。
- config 已 gitignore(非新增限制),但改存 Secret Manager 後本地開發需先從 Secret Manager 拉取(或
flutterfire configure)才能 build。
中性:
- App Distribution release 與 Cloud Functions deploy 仍是 imperative 的 CI 動作,
terraform plan永遠不會偵測其漂移——這是預期的分工結果,非缺陷。
Open Questions / Roadmap(全面治理範圍)
- FCM:後端推通知實作何時補齊;FCM 多數設定靠 Admin SDK runtime(非 IaC),需釐清哪些(若有)值得進 Terraform。
- Firebase Auth:是否啟用由 ADR-0016 / change 0032 裁決;若採用,其 provider 設定納入本 ADR 的 L1(Terraform
google_identity_platform_*)。 - Firestore / Storage:目前未使用;若啟用則納入 L1,Security Rules 走 L2(firebase CLI)。
- fastlane 升級:是否從方案 B 進一步走方案 C(App Distribution 改 fastlane),待行動 CI 成熟度評估。
- config 保管:✅ 已裁決存 GCP Secret Manager(見 Decision)。未來優化(非阻塞):app 經 Terraform 註冊後改用
flutterfire configure/ data sourcegoogle_firebase_android_app_config重新生成,達零保管。 - iOS plist 缺失:補齊
GoogleService-Info.plist、建立對應 Secret Manager secret + CI 注入(與 Android 對稱)。 - 環境切分粒度:✅ 已裁決——per-env Firebase project 僅
staging+prod,不新增獨立devproject;local 開發以 Auth emulator + FCM stub +(必要時)直連 staging 覆蓋(見 Decision「Local 開發 Firebase 策略」)。 - 一次性 Console 項目記錄:Firebase 條款接受、Remote Config 等不可 IaC 的步驟,應在
docs/runbook/deployment/明確記錄,避免被誤認為可自動化。 - Google Play 上傳(範圍外,另立 ADR):本 ADR 範圍是 Firebase project
myyibi,不涵蓋 Google Play(Play Console 是與 Firebase 獨立的系統)。AAB 上傳 Google Play 的 keyless WIF 決策另見 ADR-0020(accepted);其與本 ADR 正交、可獨立執行,唯一交集是共用既有 WIF/deployer SA 機制與cd-mobile.yml。
Links
- Firebase 官方 Terraform 入門:https://firebase.google.com/docs/projects/terraform/get-started
- App Distribution + fastlane:https://firebase.google.com/docs/app-distribution/android/distribute-fastlane
- Terraform App Distribution feature request:https://github.com/hashicorp/terraform-provider-google/issues/16604
- Terraform Remote Config feature request:https://github.com/hashicorp/terraform-provider-google/issues/10886
- 相關 ADR:ADR-0016(Firebase Auth identity broker,PR #573)、ADR-0020(Google Play AAB keyless WIF 上傳,accepted)