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

1. 简介

目标

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

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

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

学习内容

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

您需要满足的条件

若要顺利完成此 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
    }
    
    通过启用 Service Usage API,您的新项目将能够接受配额检查!因此,对于所有后续资源预配和服务启用,您应该将提供程序与 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 FriendlyChat 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 控制台中,找到左侧面板中的 Build 部分。
  2. 依次点击身份验证开始使用登录方法标签页(或点击此处直接前往该标签页)。
  3. 点击添加新的提供商,然后从其他提供商部分选择 Google
  4. 开启启用切换开关。
  5. 将应用的公开名称设置为 FriendlyChat 之类的名称(此名称不需要是全局唯一的)。
  6. 从下拉菜单中选择项目支持电子邮件地址,然后点击保存在 Firebase 控制台中配置 Firebase 身份验证
  7. 您应该会看到 Google 作为已启用的登录服务提供方。Firebase 控制台“Authentication”(身份验证)页面:已启用 Google 登录

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

若要通过 Terraform 设置 Firebase Authentication,您必须使用 GCIP API,这意味着项目必须采用 Blaze 定价方案。您可以将 Firebase 项目与 Cloud Billing 账号关联,从而将其升级为使用 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. 若要启用“使用 Google 账号登录”,您需要拥有 OAuth 客户端。转到API 和Google Cloud 控制台的“服务”部分,以执行此设置。
  3. 由于这是您首次为此项目创建客户端 ID,因此您需要配置 OAuth 同意屏幕。
    1. 打开 OAuth 同意屏幕页面,然后选择您刚刚创建的项目。
    2. 用户类型设置为外部,然后点击创建
    3. 在下一个屏幕中,填写以下内容,然后点击保存并继续
      • 将应用的面向公众的应用名称设置为 FriendlyChat 之类的名称(此名称不需要是全局唯一的)。
      • 从下拉菜单中选择用户支持电子邮件地址
      • 开发者联系信息中输入电子邮件地址。
    4. 在随后显示的屏幕中,完成以下操作:
      • 范围页面上接受默认值,然后点击保存并继续
      • 测试用户页面上接受默认设置,然后点击保存并继续
      • 查看摘要,然后点击返回信息中心
      使用 Google Cloud 控制台配置 OAuth2 客户端
  4. 凭据页面中设置 OAuth 客户端,具体方法如下:
    1. 点击创建凭据并选择 OAuth 客户端 ID
    2. 应用类型下拉菜单中,选择网页应用
    3. 名称字段中,输入应用的名称,例如 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 控制台的“身份验证”页面:已启用 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 控制台中的 Publish 按钮或运行 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 控制台中,找到左侧面板中的 Build 部分。
    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 安全规则的规则集。
    • 释放 Storage 存储分区的规则集。
    请注意,这些资源块的作用相当于点击 Firebase 控制台中的 Publish 按钮或运行 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 控制台中,找到左侧面板中的 Build 部分。
    2. 转到存储部分,然后点击规则标签页。
    使用 Firebase 控制台验证安全规则

9. 在本地运行应用

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

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

您应该会看到 FriendlyChat 应用的界面(尚未!)正常运行。该应用尚未连接到 Firebase,但完成此 Codelab 的后续步骤后,便可以连接到 Firebase!

请注意,每当您更改 Web 应用(如本 Codelab 后续步骤中所述)时,都需要刷新浏览器,以便使用这些更改更新本地网址。

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

为了让应用能够与 Firebase 集成,您的应用需要 Firebase SDK 以及 Firebase 项目的 Firebase 配置。

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

虽然示例代码已基本完成,但您仍需要执行一些操作才能运行应用,包括:安装 Firebase SDK、启动 build、将 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 擅长管理多个配置类似的基础架构(例如,设置与 prod 项目类似的 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 属性以及 billing_account 属性(如果您通过 Terraform 设置 Authentication)。
      • 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 属性以及 billing_account 属性(如果您通过 Terraform 设置 Authentication)。
      • 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. 同样,如需修复所有其他资源块,请针对 main.tf 文件中的 google_firebase_projectgoogle_firebase_web_app 和所有其他资源块运行 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 配置示例,以及实用的问题排查和常见问题解答。