通过 Terraform 设置和管理 Firebase 项目及产品

1. 简介

目标

您可以使用 Terraform 设置和管理 Firebase 项目,包括以程序化方式配置基础架构和 Firebase 产品。

此 Codelab 首先介绍如何构建 Terraform 配置文件以创建新的 Firebase 项目,然后如何配置要在该项目中使用的应用和 Firebase 产品。我们还介绍了 Terraform 命令行的基础知识,例如预览要进行的更改并实现这些更改。

如果您想了解如何使用 Terraform 设置和管理 Firebase 项目及产品,那么此 Codelab 正适合您!

学习内容

  • 如何创建 Terraform 配置文件 (*.tf)
  • 如何使用 Terraform CLI 命令管理基础架构
  • 如何修改配置以更新资源和服务
  • 如何在实际 Web 应用(称为友好聊天)中应用您的配置
  • 如何在不同环境(生产环境、预演环境等)中定义并行(和同步)配置

所需条件

为了顺利完成此 Codelab,您需要基本掌握 Terraform 及其术语,同时,还需具备以下前提条件:

此 Codelab 提供了一个真实的示例应用,以便您测试通过 Terraform 预配的内容并与之交互。为此,您需要具备以下条件:

  • Web 应用的示例代码 - 请在此 Codelab 的下一步中下载此代码
  • 软件包管理器 npm(通常随 Node.js 一起提供)- 安装这些工具
  • Firebase CLI - 安装此 CLI 并登录

2. 获取起始代码

在此 Codelab 中,您可以在真实的 Web 应用中测试通过 Terraform 预配的资源。我们建议您这样做,以便了解使用 Terraform 预配的资源所需的所有步骤。

从命令行克隆此 Codelab 的 GitHub 代码库

git clone https://github.com/firebase/codelab-friendlychat-web

或者,如果您未安装 git,则可以以 ZIP 文件的形式下载代码库

3. 创建 Terraform 配置

Terraform 设置

  1. 在所下载示例应用的代码库中,转到 web 目录的根目录。
  2. 在该目录的根目录下,创建名为 main.tf 的 Terraform 配置文件,并进行以下初始设置:

    main.tf
    # Terraform configuration to set up providers by version.
    terraform {
      required_providers {
        google-beta = {
          source  = "hashicorp/google-beta"
          version = "~> 4.0"
        }
      }
    }
    
    # Configure the provider not to use the specified project for quota check.
    # This provider should only be used during project creation and initializing services.
    provider "google-beta" {
      alias                 = "no_user_project_override"
      user_project_override = false
    }
    
    # Configure the provider that uses the new project's quota.
    provider "google-beta" {
      user_project_override = true
    }
    

每个 google-beta 提供程序都有一个名为 user_project_override 的属性,该属性决定了如何对 Terraform 中的操作进行配额检查。如需预配大多数资源,则应使用 user_project_override = true,这意味着系统会检查您自己的 Firebase 项目的配额。不过,若要将新项目设置为接受配额检查,您需要先使用 user_project_override=false。借助 Terraform alias 语法,您可以在此 Codelab 的后续步骤中区分这两种提供程序设置。

在目录中初始化 Terraform

首次创建新配置时,需要下载配置中指定的提供程序。

如需执行此初始化操作,请从 main.tf 配置文件所在目录的根目录下运行以下命令:

terraform init

4.通过 Terraform 创建 Firebase 项目

如需“创建 Firebase 项目”,请务必注意,每个 Firebase 项目实际上都是一个 Google Cloud 项目,只不过为其启用了 Firebase 服务。

为底层 Google Cloud 项目和 API 添加块

  1. 首先,预配底层 Google Cloud 项目。

    在您的 main.tf 配置文件中,添加以下资源块。

    您需要指定自己的项目名称(如 "Terraform FriendlyChat Codelab")和您自己的项目 ID(如 "terraform-codelab-your-initials")。请注意,name 值仅在 Firebase 界面中使用,最终用户看不到。不过,project_id 值可让 Google 唯一标识您的项目,因此请务必指定一个唯一的值。 main.tf
    ...
    
    # Create a new Google Cloud project.
    resource "google_project" "default" {
      provider = google-beta.no_user_project_override
    
      name            = "<PROJECT_NAME_OF_YOUR_PROJECT>"
      project_id      = "<PROJECT_ID_OF_YOUR_PROJECT>"
    
      # Required for the project to display in any list of Firebase projects.
      labels = {
        "firebase" = "enabled"
      }
    }
    
  2. 接下来,您需要启用所需的底层 API:Service Usage API 和 Firebase Management API。

    当您使用 Firebase 控制台创建 Firebase 项目时,系统通常会在后台处理此 API 启用操作,但需要明确告知 Terraform 启用该功能。

    在您的 main.tf 配置文件(就在创建新 Cloud 项目的块下方)中添加以下资源块:

    main.tf
    ...
    
    # Enable the required underlying Service Usage API.
    resource "google_project_service" "serviceusage" {
      provider = google-beta.no_user_project_override
    
      project = google_project.default.project_id
      service = "serviceusage.googleapis.com"
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    # Enable the required underlying Firebase Management API.
    resource "google_project_service" "firebase" {
      provider = google-beta.no_user_project_override
    
      project = google_project.default.project_id
      service = "firebase.googleapis.com"
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    因此,对于所有后续的资源预配和启用服务,您应将提供程序与 user_project_override 搭配使用(无需别名)。

添加代码块以启用 Firebase 服务

要“创建 Firebase 项目”,最后一步就是在项目上启用 Firebase 服务。

继续在 main.tf 配置文件中添加以下资源块。

请注意,如上文所述,此资源块将提供程序与 user_project_override(不需要别名) 搭配使用。

main.tf

...

# Enable Firebase services for the new project created above.
resource "google_firebase_project" "default" {
  provider = google-beta

  project = google_project.default.project_id

  # Wait until the required APIs are enabled.
  depends_on = [
    google_project_service.firebase,
    google_project_service.serviceusage,
  ]
}

在上面的资源块中,您可能会注意到 depends_on 子句,该子句指示 Terraform 等待底层 API 启用。如果没有此子句,Terraform 就不知道依赖项,并且在并行预配资源时可能会遇到错误。

应用配置

  1. 如需预配新资源并启用配置文件中指定的 API,请从 main.tf 文件所在目录(应为 web)的根目录运行以下命令:
    terraform apply
    
  2. 在终端中,Terraform 会输出将要执行的操作计划。

    如果一切符合预期,请输入 yes 来批准操作。

    main.tf
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
      + create
    
    Terraform will perform the following actions:
    
      # google_firebase_project.default will be created
      + resource "google_firebase_project" "default" {
          + display_name   = (known after apply)
          + id             = (known after apply)
          + project        = "terraform-friendlychat-codelab"
          + project_number = (known after apply)
        }
    
      # google_project.default will be created
      + resource "google_project" "default" {
          + auto_create_network = true
          + id                  = (known after apply)
          + labels              = {
              + "firebase" = "enabled"
            }
          + name                = "Terraform FriendlyChat Codelab"
          + number              = (known after apply)
          + project_id          = "terraform-friendlychat-codelab"
          + skip_delete         = (known after apply)
        }
    
      # google_project_service.firebase will be created
      + resource "google_project_service" "firebase" {
          + disable_on_destroy = false
          + id                 = (known after apply)
          + project            = "terraform-friendlychat-codelab"
          + service            = "firebase.googleapis.com"
        }
    
      # google_project_service.serviceusage will be created
      + resource "google_project_service" "serviceusage" {
          + disable_on_destroy = false
          + id                 = (known after apply)
          + project            = "terraform-friendlychat-codelab"
          + service            = "serviceusage.googleapis.com"
        }
    
    Plan: 4 to add, 0 to change, 0 to destroy.
    
    Do you want to perform these actions?
      Terraform will perform the actions described above.
      Only 'yes' will be accepted to approve.
    
      Enter a value: yes # <----
    

请注意,如果您只需要预览更改,而不应用,可以改用 terraform plan 命令。

验证更改

Terraform 运行完毕后,您可以运行以下命令来检查已启用的所有 Terraform 预配资源和服务的状态:

terraform show

以下是您应该会看到的内容示例。您的状态将包含特定于您的项目的值。

# google_firebase_project.default:
resource "google_firebase_project" "default" {
    display_name   = "Terraform FriendlyChat Codelab"
    id             = "projects/terraform-friendlychat-codelab"
    project        = "terraform-friendlychat-codelab"
    project_number = "000000000"
}

# google_project.default:
resource "google_project" "default" {
    auto_create_network = true
    id                  = "projects/terraform-friendlychat-codelab"
    labels              = {
        "firebase" = "enabled"
    }
    name                = "Terraform FriendlyChat Codelab"
    number              = "000000000"
    project_id          = "terraform-friendlychat-codelab"
}

# google_project_service.firebase:
resource "google_project_service" "firebase" {
    disable_on_destroy = false
    id                 = "terraform-friendlychat-codelab/firebase.googleapis.com"
    project            = "terraform-friendlychat-codelab"
    service            = "firebase.googleapis.com"
}

# google_project_service.serviceusage:
resource "google_project_service" "serviceusage" {
    disable_on_destroy = false
    id                 = "terraform-friendlychat-codelab/serviceusage.googleapis.com"
    project            = "terraform-friendlychat-codelab"
    service            = "serviceusage.googleapis.com"
}

或者,您也可以在 Firebase 控制台中查看项目,以验证该项目是否已创建。

在 Firebase 控制台上选择的 Terraform FreeChat Codelab 项目

5. 通过 Terraform 注册您的 Firebase 应用

如需使用 Firebase,您需要在 Firebase 项目中注册应用的每个平台变体。在此 Codelab 中,您将使用一个真实应用来测试通过 Terraform 预配的内容并与之交互。此应用属于 Web 应用,因此您需要告知 Terraform 在新创建的 Firebase 项目中注册 Firebase Web 应用。

添加代码块以注册 Web 应用

如需在 Firebase 项目中注册您的 Web 应用,请将 main.tf 文件附加到以下资源块中。

您需要为自己的 Web 应用指定自己的 display_name。请注意,此名称仅在 Firebase 界面中使用,最终用户看不到。

main.tf

...

# Create a Firebase Web App in the new project created above.
resource "google_firebase_web_app" "default" {
  provider = google-beta

  project      = google_firebase_project.default.project
  display_name = "<DISPLAY_NAME_OF_YOUR_WEB_APP>"
  deletion_policy = "DELETE"
}

应用配置

  1. 如需预配新的资源,请从 main.tf 文件所在目录的根目录(应为 web)运行以下命令。
    terraform apply
    
    请注意,此命令不会重新创建新的 Google Cloud 项目。Terraform 会检测出具有指定项目 ID 的项目已存在,并将项目的当前状态与 .tf 文件中的内容进行比较,并做出找到的任何更改。
  2. 查看打印的行动计划。如果一切正常,请输入 yes 并按 Enter 键批准操作。

验证更改

您可以通过运行以下命令来检查新预配的资源的状态:

terraform show

或者,您也可以在 Firebase 控制台中查看该应用,以验证该应用是否在您的项目中成功注册。前往项目设置,然后向下滚动到“您的应用”部分。

6. 设置 Firebase Authentication

对于任何应用,身份验证都至关重要。若要允许最终用户使用其 Google 账号登录您的 Web 应用,您可以启用 Firebase Authentication 并设置“使用 Google 账号登录”方法。

请注意,在此 Codelab 中,我们为设置 Firebase Authentication 提供了两个不同的选项:

  • 方法 1(推荐):在控制台中设置 Firebase Authentication,不需要 GCIP。
    • 使用此选项意味着您无需将新项目与 Cloud Billing 帐号相关联。
  • 方法 2:使用 Google Cloud Identity Platform (GCIP) API 通过 Terraform 设置 Firebase Authentication。
    • 使用此选项意味着您必须将新项目与 Cloud Billing 账号相关联,因为 GCIP 要求项目采用 Blaze 定价方案。

方法 1:使用 Firebase 控制台设置 Authentication

如需使用 Firebase 控制台设置 Firebase Authentication,您的项目不需要采用 Blaze 定价方案。

下面介绍了如何设置 Firebase Authentication 并使用 Google 账号登录:

  1. Firebase 控制台中,在左侧面板中找到构建部分。
  2. 依次点击身份验证开始使用,然后点击登录方法标签页(或点击此处直接访问)。
  3. 点击添加新提供商,然后从其他提供商部分选择 Google
  4. 打开启用切换开关。
  5. 将应用的公开名称设置为 FriendlyChat 之类的名称(无需具有全局唯一性)。
  6. 从下拉菜单中选择项目支持电子邮件地址,然后点击保存在 Firebase 控制台中配置 Firebase Auth
  7. 您应该会看到 Google 显示为已启用的登录服务提供方。Firebase 控制台“Authentication”页面:已启用 Google 登录功能

方法 2:使用 Google Cloud Identity Platform (GCIP) API 通过 Terraform 设置身份验证

如需通过 Terraform 设置 Firebase Authentication,您必须使用 GCIP API,这意味着项目必须采用 Blaze 定价方案。您可以将 Cloud Billing 账号与项目关联,从而将 Firebase 项目升级为使用 Blaze 方案。

通过 Terraform 启用结算功能

  1. 如果您还没有 Cloud Billing 帐号,第一步是在 Google Cloud 控制台中创建一个新帐号。当您执行此操作时,请记下其结算帐号 ID。您可以在“结算”页面中找到与您的项目相关联的结算帐号 ID 中的结算帐号 ID。使用 Google Cloud 控制台启用结算账号
  2. 如需通过 Terraform 在项目中启用结算功能,请在 main.tf 文件中的现有 google_project 资源中添加 billing_account 属性:

    main.tf
    ...
    
    # Create a new Google Cloud project.
    resource "google_project" "default" {
      provider = google-beta.no_user_project_override
    
      name            = "<PROJECT_NAME_OF_YOUR_PROJECT>"
      project_id      = "<PROJECT_ID_OF_YOUR_PROJECT>"
      billing_account = "<YOUR_BILLING_ACCOUNT_ID>" # Add this line with your Cloud Billing account ID
    
      # Required for the project to display in any list of Firebase projects.
      labels = {
        "firebase" = "enabled"
      }
    }
    
    ...
    

启用 Firebase Authentication 并通过 Terraform 使用 Google 账号登录

  1. 如需配置 Firebase Authentication with GCIP,请在您的 main.tf 文件中附加以下资源块:

    main.tf
    ...
    
    # Enable the Identity Toolkit API.
    resource "google_project_service" "auth" {
      provider = google-beta
    
      project  = google_firebase_project.default.project
      service =  "identitytoolkit.googleapis.com"
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    # Create an Identity Platform config.
    # Also, enable Firebase Authentication using Identity Platform (if Authentication isn't yet enabled).
    resource "google_identity_platform_config" "auth" {
      provider = google-beta
      project  = google_firebase_project.default.project
    
      # For example, you can configure to auto-delete anonymous users.
      autodelete_anonymous_users = true
    
      # Wait for identitytoolkit.googleapis.com to be enabled before initializing Authentication.
      depends_on = [
        google_project_service.auth,
      ]
    }
    
  2. 您必须拥有 OAuth 客户端,才能启用“使用 Google 账号登录”。请前往 Google Cloud 控制台的 API 和服务部分进行此设置。
  3. 由于这是您第一次为此项目创建客户端 ID,因此您需要配置 OAuth 同意屏幕。
    1. 打开 OAuth 同意屏幕页面,然后选择您刚刚创建的项目。
    2. 将“用户类型”设置为“外部”,然后点击创建
    3. 在下一个屏幕中,完成以下操作,然后点击保存并继续
      • 将应用的面向公众的应用名称设为类似 FriendlyChat 这样的内容(无需具有全局唯一性)。
      • 从下拉菜单中选择用户支持电子邮件地址
      • 输入开发者联系信息的电子邮件地址。
    4. 在接下来的屏幕中,完成以下操作:
      • Scopes(范围)页面上接受默认值,然后点击 Save and Continue(保存并继续)。
      • 测试用户页面上接受默认设置,然后点击保存并继续
      • 查看摘要,然后点击返回信息中心
      使用 Google Cloud 控制台配置 OAuth2 客户端
  4. 执行以下操作,在凭据页面中设置 OAuth 客户端:
    1. 点击创建凭据并选择 OAuth 客户端 ID
    2. 应用类型下拉菜单中,选择 Web 应用
    3. Name 字段中,输入应用的名称,例如 FriendlyChat(无需保持全局唯一)。
    4. 通过进行以下设置,允许您的应用的网址使用此 OAuth 客户端:
      • 已获授权的 JavaScript 来源下,点击添加 URI 并输入
        https://<PROJECT_ID>.firebaseapp.com,其中 <PROJECT_ID> 是您在 main.tf 中设置的项目 ID。
      • 已获授权的重定向 URI 下,点击添加 URI 并输入
        https://<PROJECT_ID>.firebaseapp.com/__/auth/handler,其中 <PROJECT_ID> 是您在 main.tf 中设置的项目 ID。
    5. 点击保存
    从 Google Cloud 控制台的“凭据”页面获取 OAuth2 客户端 ID 和密钥
  5. 如需支持使用您的 OAuth 客户端 ID 和客户端密钥使用 Google 帐号登录,请在您的 main.tf 文件中添加以下代码块:

    main.tf
    ...
    
    variable "oauth_client_secret" {
      type = string
    
      description = "OAuth client secret. For this codelab, you can pass in this secret through the environment variable TF_VAR_oauth_client_secret. In a real app, you should use a secret manager service."
    
      sensitive = true
    }
    
    resource "google_identity_platform_default_supported_idp_config" "google_sign_in" {
      provider = google-beta
      project  = google_firebase_project.default.project
    
      enabled       = true
      idp_id        = "google.com"
      client_id     = "<YOUR_OAUTH_CLIENT_ID>"
      client_secret = var.oauth_client_secret
    
      depends_on = [
         google_identity_platform_config.auth
      ]
    }
    

应用配置

  1. 如需根据您的配置设置 Authentication,请从 main.tf 文件的根目录(应为 web)运行以下命令:
    export TF_VAR_oauth_client_secret="<YOUR_OAUTH_CLIENT_SECRET>"
    
    terraform apply
    
    请注意,运行 terraform apply 不会重新创建新的 Google Cloud 项目。Terraform 会检测到具有指定项目 ID 的项目已存在,并将项目的当前状态与 .tf 文件中的内容进行比较。随后它会执行发现的任何更改。
  2. 查看打印的行动计划。如果一切正常,请输入 yes 并按 Enter 键批准操作。

验证更改

  1. Firebase 控制台中,在左侧面板中找到构建部分。
  2. 点击身份验证,然后点击登录方法标签页(或点击此处直接转到相应页面)。
  3. 您应该会看到 Google 显示为已启用的登录服务提供方。Firebase 控制台“Authentication”页面:已启用 Google 登录功能

7. 设置 Firestore 数据库及其安全规则

在此 Codelab 的 Web 应用中,您需要将最终用户之间的消息存储在 Firestore 数据库中。

  1. 如需启用所需的 API 并预配数据库实例,请在您的 main.tf 文件中附加以下资源块:

    main.tf
    ...
    
    # Enable required APIs for Cloud Firestore.
    resource "google_project_service" "firestore" {
      provider = google-beta
    
      project  = google_firebase_project.default.project
      for_each = toset([
        "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
    }
    
    # Provision the Firestore database instance.
    resource "google_firestore_database" "default" {
      provider                    = google-beta
    
      project                     = google_firebase_project.default.project
      name                        = "(default)"
      # See available locations:
      # https://firebase.google.com/docs/firestore/locations
      location_id                 = "<NAME_OF_DESIRED_REGION>"
      # "FIRESTORE_NATIVE" is required to use Firestore with Firebase SDKs,
      # authentication, and Firebase Security Rules.
      type                        = "FIRESTORE_NATIVE"
      concurrency_mode            = "OPTIMISTIC"
    
      depends_on = [
        google_project_service.firestore
      ]
    }
    
  2. <NAME_OF_DESIRED_REGION> 更改为您所需的数据库所在的区域。

    开发正式版应用时,应选择靠近大多数用户的区域,并且与其他 Firebase 服务(例如 Cloud Functions)相同。对于此 Codelab,您可以使用 us-east1(南卡罗来纳),也可以使用离您最近的区域(请参阅 Cloud Firestore 位置)。
  3. Firebase 可访问的每个 Firestore 数据库实例都必须受 Firebase 安全规则保护。

    此 Codelab 的示例代码在 firestore.rules 文件中提供了一组安全的 Firestore 规则,该文件位于 web 目录的根目录下。
  4. main.tf 文件附加到以下资源块,以执行以下操作:
    • 通过本地 firestore.rules 文件创建 Firebase 安全规则的规则集。
    • 释放 Firestore 实例的规则集。
    请注意,这些资源块的作用相当于点击 Firebase 控制台中的发布按钮或运行 firebase deploy --only firestore:rules

    main.tf
    ...
    
    # Create a ruleset of Firestore Security Rules from a local file.
    resource "google_firebaserules_ruleset" "firestore" {
      provider = google-beta
    
      project  = google_firebase_project.default.project
      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.default,
      ]
    }
    
    # Release 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_firebase_project.default.project
    
      # Wait for Firestore to be provisioned before releasing the ruleset.
      depends_on = [
        google_firestore_database.default,
      ]
    
      lifecycle {
        replace_triggered_by = [
          google_firebaserules_ruleset.firestore
        ]
      }
    }
    
  5. 运行 terraform apply 以预配 Firestore 数据库并部署其安全规则。
  6. 验证数据库是否已预配以及其安全规则是否已部署:
    1. Firebase 控制台中,在左侧面板中找到构建部分。
    2. 转到 Firestore 数据库部分,然后点击规则标签页。
    使用 Firebase 控制台验证 Cloud Firestore 规则

8. 设置 Cloud Storage 存储分区及其安全规则

对于此 Codelab 的 Web 应用,您要将最终用户共享的图片存储在 Cloud Storage 存储分区中。

  1. 如需启用所需的 API 并预配 Cloud Storage 默认存储分区,请在您的 main.tf 文件中附加以下资源块。

    请注意,项目的默认 Cloud Storage 存储分区是通过 Google App Engine 预配的,并且必须与您的 Firestore 数据库位于同一位置。如需了解详情,请参阅 App Engine 位置

    如果您希望项目包含多个存储分区,请使用 google_storage_bucket 资源预配这些存储分区(此 Codelab 中未显示这些存储分区)。

    main.tf
    ...
    
    # Enable required APIs for Cloud Storage for Firebase.
    resource "google_project_service" "storage" {
      provider = google-beta
    
      project  = google_firebase_project.default.project
      for_each = toset([
        "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
    }
    
    # Provision the default Cloud Storage bucket for the project via Google App Engine.
    resource "google_app_engine_application" "default" {
      provider    = google-beta
    
      project     = google_firebase_project.default.project
      # See available locations: https://firebase.google.com/docs/projects/locations#default-cloud-location
      # This will set the location for the default Storage bucket and the App Engine App.
      location_id = "<NAME_OF_DESIRED_REGION_FOR_DEFAULT_BUCKET>"  # Must be in the same location as Firestore (above)
    
      # Wait until Firestore is provisioned first.
      depends_on = [
        google_firestore_database.default
      ]
    }
    
    # Make the default Storage bucket accessible for Firebase SDKs, authentication, and Firebase Security Rules.
    resource "google_firebase_storage_bucket" "default-bucket" {
      provider  = google-beta
    
      project   = google_firebase_project.default.project
      bucket_id = google_app_engine_application.default.default_bucket
    }
    
  2. Firebase 可访问的每个 Cloud Storage 存储分区都必须受 Firebase 安全规则保护。

    此 Codelab 的示例代码在 storage.rules 文件中提供了一组安全的 Firestore 规则,该文件位于 web 目录的根目录下。
  3. main.tf 文件附加到以下资源块,以执行以下操作:
    • 从本地文件创建 Firebase 安全规则的规则集。
    • 释放存储分区的规则集。
    请注意,这些资源块的作用相当于点击 Firebase 控制台中的发布按钮或运行 firebase deploy --only storage

    main.tf
    ...
    
    # Create a ruleset of Cloud Storage Security Rules from a local file.
    resource "google_firebaserules_ruleset" "storage" {
      provider = google-beta
    
      project  = google_firebase_project.default.project
      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 default Storage bucket to be provisioned before creating this ruleset.
      depends_on = [
        google_firebase_storage_bucket.default-bucket,
      ]
    }
    
    # Release the ruleset to the default Storage bucket.
    resource "google_firebaserules_release" "default-bucket" {
      provider     = google-beta
    
      name         = "firebase.storage/${google_app_engine_application.default.default_bucket}"
      ruleset_name = "projects/${google_firebase_project.default.project}/rulesets/${google_firebaserules_ruleset.storage.name}"
      project      = google_firebase_project.default.project
    
      lifecycle {
        replace_triggered_by = [
          google_firebaserules_ruleset.storage
        ]
      }
    }
    
  4. 运行 terraform apply 以预配默认的 Cloud Storage 存储分区并部署其安全规则。
  5. 验证存储分区是否已预配,其安全规则是否已部署:
    1. Firebase 控制台中,在左侧面板中找到构建部分。
    2. 转到存储部分,然后点击规则标签页。
    使用 Firebase 控制台验证安全规则

9. 在本地运行应用

现在,您可以开始首次运行自己的 Web 应用了!您将使用 Firebase Hosting 模拟器在本地提供应用。

  1. 打开一个新的终端窗口,然后从 web 目录运行以下 Firebase CLI 命令以启动模拟器:
    firebase emulators:start --project=<PROJECT_ID>
    
  2. 在浏览器中,使用 CLI 返回的本地网址(通常为 http://localhost:5000)打开您的 Web 应用。

您应该会看到 FreeChat 应用的界面(尚未运行)。该应用尚未连接到 Firebase,但完成此 Codelab 的后续步骤后,应用就会连接到 Firebase!

请注意,每当您对 Web 应用进行更改(就像您在此 Codelab 的后续步骤中进行更改)时,请刷新浏览器,以使用这些更改更新本地网址。

10. 安装、配置和初始化 Firebase

为了让应用能够与 Firebase 搭配使用,您的应用需要 Firebase SDK 和 Firebase 项目的 Firebase 配置。

此 Codelab 的示例代码已经是一个可正常运行的应用,其中包含在应用中使用各种 Firebase 产品所需的全部依赖项和所需的函数。如果您想查看已完成的操作,可以在 web/package.jsonweb/src/index.js 中查看。

虽然示例代码已基本完成,但您仍需要执行一些操作才能让您的应用正常运行,包括安装 Firebase SDK、启动构建、将 Firebase 配置添加到您的应用,最后初始化 Firebase。

安装 Firebase SDK 并开始构建 webpack

您需要运行几个命令以开始构建应用。

  1. 打开一个新的终端窗口。
  2. 确保您位于 web 目录的根目录下。
  3. 运行 npm install 以下载 Firebase SDK。
  4. 运行 npm update 以更新所有依赖项。
  5. 运行 npm run start 以启动 webpack。

在此 Codelab 的剩余部分中,Webpack 现在将不断重新构建您的源代码。

将 Firebase 配置添加到您的应用

您还需要将 Firebase 配置添加到您的应用中,以便 Firebase SDK 知道您希望他们使用哪个 Firebase 项目。

对于此 Codelab,您可以通过两种不同的方式获取 Firebase 配置:

  • 方法 1:从 Firebase 控制台获取 Firebase 配置。
  • 方法 2:通过 Terraform 获取 Firebase 配置。

方法 1:从 Firebase 控制台获取配置并将其添加到您的代码库中

  1. 在 Firebase 控制台中,前往项目设置
  2. 向下滚动到您的应用卡片,然后选择您的 Web 应用。
  3. 从 Firebase SDK 代码段窗格中选择配置,然后复制配置代码段。
  4. 将配置粘贴到应用的 web/src/firebase-config.js 文件中,如下所示:

    firebase-config.js
    ...
    
    const config = {
      apiKey: "<API_KEY>",
      authDomain: "<PROJECT_ID>.firebaseapp.com",
      projectId: "<PROJECT_ID>",
      storageBucket: "<PROJECT_ID>.appspot.com",
      messagingSenderId: "<SENDER_ID>",
      appId: "<APP_ID>",
      measurementId: "<G-MEASUREMENT_ID>",
    };
    
    ...
    

方法 2:通过 Terraform 获取配置并将其添加到代码库中

或者,您也可以通过 Terraform 获取 Firebase 配置,将其作为 CLI 中的输出值

  1. main.tf 文件中,找到 google_firebase_web_app 资源块(在您的项目中注册 Web 应用的块)。
  2. 在该代码块之后,立即添加以下代码块:

    main.tf
    ...
    
    data "google_firebase_web_app_config" "default" {
      provider     = google-beta
      project      = google_firebase_project.default.project
      web_app_id   = google_firebase_web_app.default.app_id
    }
    
    output "friendlychat_web_app_config" {
      value = {
        projectId         = google_firebase_project.default.project
        appId             = google_firebase_web_app.default.app_id
        apiKey            = data.google_firebase_web_app_config.default.api_key
        authDomain        = data.google_firebase_web_app_config.default.auth_domain
        storageBucket     = lookup(data.google_firebase_web_app_config.default, "storage_bucket", "")
        messagingSenderId = lookup(data.google_firebase_web_app_config.default, "messaging_sender_id", "")
        measurementId     = lookup(data.google_firebase_web_app_config.default, "measurement_id", "")
      }
    }
    
    ...
    
  3. 由于 data 代码块和 output 代码块并非用于以任何方式修改基础架构,因此您只需运行以下命令。
    1. 如需将 Web 应用的 Firebase 配置加载到目录的 Terraform 状态,请运行以下命令:
      terraform refresh
      
    2. 如需输出 Firebase 配置值,请运行以下命令:
      terraform output –json
      
      以下是配置的输出示例。输出的输出将包含项目和应用的值。
      {
        "friendlychat_web_app_config": {
          "sensitive": false,
          "type": [
            "object",
            {
              "apiKey": "string",
              "appId": "string",
              "authDomain": "string",
              "measurementId": "string",
              "messagingSenderId": "string",
              "projectId": "string",
              "storageBucket": "string"
            }
          ],
          "value": {
            "apiKey": "<API_KEY>",
            "appId": "<APP_ID>",
            "authDomain": "<PROJECT_ID>.firebaseapp.com",
            "measurementId": "<G-MEASUREMENT_ID>",
            "messagingSenderId": "<SENDER_ID>",
            "projectId": "<PROJECT_ID>",
            "storageBucket": "<PROJECT_ID>.appspot.com"
          }
        }
      }
      
  4. value 映射中复制值。
  5. 将这些值(您的配置)粘贴到您应用的 web/src/firebase-config.js 文件中,如下所示:

    firebase-config.js
    ...
    
    const config = {
      apiKey: "<API_KEY>",
      appId: "<APP_ID>",
      authDomain: "<PROJECT_ID>.firebaseapp.com",
      measurementId: "<G-MEASUREMENT_ID>",
      messagingSenderId: "<SENDER_ID>",
      projectId: "<PROJECT_ID>",
      storageBucket: "<PROJECT_ID>.appspot.com",
    };
    
    ...
    

在您的应用中初始化 Firebase

最后,为了初始化 Firebase,请为应用的 web/src/index.js 文件附加以下内容:

index.js

...

const firebaseAppConfig = getFirebaseConfig();
initializeApp(firebaseAppConfig);

试用您的应用

现在,一切都已针对 Firebase 完成配置,接下来您可以试用可正常运行的 Web 应用。

  1. 刷新提供应用的浏览器。
  2. 现在,您应该可以使用 Google 账号登录并开始在聊天中发布消息了。如果您有图片文件,甚至可以将其上传!

11. 跨环境复制配置

Terraform 擅长管理多个配置类似的基础架构(例如,设置与生产项目类似的预演 Firebase 项目)。

在此 Codelab 中,您将创建第二个 Firebase 项目作为预演环境。

如需复制现有配置以创建此预演项目,您有两个选择:

  • 方法 1:复制 Terraform 配置。
    在复制项目与源项目的差异程度方面,此选项提供了极大的灵活性。
  • 方法 2:通过 for_each 重复使用配置。
    如果每个项目不应有显著差异,并且您希望一次性将更改传播到所有项目,则此选项可以实现更多代码重用。

方法 1:复制 Terraform 配置

此选项为复制的项目与源项目的差异提供了极大的灵活性,例如设置具有不同显示名称的应用和分阶段发布应用。

  1. web 目录的根目录下,新建一个名为 main_staging.tf 的 Terraform 配置文件。
  2. 复制 main.tf 文件中的所有资源块(terraformprovider 块除外),然后将其粘贴到 main_staging.tf 文件中。
  3. 然后,您需要修改 main_staging.tf 中的每个复制资源块,以便它们与您的预演项目搭配使用:
    • 资源标签:请使用新名称以避免冲突。例如,将 resource "google_project" "default" 重命名为 resource "google_project" "staging"
    • 资源引用:更新每个资源引用。例如,将 google_firebase_project.default.project 更新为 google_firebase_project.staging.project
    您可以在此 Codelab 的 GitHub 代码库中找到 main_staging.tf 文件的完整配置:

    web/terraform-checkpoints/replicate-config/main_staging-copypaste.tf

    如果您想要使用此配置,请务必执行以下操作:
    1. 复制 main_staging-copypaste.tf 中的配置,然后将其粘贴到 main_staging.tf 文件中。
    2. main_staging.tf 文件中,执行以下操作:
      • google_project 资源块中,使用您自己的值更新 name 属性、project-id 属性,以及(如果您通过 Terraform 设置身份验证)billing_account 属性。
      • google_firebase_web_app 资源块中,使用您自己的值更新 display_name 属性。
      • google_firestore_databasegoogle_app_engine_application 资源块中,使用您自己的值更新 location_id 属性。
    main_staging.tf
    # Create a new Google Cloud project.
    resource "google_project" "staging" {
      provider = google-beta.no_user_project_override
    
      name            = "<PROJECT_NAME_OF_STAGING_PROJECT>"
      project_id      = "<PROJECT_ID_OF_STAGING_PROJECT"
      # Required if you want to set up Authentication via Terraform
      billing_account = "<YOUR_BILLING_ACCOUNT_ID>"
    
      # Required for the project to display in any list of Firebase projects.
      labels = {
        "firebase" = "enabled"
      }
    }
    
    # Enable the required underlying Service Usage API.
    resource "google_project_service" "staging_serviceusage" {
      provider = google-beta.no_user_project_override
    
      project = google_project.staging.project_id
      service = "serviceusage.googleapis.com"
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    # Enable the required underlying Firebase Management API.
    resource "google_project_service" "staging_firebase" {
      provider = google-beta.no_user_project_override
    
      project = google_project.staging.project_id
      service = "firebase.googleapis.com"
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    # Enable Firebase services for the new project created above.
    resource "google_firebase_project" "staging" {
      provider = google-beta
    
      project = google_project.staging.project_id
    
      # Wait until the required APIs are enabled.
      depends_on = [
        google_project_service.staging_serviceusage,
        google_project_service.staging_firebase,
      ]
    }
    
    # Create a Firebase Web App in the new project created above.
    resource "google_firebase_web_app" "staging" {
      provider = google-beta
    
      project      = google_firebase_project.staging.project
      display_name = "<DISPLAY_NAME_OF_YOUR_WEB_APP>"
      deletion_policy = "DELETE"
    }
    
  4. 运行 terraform apply 以预配新的“预演”Firebase 项目及其所有资源,并启用其服务。
  5. 像以前一样在 Firebase 控制台中检查各项内容,以验证是否按预期预配和启用所有内容。

方法 2:通过 for_each 重复使用配置

如果各个项目不应出现显著差异,并且您希望一次性将更改传播到所有项目,则此选项可以实现更高的代码重用程度。它使用 Terraform 语言中的 for_each 元参数。

  1. 打开 main.tf 文件。
  2. 在您要复制的每个资源块中,添加一个 for_each 元参数,如下所示:

    main.tf
    # Create new Google Cloud projects.
    resource "google_project" "default" {
      provider        = google-beta.no_user_project_override
      name            = each.value
      # Create a unique project ID for each project, with each ID starting with <PROJECT_ID>.
      project_id      = "<PROJECT_ID>-${each.key}"
      # Required if you want to set up Authentication via Terraform
      billing_account = "<YOUR_BILLING_ACCOUNT_ID>"
    
      # Required for the projects to display in any list of Firebase projects.
      labels = {
        "firebase" = "enabled"
      }
    
      for_each = {
        prod    = "<PROJECT_NAME_OF_PROD_PROJECT>"
        staging = "<PROJECT_NAME_OF_STAGING_PROJECT>"
      }
    }
    
    # Enable the required underlying Service Usage API.
    resource "google_project_service" "serviceusage" {
      provider = google-beta.no_user_project_override
      for_each = google_project.default
    
      project = each.value.project_id
      service = "serviceusage.googleapis.com"
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    # Enable the required underlying Firebase Management API.
    resource "google_project_service" "firebase" {
      provider = google-beta.no_user_project_override
      for_each = google_project.default
    
      project = each.value.project_id
      service = "firebase.googleapis.com"
    
      # Don't disable the service if the resource block is removed by accident.
      disable_on_destroy = false
    }
    
    # Enable Firebase services for each of the new projects created above.
    resource "google_firebase_project" "default" {
      provider = google-beta
      for_each = google_project.default
    
      project = each.value.project_id
    
      depends_on = [
        google_project_service.serviceusage,
        google_project_service.firebase,
      ]
    }
    
    # Create a Firebase Web App in each of the new projects created above.
    resource "google_firebase_web_app" "default" {
      provider = google-beta
      for_each = google_firebase_project.default
    
      project      = each.value.project
      # The Firebase Web App created in each project will have the same display name.
      display_name = "<DISPLAY_NAME_OF_YOUR_WEB_APP>"
      deletion_policy = "DELETE"
    }
    
    
    # NOTE: For this codelab, we recommend setting up Firebase Authentication
    # using the Firebase console. However, if you set up Firebase Authentication
    # using Terraform, copy-paste from your main.tf the applicable blocks.
    # Make sure to add the `for_each` meta-argument into each block.
    
    
    # Copy-paste from your main.tf file the applicable resource blocks
    # for setting up Cloud Firestore (including rules) and
    # for setting up Cloud Storage for Firebase (including rules).
    # Make sure to add the `for_each` meta-argument into each block.
    
    您可以在此 Codelab 的 GitHub 代码库中找到使用 for_each 元参数的 main.tf 文件的完整配置:

    web/terraform-checkpoints/replicate-config/main-foreach.tf

    如果您想要使用此配置,请确保执行以下操作:
    1. 复制 main-foreach.tf 中的配置,然后将其粘贴到 main.tf 文件中。
    2. main.tf 文件中,执行以下操作:
      • google_project 资源块中,使用您自己的值更新 name 属性、project-id 属性,以及(如果您通过 Terraform 设置身份验证)billing_account 属性。
      • google_firebase_web_app 资源块中,使用您自己的值更新 display_name 属性。
      • google_firestore_databasegoogle_app_engine_application 资源块中,使用您自己的值更新 location_id 属性。
  3. 请务必了解并修正一些关于 Terraform 如何解读此配置(相对于现有基础架构)的问题,而不是立即应用此配置。
    1. 现在,如果应用此使用 for_each 的配置,资源地址将如下所示:
      google_project.default["prod"]
      google_project.default["staging"]
      google_firebase_project.default["prod"]
      google_firebase_project.default["staging"]
      google_firebase_web_app.default["prod"]
      google_firebase_web_app.default["staging"]
      
      不过,Terraform 已知您在此 Codelab 的第一部分中创建的现有项目,如下所示:
      google_project.default
      google_firebase_project.default
      google_firebase_android_app.default
      
    2. 运行 terraform plan,看看 Terraform 在当前状态下会执行哪些操作。

      输出结果应显示 Terraform 会删除您在此 Codelab 的第一部分中创建的项目,并创建两个新项目。这是因为 Terraform 不知道地址 google_project.default 的项目已移到新地址 google_project.default["prod"]
    3. 如需解决此问题,请运行 terraform state mv 命令:
      terraform state mv "google_project.default" "google_project.default[\"prod\"]"
      
    4. 同样,如需修复所有其他资源块,请针对 google_firebase_projectgoogle_firebase_web_app 以及 main.tf 文件中的所有其他资源块运行 terraform state mv
    5. 现在,如果您再次运行 terraform plan,系统应该不会再显示 Terraform 会删除您在此 Codelab 的第一部分中创建的项目。
  4. 运行 terraform apply 以预配新的“预演”Firebase 项目及其所有资源,并启用其服务。
  5. 像以前一样在 Firebase 控制台中检查各项内容,以验证是否按预期预配和启用所有内容。

12. 额外步骤:部署预演应用和正式版应用

  1. 在应用的代码库中,更改 firebase-config.js 以改用预演项目中的 Firebase 配置。

    如需提醒自己如何获取 Firebase 配置并将其添加到您的应用,请参阅此 Codelab 的上一步“将 Firebase 配置添加到您的应用”。
  2. web 目录的根目录下,运行以下命令,将您的应用部署到 Firebase 预演项目。
    firebase deploy --only hosting --project=<STAGING_PROJECT_ID>
    
  3. 通过 firebase deploy 输出中输出的网址,在浏览器中打开预演应用。请尝试登录账号、发送消息和上传图片。

    将应用部署到 Firebase 项目时,该应用会使用真实的 Firebase 资源,而不是模拟资源。与预演应用交互时,您应该会在 Firebase 控制台的预演项目中看到数据和图片。
  4. 在预演阶段测试应用后,将 firebase-config.js 更改回使用生产项目的 Firebase 配置(您在此 Codelab 中创建的第一个项目)。
  5. web 目录的根目录下,运行以下命令,将您的应用部署到正式版 Firebase 项目。
    firebase deploy --only hosting --project=<PRODUCTION_PROJECT_ID>
    
  6. 通过 firebase deploy 输出中输出的网址,在浏览器中打开您的正式版应用。请尝试登录、发送消息和上传图片。

    您应该会在 Firebase 控制台的生产项目中看到数据和图片。
  7. 完成与此 Codelab 的两个应用的交互后,您可以阻止 Firebase 为它们提供服务。对您的每个项目运行以下命令:
    firebase hosting:disable --project=<STAGING_PROJECT_ID>
    
    firebase hosting:disable --project=<PRODUCTION_PROJECT_ID>
    

13. 恭喜!

您已使用 Terraform 配置了一个实时聊天 Web 应用!您遵循了开发环境的最佳实践,创建了单独的用于预演和生产环境的 Firebase 项目。

所学内容

  • 使用 Terraform CLI 管理云资源
  • 使用 Terraform 配置 Firebase 产品(Authentication、Firestore、Cloud Storage 和安全规则)
  • 使用 Firebase Local Emulator Suite 在本地运行和测试 Web 应用
  • 将 Firebase 导入 Web 应用
  • 使用 Terraform 在多个环境中复制配置

如需详细了解 Firebase 和 Terraform,请参阅我们的文档。您可以在其中找到支持 Terraform 的所有 Firebase 产品的列表、适用于常见使用场景的 Terraform 配置示例,以及有用的问题排查信息和常见问题解答。