使用入门:将 Terraform 与 Firebase 搭配使用

Firebase 现在提供 Terraform 支持。如果您所在的团队想要用一套标准化的流程自动创建预配了特定资源并启用了相关服务的 Firebase 项目,那么将 Terraform 与 Firebase 搭配使用会非常有用。

若要将 Terraform 与 Firebase 搭配使用,需完成下面的基本工作流程:

  • 创建和自定义 Terraform 配置文件(一个 .tf 文件),用于指定您要预配的基础架构(即要预配的资源和要启用的服务)。

  • 使用与 Terraform 关联的 gcloud CLI 命令来预配 .tf 文件中指定的基础架构。

您可以使用 Terraform 和 Firebase 执行哪些操作?

本指南中的通用工作流示例会创建一个新的 Firebase 项目来提供 Android 应用。不过,您可以使用 Terraform 执行更多操作,例如:

  • 使用 Terraform 删除和修改现有基础架构。

  • 使用 Terraform 管理产品专用配置和任务,如:

    • 启用 Firebase Authentication 登录提供方。
    • 创建 Cloud Storage 存储桶或数据库实例并为其部署 Firebase Security Rules

您可以使用标准 Terraform 配置文件和命令来完成所有这些任务。另外,为了帮助您完成这些任务,我们针对几种常见的使用场景提供了 Terraform 示例配置文件



将 Terraform 与 Firebase 搭配使用的通用工作流

前提条件

本指南介绍如何将 Terraform 与 Firebase 搭配使用,因此假定您已基本掌握 Terraform 的使用。在开始此工作流之前,请确保您已满足以下前提条件。

  • 安装 Terraform 并通过官方教程熟悉 Terraform 的使用。

  • 安装 Google Cloud CLI (gcloud CLI)。使用用户账号服务账号登录。

    • 如果使用用户账号,您必须已接受 Firebase 服务条款 (TOS)。如果您可以在 Firebase 控制台中查看 Firebase 项目,则表示您已接受 Firebase 服务条款 (ToS)
    • 为了让 Terraform 能够执行某些操作(例如,创建项目),还必须满足以下条件:
      • 用户账号或服务账号必须拥有允许执行这些操作的 IAM 权限。
      • 如果用户账号或服务账号属于某个 Google Cloud 组织,那么该组织的政策必须允许该账号执行这些操作。


第 1 步:创建和自定义 Terraform 配置文件

Terraform 配置文件需要有两个主要构成部分(详见下文):

设置您的 provider

无论涉及哪些 Firebase 产品或服务,都需要进行 provider 设置。

  1. 在您的本地目录中创建一个 Terraform 配置文件(如 main.tf 文件)。

    在本指南中,您将使用此配置文件来指定 provider 设置以及您希望 Terraform 创建的所有基础架构。但请注意,您可以自行选择添加该提供方设置的方法。

    您可以通过以下方法将 provider 设置添加到 Terraform 配置的其余部分:

    • 方法 1:在同一个 Terraform .tf 配置文件的开头部分添加该设置(如本指南所示)。

      • 如果您刚开始使用 Terraform,或者只是想尝试将 Terraform 与 Firebase 搭配使用的效果,则可以使用此方法。
    • 方法 2:在一个单独的 .tf 文件(如 provider.tf 文件)中添加该设置,该文件与您在其中指定要创建的基础架构的 .tf 文件(如 main.tf 文件)彼此区分。

      • 如果您所在的团队规模较大且需要实施标准化设置,则可以使用此方法。
      • 在这种情况下,运行 Terraform 命令时,provider.tf 文件和 main.tf 文件必须位于同一目录中。

  2. main.tf 文件的开头部分添加以下 provider 设置。

    必须使用 google-beta 提供方,因为将 Firebase 与 Terraform 搭配使用的支持尚处于 Beta 版阶段。若要在生产环境中实践,则需格外谨慎。

    # Terraform configuration to set up providers by version.
    terraform {
      required_providers {
        google-beta = {
          source  = "hashicorp/google-beta"
          version = "~> 5.0"
        }
      }
    }
    
    # Configures the provider to use the resource block's specified project for quota checks.
    provider "google-beta" {
      user_project_override = true
    }
    
    # Configures the provider to not use the resource block's specified project for quota checks.
    # This provider should only be used during project creation and initializing services.
    provider "google-beta" {
      alias = "no_user_project_override"
      user_project_override = false
    }

    详细了解将 Terraform 与 Firebase 搭配使用时涉及的不同类型的项目相关特性(包括本指南中称为“配额检查项目”的内容)。

  3. 继续下一部分内容,完成配置文件并指定要创建的基础架构。

使用 resource 块指定要创建的基础架构

在 Terraform 配置文件(在本指南中,即为 main.tf 文件)中,需指定您想要 Terraform 创建的所有基础架构(即要预配的所有资源和要启用的所有服务)。本指南中提供了支持 Terraform 的所有 Firebase 资源的完整列表。

  1. 打开 main.tf 文件。

  2. provider 设置下,添加以下 resource 块配置。

    此基本示例会创建一个新的 Firebase 项目,然后在该项目中创建一个 Firebase Android 应用。

    # Terraform configuration to set up providers by version.
    ...
    
    # Configures the provider to use the resource block's specified project for quota checks.
    ...
    
    # Configures the provider to not use the resource block's specified project for quota checks.
    ...
    
    # Creates a new Google Cloud project.
    resource "google_project" "default" {
      provider   = google-beta.no_user_project_override
    
      name       = "Project Display Name"
      project_id = "project-id-for-new-project"
      # Required for any service that requires the Blaze pricing plan
      # (like Firebase Authentication with GCIP)
      billing_account = "000000-000000-000000"
    
      # Required for the project to display in any list of Firebase projects.
      labels = {
        "firebase" = "enabled"
      }
    }
    
    # Enables required APIs.
    resource "google_project_service" "default" {
      provider = google-beta.no_user_project_override
      project  = google_project.default.project_id
      for_each = toset([
        "cloudbilling.googleapis.com",
        "cloudresourcemanager.googleapis.com",
        "firebase.googleapis.com",
        # Enabling the ServiceUsage API allows the new project to be quota checked from now on.
        "serviceusage.googleapis.com",
      ])
      service = each.key
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    # Enables Firebase services for the new project created above.
    resource "google_firebase_project" "default" {
      provider = google-beta
      project  = google_project.default.project_id
    
      # Waits for the required APIs to be enabled.
      depends_on = [
        google_project_service.default
      ]
    }
    
    # Creates a Firebase Android App in the new project created above.
    resource "google_firebase_android_app" "default" {
      provider = google-beta
    
      project      = google_project.default.project_id
      display_name = "My Awesome Android app"
      package_name = "awesome.package.name"
    
      # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
      depends_on = [
        google_firebase_project.default,
      ]
    }

如果您还不熟悉将项目和应用作为资源进行部署的基础架构,请参阅以下文档:

# Terraform configuration to set up providers by version.
...

# Configures the provider to use the resource block's specified project for quota checks.
...

# Configures the provider to not use the resource block's specified project for quota checks.
...

# Creates a new Google Cloud project.
resource "google_project" "default" {
  # Use the provider that enables the setup of quota checks for a new project
  provider   = google-beta.no_user_project_override

  name            = "Project Display Name"        // learn more about the project name
  project_id      = "project-id-for-new-project"  // learn more about the project ID
  # Required for any service that requires the Blaze pricing plan
  # (like Firebase Authentication with GCIP)
  billing_account = "000000-000000-000000"

  # Required for the project to display in any list of Firebase projects.
  labels = {
    "firebase" = "enabled"  // learn more about the Firebase-enabled label
  }
}

# Enables required APIs.
resource "google_project_service" "default" {
  # Use the provider without quota checks for enabling APIS
  provider = google-beta.no_user_project_override
  project  = google_project.default.project_id
  for_each = toset([
    "cloudbilling.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "firebase.googleapis.com",
    # Enabling the ServiceUsage API allows the new project to be quota checked from now on.
    "serviceusage.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}

# Enables Firebase services for the new project created above.
# This action essentially "creates a Firebase project" and allows the project to use
# Firebase services (like Firebase Authentication) and
# Firebase tooling (like the Firebase console).
# Learn more about the relationship between Firebase projects and Google Cloud.
resource "google_firebase_project" "default" {
  # Use the provider that performs quota checks from now on
  provider = google-beta

  project  = google_project.default.project_id

  # Waits for the required APIs to be enabled.
  depends_on = [
    google_project_service.default
  ]
}

# Creates a Firebase Android App in the new project created above.
# Learn more about the relationship between Firebase Apps and Firebase projects.
resource "google_firebase_android_app" "default" {
  provider = google-beta

  project      = google_project.default.project_id
  display_name = "My Awesome Android app"  # learn more about an app's display name
  package_name = "awesome.package.name"    # learn more about an app's package name

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.default,
  ]
}


第 2 步:运行 Terraform 命令以创建指定的基础架构

如需预配 main.tf 文件中指定的资源并启用指定的服务,请从 main.tf 文件所在的目录中运行以下命令。如需详细了解这些命令,请参阅 Terraform 文档

  1. 如果这是您首次在该目录中运行 Terraform 命令,您需要初始化配置目录并安装 Google Terraform 提供方。为此,请运行以下命令:

    terraform init
  2. 运行以下命令,创建 main.tf 文件中指定的基础架构:

    terraform apply
  3. 通过以下方法,确认所有内容均已按预期预配或启用:

    • 方法 1:运行以下命令,查看在终端中显示的配置输出:

      terraform show
    • 方法 2:在 Firebase 控制台中查看您的 Firebase 项目。



支持 Terraform 的 Firebase 资源

以下 Firebase 和 Google 资源现在支持 Terraform。我们还在不断添加更多提供该支持的资源!因此,即便您现在没有看到自己想要通过 Terraform 管理的资源,您可以隔一段时间再来查看,也可以在 GitHub 代码库中提交问题,申请加速对该资源的支持。


Firebase 项目和应用管理

  • google_firebase_project - 在现有 Google Cloud 项目中启用 Firebase 服务

  • Firebase 应用


Firebase Authentication

暂不支持以下功能:

  • 通过 Terraform 配置多重身份验证 (MFA)

Firebase Realtime Database

暂不支持以下功能:

  • 通过 Terraform 部署 Firebase Realtime Database Security Rules(了解如何使用其他工具,包括通过编程方式,来部署这些Rules

Cloud Firestore

  • google_firestore_database — 创建一个 Cloud Firestore 实例

  • google_firestore_index - 为 Cloud Firestore 实现高效查询

  • google_firestore_document - 使用集合中的特定文档初始化 Cloud Firestore 实例

    重要提示:请勿在此种子文档中使用真实的最终用户数据或生产数据。


Cloud Storage for Firebase

  • google_firebase_storage_bucket - 将一个现有的 Cloud Storage 存储桶设置为可供 Firebase SDK、身份验证机制和 Firebase Security Rules 访问

  • google_storage_bucket_object - 向 Cloud Storage 存储桶添加对象

    重要提示:请勿在此文件中使用真实的最终用户数据或生产数据。


Firebase Security Rules(适用于 Cloud FirestoreCloud Storage

请注意,Firebase Realtime DatabaseFirebase Security Rules使用的是不同的预配系统。

  • google_firebaserules_ruleset - 定义适用于 Cloud Firestore 实例或 Cloud Storage 存储桶的 Firebase Security Rules

  • google_firebaserules_release - 将特定规则集部署到 Cloud Firestore 实例或 Cloud Storage 存储桶


Firebase App Check


Firebase Extensions



常见使用场景的 Terraform 示例配置文件

此配置会创建一个新的 Google Cloud 项目,将该项目与一个 Cloud Billing 账号关联(若要使用 Firebase Authentication with GCIP,您的项目必须采用 Blaze 定价方案),为项目启用所需的 Firebase 服务,设置 Firebase Authentication with GCIP,并在项目中注册三种不同的应用类型。

请注意,必须启用 GCIP,才能通过 Terraform 设置 Firebase Authentication

# Creates a new Google Cloud project.
resource "google_project" "auth" {
  provider  = google-beta.no_user_project_override
  folder_id = "folder-id-for-new-project"
  name            = "Project Display Name"
  project_id      = "project-id-for-new-project"

  # Associates the project with a Cloud Billing account
  # (required for Firebase Authentication with GCIP).
  billing_account = "000000-000000-000000"

  # Required for the project to display in a list of Firebase projects.
  labels = {
    "firebase" = "enabled"
  }
}

# Enables required APIs.
resource "google_project_service" "auth" {
  provider = google-beta.no_user_project_override
  project  = google_project.auth.project_id
  for_each = toset([
    "cloudbilling.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "serviceusage.googleapis.com",
    "identitytoolkit.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}

# Enables Firebase services for the new project created above.
resource "google_firebase_project" "auth" {
  provider = google-beta
  project  = google_project.auth.project_id

  depends_on = [
    google_project_service.auth,
  ]
}

# Creates an Identity Platform config.
# Also enables Firebase Authentication with Identity Platform in the project if not.
resource "google_identity_platform_config" "auth" {
  provider = google-beta
  project  = google_project.auth.project_id

  # Auto-deletes anonymous users
  autodelete_anonymous_users = true

  # Configures local sign-in methods, like anonymous, email/password, and phone authentication.
  sign_in {
    allow_duplicate_emails = true

    anonymous {
      enabled = true
    }

    email {
      enabled = true
      password_required = false
    }

    phone_number {
      enabled = true
      test_phone_numbers = {
        "+11231231234" = "000000"
      }
    }
  }

  # Sets an SMS region policy.
  sms_region_config {
    allowlist_only {
      allowed_regions = [
        "US",
        "CA",
      ]
    }
  }

  # Configures blocking functions.
  blocking_functions {
    triggers {
      event_type = "beforeSignIn"
      function_uri = "https://us-east1-${google_project.auth.project_id}.cloudfunctions.net/before-sign-in"
    }
    forward_inbound_credentials {
      refresh_token = true
      access_token = true
      id_token = true
    }
  }

  # Configures a temporary quota for new signups for anonymous, email/password, and phone number.
  quota {
    sign_up_quota_config {
      quota = 1000
      start_time = ""
      quota_duration = "7200s"
    }
  }

  # Configures authorized domains.
  authorized_domains = [
    "localhost",
    "${google_project.auth.project_id}.firebaseapp.com",
    "${google_project.auth.project_id}.web.app",
  ]

  # Wait for identitytoolkit.googleapis.com to be enabled before initializing Authentication.
  depends_on = [
    google_project_service.auth,
  ]
}

# Creates a Firebase Android App in the new project created above.
resource "google_firebase_android_app" "auth" {
  provider     = google-beta
  project      = google_project.auth.project_id
  display_name = "My Android app"
  package_name = "android.package.name"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.auth,
  ]
}

# Creates a Firebase Apple-platforms App in the new project created above.
resource "google_firebase_apple_app" "auth" {
  provider     = google-beta
  project      = google_project.auth.project_id
  display_name = "My Apple app"
  bundle_id    = "apple.app.12345"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.auth,
  ]
}

# Creates a Firebase Web App in the new project created above.
resource "google_firebase_web_app" "auth" {
  provider     = google-beta
  project      = google_project.auth.project_id
  display_name = "My Web app"

  # The other App types (Android and Apple) use "DELETE" by default.
  # Web apps don't use "DELETE" by default due to backward-compatibility.
  deletion_policy = "DELETE"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.auth,
  ]
}

此配置会创建一个新的 Google Cloud 项目,为项目启用所需的 Firebase 服务,为项目预配默认的 Realtime Database 实例,并在项目中注册三种不同的应用类型。

# Creates a new Google Cloud project.
resource "google_project" "rtdb" {
  provider   = google-beta.no_user_project_override
  folder_id  = "folder-id-for-new-project"
  name       = "Project Display Name"
  project_id = "project-id-for-new-project"

  # Required for the project to display in a list of Firebase projects.
  labels = {
    "firebase" = "enabled"
  }
}

# Enables required APIs.
resource "google_project_service" "rtdb" {
  provider = google-beta.no_user_project_override
  project  = google_project.rtdb.project_id
  for_each = toset([
    "serviceusage.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "firebasedatabase.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}

# Enables Firebase services for the new project created above.
resource "google_firebase_project" "rtdb" {
  provider = google-beta
  project  = google_project.rtdb.project_id
}

# Provisions the default Realtime Database default instance.
resource "google_firebase_database_instance" "database" {
  provider    = google-beta
  project     = google_project.rtdb.project_id
  # See available locations: https://firebase.google.com/docs/database/locations
  region      = "name-of-region"
  # This value will become the first segment of the database's URL.
  instance_id = "${google_project.rtdb.project_id}-default-rtdb"
  type        = "DEFAULT_DATABASE"

  # Wait for Firebase to be enabled in the Google Cloud project before initializing Realtime Database.
  depends_on = [
    google_firebase_project.rtdb,
  ]
}

# Creates a Firebase Android App in the new project created above.
resource "google_firebase_android_app" "rtdb" {
  provider     = google-beta
  project      = google_project.rtdb.project_id
  display_name = "My Android app"
  package_name = "android.package.name"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.rtdb,
  ]
}

# Creates a Firebase Apple-platforms App in the new project created above.
resource "google_firebase_apple_app" "rtdb" {
  provider     = google-beta
  project      = google_project.rtdb.project_id
  display_name = "My Apple app"
  bundle_id    = "apple.app.12345"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.rtdb,
  ]
}

# Creates a Firebase Web App in the new project created above.
resource "google_firebase_web_app" "rtdb" {
  provider     = google-beta
  project      = google_project.rtdb.project_id
  display_name = "My Web app"

  # The other App types (Android and Apple) use "DELETE" by default.
  # Web apps don't use "DELETE" by default due to backward-compatibility.
  deletion_policy = "DELETE"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.rtdb,
  ]
}

此配置会创建一个新的 Google Cloud 项目,将该项目与 Cloud Billing 账号相关联(若有多个 Realtime Database 实例,则必须采用 Blaze 定价方案),为项目启用所需的 Firebase 服务,预配多个 Realtime Database 实例(包括项目的默认 Realtime Database 实例),并在项目中注册三种不同的应用类型。

# Creates a new Google Cloud project.
resource "google_project" "rtdb-multi" {
  provider   = google-beta.no_user_project_override
  folder_id  = "folder-id-for-new-project"
  name       = "Project Display Name"
  project_id = "project-id-for-new-project"

  # Associate the project with a Cloud Billing account
  # (required for multiple Realtime Database instances).
  billing_account = "000000-000000-000000"

  # Required for the project to display in a list of Firebase projects.
  labels = {
    "firebase" = "enabled"
  }
}

# Enables required APIs.
resource "google_project_service" "rtdb-multi" {
  provider = google-beta.no_user_project_override
  project  = google_project.rtdb-multi.project_id
  for_each = toset([
    "cloudbilling.googleapis.com",
    "serviceusage.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "firebasedatabase.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}

# Enables Firebase services for the new project created above.
resource "google_firebase_project" "rtdb-multi" {
  provider = google-beta
  project  = google_project.rtdb-multi.project_id
}

# Provisions the default Realtime Database default instance.
resource "google_firebase_database_instance" "database-default" {
  provider    = google-beta
  project     = google_project.rtdb-multi.project_id
  # See available locations: https://firebase.google.com/docs/database/locations
  region      = "name-of-region"
  # This value will become the first segment of the database's URL.
  instance_id = "${google_project.rtdb-multi.project_id}-default-rtdb"
  type        = "DEFAULT_DATABASE"

  # Wait for Firebase to be enabled in the Google Cloud project before initializing Realtime Database.
  depends_on = [
    google_firebase_project.rtdb-multi,
  ]
}

# Provisions an additional Realtime Database instance.
resource "google_firebase_database_instance" "database-additional" {
  provider    = google-beta
  project     = google_project.rtdb-multi.project_id
  # See available locations: https://firebase.google.com/docs/projects/locations#rtdb-locations
  # This location doesn't need to be the same as the default database instance.
  region      = "name-of-region"
  # This value will become the first segment of the database's URL.
  instance_id = "name-of-additional-database-instance"
  type        = "USER_DATABASE"

  # Wait for Firebase to be enabled in the Google Cloud project before initializing Realtime Database.
  depends_on = [
    google_firebase_project.rtdb-multi,
  ]
}

# Creates a Firebase Android App in the new project created above.
resource "google_firebase_android_app" "rtdb-multi" {
  provider     = google-beta
  project      = google_project.rtdb-multi.project_id
  display_name = "My Android app"
  package_name = "android.package.name"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.rtdb-multi,
  ]
}

# Creates a Firebase Apple-platforms App in the new project created above.
resource "google_firebase_apple_app" "rtdb-multi" {
  provider     = google-beta
  project      = google_project.rtdb-multi.project_id
  display_name = "My Apple app"
  bundle_id    = "apple.app.12345"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.rtdb-multi,
  ]
}

# Creates a Firebase Web App in the new project created above.
resource "google_firebase_web_app" "rtdb-multi" {
  provider     = google-beta
  project      = google_project.rtdb-multi.project_id
  display_name = "My Web app"

  # The other App types (Android and Apple) use "DELETE" by default.
  # Web apps don't use "DELETE" by default due to backward-compatibility.
  deletion_policy = "DELETE"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.rtdb-multi,
  ]
}

此配置会创建一个新的 Google Cloud 项目,为项目启用所需的 Firebase 服务,为项目预配默认的 Cloud Firestore 实例,并在项目中注册三种不同的应用类型。

此外,还会为默认的 Cloud Firestore 实例预配 Firebase Security Rules,创建 Cloud Firestore 索引,并添加包含种子数据的 Cloud Firestore 文档。

# Creates a new Google Cloud project.
resource "google_project" "firestore" {
  provider   = google-beta.no_user_project_override
  folder_id  = "folder-id-for-new-project"
  name       = "Project Display Name"
  project_id = "project-id-for-new-project"

  # Required for the project to display in a list of Firebase projects.
  labels = {
    "firebase" = "enabled"
  }
}

# Enables required APIs.
resource "google_project_service" "firestore" {
  provider = google-beta.no_user_project_override
  project  = google_project.firestore.project_id
  for_each = toset([
    "cloudresourcemanager.googleapis.com",
    "serviceusage.googleapis.com",
    "firestore.googleapis.com",
    "firebaserules.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}

# Enables Firebase services for the new project created above.
resource "google_firebase_project" "firestore" {
  provider = google-beta
  project  = google_project.firestore.project_id
}

# Provisions the Firestore database instance.
resource "google_firestore_database" "firestore" {
  provider                    = google-beta
  project                     = google_project.firestore.project_id
  name                        = "(default)"
  # See available locations: https://firebase.google.com/docs/firestore/locations
  location_id                 = "name-of-region"
  # "FIRESTORE_NATIVE" is required to use Firestore with Firebase SDKs, authentication, and Firebase Security Rules.
  type                        = "FIRESTORE_NATIVE"
  concurrency_mode            = "OPTIMISTIC"

  # Wait for Firebase to be enabled in the Google Cloud project before initializing Firestore.
  depends_on = [
    google_firebase_project.firestore,
  ]
}

# Creates a ruleset of Firestore Security Rules from a local file.
resource "google_firebaserules_ruleset" "firestore" {
  provider = google-beta
  project  = google_project.firestore.project_id
  source {
    files {
      name = "firestore.rules"
      # Write security rules in a local file named "firestore.rules".
      # Learn more: https://firebase.google.com/docs/firestore/security/get-started
      content = file("firestore.rules")
    }
  }

  # Wait for Firestore to be provisioned before creating this ruleset.
  depends_on = [
    google_firestore_database.firestore,
  ]
}

# Releases the ruleset for the Firestore instance.
resource "google_firebaserules_release" "firestore" {
  provider     = google-beta
  name         = "cloud.firestore"  # must be cloud.firestore
  ruleset_name = google_firebaserules_ruleset.firestore.name
  project      = google_project.firestore.project_id

  # Wait for Firestore to be provisioned before releasing the ruleset.
  depends_on = [
    google_firestore_database.firestore,
  ]
}

# Adds a new Firestore index.
resource "google_firestore_index" "indexes" {
  provider = google-beta
  project  = google_project.firestore.project_id

  collection  = "quiz"
  query_scope = "COLLECTION"

  fields {
    field_path = "question"
    order      = "ASCENDING"
  }

  fields {
    field_path = "answer"
    order      = "ASCENDING"
  }

  # Wait for Firestore to be provisioned before adding this index.
  depends_on = [
    google_firestore_database.firestore,
  ]
}

# Adds a new Firestore document with seed data.
# Don't use real end-user or production data in this seed document.
resource "google_firestore_document" "doc" {
  provider    = google-beta
  project     = google_project.firestore.project_id
  collection  = "quiz"
  document_id = "question-1"
  fields      = "{\"question\":{\"stringValue\":\"Favorite Database\"},\"answer\":{\"stringValue\":\"Firestore\"}}"

  # Wait for Firestore to be provisioned before adding this document.
  depends_on = [
    google_firestore_database.firestore,
  ]
}

# Creates a Firebase Android App in the new project created above.
resource "google_firebase_android_app" "firestore" {
  provider     = google-beta
  project      = google_project.firestore.project_id
  display_name = "My Android app"
  package_name = "android.package.name"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.firestore,
  ]
}

# Creates a Firebase Apple-platforms App in the new project created above.
resource "google_firebase_apple_app" "firestore" {
  provider     = google-beta
  project      = google_project.firestore.project_id
  display_name = "My Apple app"
  bundle_id    = "apple.app.12345"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.firestore,
  ]
}

# Creates a Firebase Web App in the new project created above.
resource "google_firebase_web_app" "firestore" {
  provider     = google-beta
  project      = google_project.firestore.project_id
  display_name = "My Web app"

  # The other App types (Android and Apple) use "DELETE" by default.
  # Web apps don't use "DELETE" by default due to backward-compatibility.
  deletion_policy = "DELETE"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.firestore,
  ]
}

这是应包含在名为 firestore.rules 的本地文件中的 Cloud Firestore Security Rules的规则集。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /some_collection/{document} {
      allow read, create, update: if request.auth != null;
    }
  }
}

此配置会创建一个新的 Google Cloud 项目,将该项目与一个 Cloud Billing 账号关联(若要使用其他存储桶,您的项目必须采用 Blaze 定价方案),为项目启用所需的 Firebase 服务,预配其他非默认的 Cloud Storage 存储桶,并在项目中注册三种不同的应用类型。

此外,还会为每个 Cloud Storage 存储桶预配 Firebase Security Rules,并将文件上传到其中一个 Cloud Storage 存储桶。

# Creates a new Google Cloud project.
resource "google_project" "storage-multi" {
  provider  = google-beta.no_user_project_override
  folder_id = "folder-id-for-new-project"
  name            = "Project Display Name"
  project_id      = "project-id-for-new-project"

  # Associates the project with a Cloud Billing account
  # (required for multiple Cloud Storage buckets).
  billing_account = "000000-000000-000000"

  # Required for the project to display in a list of Firebase projects.
  labels = {
    "firebase" = "enabled"
  }
}

# Enables required APIs.
resource "google_project_service" "storage-multi" {
  provider = google-beta.no_user_project_override
  project  = google_project.storage-multi.project_id
  for_each = toset([
    "cloudbilling.googleapis.com",
    "serviceusage.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "firebaserules.googleapis.com",
    "firebasestorage.googleapis.com",
    "storage.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}

# Enables Firebase services for the new project created above.
resource "google_firebase_project" "storage-multi" {
  provider = google-beta
  project  = google_project.storage-multi.project_id
}

# Provisions a Cloud Storage bucket.
resource "google_storage_bucket" "bucket-1" {
  provider = google-beta
  project  = google_project.storage-multi.project_id
  name     = "name-of-storage-bucket"
  # See available locations: https://cloud.google.com/storage/docs/locations#available-locations
  location = "name-of-region-for-bucket"
}

# Provisions an additional Cloud Storage bucket.
resource "google_storage_bucket" "bucket-2" {
  provider = google-beta
  project  = google_project.storage-multi.project_id
  name     = "name-of-additional-storage-bucket"
  # See available locations: https://cloud.google.com/storage/docs/locations#available-locations
  # This location does not need to be the same as the existing Storage bucket.
  location = "name-of-region-for-additional-bucket"
}

# Makes the first Storage bucket accessible for Firebase SDKs, authentication, and Firebase Security Rules.
resource "google_firebase_storage_bucket" "bucket-1" {
  provider  = google-beta
  project   = google_project.storage-multi.project_id
  bucket_id = google_storage_bucket.bucket-1.name
}

# Makes the additional Storage bucket accessible for Firebase SDKs, authentication, and Firebase Security Rules.
resource "google_firebase_storage_bucket" "bucket-2" {
  provider  = google-beta
  project   = google_project.storage-multi.project_id
  bucket_id = google_storage_bucket.bucket-2.name
}

# Creates a ruleset of Firebase Security Rules from a local file.
resource "google_firebaserules_ruleset" "storage-multi" {
  provider = google-beta
  project  = google_project.storage-multi.project_id
  source {
    files {
      # Write security rules in a local file named "storage.rules"
      # Learn more: https://firebase.google.com/docs/storage/security/get-started
      name    = "storage.rules"
      content = file("storage.rules")
    }
  }

  # Wait for the Storage buckets to be provisioned before creating this ruleset.
  depends_on = [
    google_firebase_project.storage-multi,
  ]
}

# Releases the ruleset to the first Storage bucket.
resource "google_firebaserules_release" "bucket-1" {
  provider     = google-beta
  name         = "firebase.storage/${google_storage_bucket.bucket-1.name}"
  ruleset_name = "projects/${google_project.storage-multi.project_id}/rulesets/${google_firebaserules_ruleset.storage-multi.name}"
  project      = google_project.storage-multi.project_id
}

# Releases the ruleset to the additional Storage bucket.
resource "google_firebaserules_release" "bucket-2" {
  provider     = google-beta
  name         = "firebase.storage/${google_storage_bucket.bucket-2.name}"
  ruleset_name = "projects/${google_project.storage-multi.project_id}/rulesets/${google_firebaserules_ruleset.storage-multi.name}"
  project      = google_project.storage-multi.project_id
}

# Uploads a new file to the first Storage bucket.
# Do not use real end-user or production data in this file.
resource "google_storage_bucket_object" "cat-picture-multi" {
  provider = google-beta
  name     = "cat.png"
  source   = "path/to/cat.png"
  bucket   = google_storage_bucket.bucket-1.name
}

# Creates a Firebase Android App in the new project created above.
resource "google_firebase_android_app" "storage-multi" {
  provider     = google-beta
  project      = google_project.storage-multi.project_id
  display_name = "My Android app"
  package_name = "android.package.name"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.storage-multi,
  ]
}

# Creates a Firebase Apple-platforms App in the new project created above.
resource "google_firebase_apple_app" "storage-multi" {
  provider     = google-beta
  project      = google_project.storage-multi.project_id
  display_name = "My Apple app"
  bundle_id    = "apple.app.12345"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.storage-multi,
  ]
}

# Creates a Firebase Web App in the new project created above.
resource "google_firebase_web_app" "storage-multi" {
  provider     = google-beta
  project      = google_project.storage-multi.project_id
  display_name = "My Web app"

  # Wait for Firebase to be enabled in the Google Cloud project before creating this App.
  depends_on = [
    google_firebase_project.storage-multi,
  ]
}

这是应包含在名为 storage.rules 的本地文件中的 Cloud Storage Security Rules的规则集。

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /some_folder/{fileName} {
      allow read, write: if request.auth != null;
    }
  }
}

此配置会创建一个新的 Google Cloud 项目,为项目启用 Firebase 服务,并为 Cloud Firestore 设置并启用 Firebase App Check 强制执行,以便只能通过您的 Android 应用访问该项目。

# Creates a new Google Cloud project.
resource "google_project" "appcheck" {
  provider   = google-beta.no_user_project_override
  folder_id  = "folder-id-for-new-project"
  name       = "Project Display Name"
  project_id = "project-id-for-new-project"

  # Required for the project to display in a list of Firebase projects.
  labels = {
    "firebase" = "enabled"
  }
}

# Enables required APIs.
resource "google_project_service" "services" {
  provider = google-beta.no_user_project_override
  project  = google_project.appcheck.project_id
  for_each = toset([
    "cloudresourcemanager.googleapis.com",
    "firebase.googleapis.com",
    "firebaseappcheck.googleapis.com",
    "firestore.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
}

# Enables Firebase services for the new project created earlier.
resource "google_firebase_project" "appcheck" {
  provider = google-beta
  project  = google_project.appcheck.project_id

  depends_on = [google_project_service.services]
}

# Provisions the Firestore database instance.
resource "google_firestore_database" "database" {
  provider = google-beta
  project  = google_firebase_project.appcheck.project
  name     = "(default)"
  # See available locations: https://firebase.google.com/docs/projects/locations#default-cloud-location
  location_id = "name-of-region"
  # "FIRESTORE_NATIVE" is required to use Firestore with Firebase SDKs, authentication, and Firebase Security Rules.
  type             = "FIRESTORE_NATIVE"
  concurrency_mode = "OPTIMISTIC"

  # Wait for Firebase to be enabled in the Google Cloud project before initializing Firestore.
  depends_on = [
    google_firebase_project.appcheck,
  ]
}

# Creates a Firebase Android App in the new project created earlier.
resource "google_firebase_android_app" "appcheck" {
  provider     = google-beta
  project      = google_firebase_project.appcheck.project
  display_name = "Play Integrity app"
  package_name = "package.name.playintegrity"
  sha256_hashes = [
    # TODO: insert your Android app's SHA256 certificate
  ]
}

# It takes a while for App Check to recognize the new app
# If your app already exists, you don't have to wait 30 seconds.
resource "time_sleep" "wait_30s" {
  depends_on      = [google_firebase_android_app.appcheck]
  create_duration = "30s"
}

# Register the Android app with the Play Integrity provider
resource "google_firebase_app_check_play_integrity_config" "appcheck" {
  provider = google-beta
  project  = google_firebase_project.appcheck.project
  app_id   = google_firebase_android_app.appcheck.app_id

  depends_on = [time_sleep.wait_30s, google_firestore_database.database]

  lifecycle {
    precondition {
      condition     = length(google_firebase_android_app.appcheck.sha256_hashes) > 0
      error_message = "Provide a SHA-256 certificate on the Android App to use App Check"
    }
  }
}

# Enable enforcement of App Check for Firestore
resource "google_firebase_app_check_service_config" "firestore" {
  provider = google-beta

  project    = google_firebase_project.appcheck.project
  service_id = "firestore.googleapis.com"

  depends_on = [google_project_service.services]
}

此配置会创建一个新的 Google Cloud 项目,为项目启用 Firebase 服务,并在项目中安装新的 Firebase Extension 实例。如果实例已存在,系统会根据配置中提供的值更新其参数。

# Creates a new Google Cloud project.
resource "google_project" "extensions" {
  provider   = google-beta.no_user_project_override
  folder_id  = "folder-id-for-new-project"
  name       = "Project Display Name"
  project_id = "project-id-for-new-project"

  # Associates the project with a Cloud Billing account
  # (required to use Firebase Extensions).
  billing_account = "000000-000000-000000"

  # Required for the project to display in a list of Firebase projects.
  labels = {
    "firebase" = "enabled"
  }
}

# Enables required APIs.
resource "google_project_service" "extensions" {
  provider = google-beta.no_user_project_override
  project  = google_project.extensions.project_id
  for_each = toset([
    "cloudbilling.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "serviceusage.googleapis.com",
    "firebase.googleapis.com",
    "firebaseextensions.googleapis.com",
  ])
  service = each.key

  # Don't disable the service if the resource block is removed by accident.
  disable_on_destroy = false
}

# Enables Firebase services for the new project created above.
resource "google_firebase_project" "extensions" {
  provider = google-beta
  project  = google_project.extensions.project_id

  depends_on = [
    google_project_service.extensions,
  ]
}

# Installs an instance of the "Translate Text in Firestore" extension.
# Or updates the extension if the specified instance already exists.
resource "google_firebase_extensions_instance" "translation" {
  provider = google-beta
  project = google_project.extensions.project_id

  instance_id = "translate-text-in-firestore"
  config {
    extension_ref = "firebase/firestore-translate-text"

    params = {
      COLLECTION_PATH      = "posts/comments/translations"
      DO_BACKFILL          = true
      LANGUAGES            = "ar,en,es,de,fr"
      INPUT_FIELD_NAME     = "input"
      LANGUAGES_FIELD_NAME = "languages"
      OUTPUT_FIELD_NAME    = "translated"
    }

    system_params = {
      "firebaseextensions.v1beta.function/location"                   = "us-central1"
      "firebaseextensions.v1beta.function/memory"                     = "256"
      "firebaseextensions.v1beta.function/minInstances"               = "0"
      "firebaseextensions.v1beta.function/vpcConnectorEgressSettings" = "VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED"
    }
  }
}



问题排查和常见问题解答

本指南在涉及“项目”时会用到下面的 Terraform 属性。

resource 块中的 project

建议:尽可能在每个 resource 块中添加 project 属性

如果添加了 project 属性,Terraform 将在 resource 块中的指定项目内创建指定基础架构。本指南相关内容及示例配置文件都是采用这种做法。

请参阅 Terraform 官方文档中有关 project 的内容。

provider 块中的 user_project_override

若要预配大多数资源,则应使用 user_project_override = true,这意味着系统会检查您的 Firebase 项目的配额。不过,若要将新项目设置为能够接受配额检查,您需要首先使用 user_project_override = false

请参阅 Terraform 官方文档中有关 user_project_override 的内容。

请确保您用于运行 gcloud CLI 命令的用户账号已接受 Firebase 服务条款 (Firebase ToS)。

  • 您可以使用已登录该用户账号的浏览器尝试在 Firebase 控制台中查看一个现有 Firebase 项目,以进行确认。如果您可以查看现有 Firebase 项目,则表明该用户账号已接受 Firebase 服务条款 (TOS)。

  • 如果您无法查看任何现有 Firebase 项目,则表明该用户账号可能尚未接受 Firebase 服务条款 (TOS)。若要解决此问题,您可以通过 Firebase 控制台创建一个新的 Firebase 项目,并在项目创建过程中接受 Firebase 服务条款 (TOS)。您可以在完成此操作后立即通过控制台中的“项目设置”删除此项目。

请等待几分钟,然后再次尝试运行 terraform apply

这可能是由于各种系统中的传播延迟造成的。您可以运行 terraform import,将资源导入 Terraform 状态,以尝试解决此问题。然后,尝试再次运行 terraform apply

如需了解如何导入每种资源,请参阅其相应 Terraform 文档的“导入”部分(如 Cloud Firestore 的“导入”文档)。

如该错误所示,Terraform 可能在尝试同时预配多个索引并/或创建同一个文档,从而遇到并发错误。尝试再次运行 terraform apply

此错误表示 Terraform 不知道要检查哪个项目的配额。如需进行问题排查,请在 resource 块中检查以下内容:

  • 确保为 project 属性指定了值。
  • 确保为提供方设置了 user_project_override = true(无别名),在 Firebase 示例中该提供方为 google-beta

项目 ID 已存在的可能原因如下:

  • 与该 ID 关联的项目属于他人。

    • 解决方法:选择其他项目 ID。
  • 与该 ID 关联的项目最近被删除(处于软删除状态)。

    • 解决方法:如果您确定与该 ID 关联的项目属于您,则可以使用 projects.getREST API 检查该项目的状态。
  • 与该 ID 关联的项目正确地存在于当前用户下。错误的可能原因是先前的 terraform apply 被中断。

    • 解决方法:依次运行以下命令:
      terraform import google_project.default PROJECT_ID
      terraform import google_firebase_project.default PROJECT_ID

如果您在尝试配置默认 Cloud Firestore 实例之前(通过 google_app_engine_application)配置了默认的 Cloud Storage 存储桶,那么您会发现默认的 Cloud Firestore 实例已预配。请注意,预配的数据库实例处于 Datastore 模式,这意味着 Firebase SDK、身份验证机制或 Firebase Security Rules无法访问该实例。在这种情况下,如果您希望能够搭配这些 Firebase 服务使用 Cloud Firestore,则需要清空数据库,然后在 Google Cloud 控制台中更改其数据库类型。

如果您通过 google_app_engine_application 预配项目的默认 Cloud Storage 存储桶,而项目还没有默认 Cloud Firestore 实例,则 google_app_engine_application 会自动预配项目的默认 Cloud Firestore 实例。

因此,由于项目的默认 Cloud Firestore 实例已预配,如果您尝试再次明确预配该默认实例,google_firestore_database 会出错。

一旦项目的默认 Cloud Firestore 实例已预配完毕,您便无法“重新预配”或更改其位置。请注意,预配的数据库实例处于 Datastore 模式,这意味着 Firebase SDK、身份验证机制或 Firebase Security Rules无法访问该实例。在这种情况下,如果您希望能够搭配这些 Firebase 服务使用 Cloud Firestore,则需要清空数据库,然后在 Google Cloud 控制台中更改其数据库类型。