Terraform を使用して Firebase プロジェクトとプロダクトを設定して管理する

1. はじめに

目標

Terraform を使用すると、Firebase プロジェクトを設定および管理できます。インフラストラクチャや Firebase プロダクトのプログラムによる構成も可能です。

この Codelab では、まず Terraform 構成ファイルをビルドして新しい Firebase プロジェクトを作成する方法について説明します。次に、そのプロジェクトで使用するアプリと Firebase プロダクトを構成する方法について説明します。また、変更のプレビューや実装など、Terraform コマンドラインの基本についても説明します。

Terraform を使用して Firebase プロジェクトとプロダクトを設定して管理する方法を学びたい場合は、この Codelab がおすすめです。

学習内容

  • Terraform 構成ファイルの作成方法(*.tf
  • Terraform CLI コマンドを使用してインフラストラクチャを管理する方法
  • 構成を変更してリソースとサービスを更新する方法
  • 実際のウェブアプリ(フレンドリー チャット)に構成を適用する方法
  • さまざまな環境(本番環境、ステージング環境など)で並列(同期)構成を定義する方法

必要なもの

この Codelab を効果的に進めるには、Terraform とその用語について基本的な知識が必要です。前提条件は次のとおりです。

この Codelab では、実際のサンプルアプリを提供します。これにより、Terraform でプロビジョニングした内容をテストして操作できます。そのためには、次のものが必要です。

  • ウェブアプリのサンプルコード - Codelab の次のステップでこのコードをダウンロードします。
  • パッケージ マネージャー npm(通常は Node.js に付属しています)- これらのツールをインストールします。
  • Firebase CLI - この CLI をインストールしてログインする

2. 開始用コードを取得する

この Codelab では、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 プロバイダには、Terraform のオペレーションの割り当てチェック方法を決定する user_project_override という属性があります。ほとんどのリソースのプロビジョニングで user_project_override = true の使用を推奨します。これを使用すると、Firebase プロジェクトの割り当てがチェックされます。ただし、新しいプロジェクトをセットアップする際に割り当てチェックが行われるようにするには、最初に user_project_override=false を使用する必要があります。Terraform の alias 構文を使用すると、この Codelab の次のステップで 2 つのプロバイダの設定を区別できます。

ディレクトリで 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)を有効にする必要があります。

    通常、この API の有効化は、Firebase コンソールを使用して Firebase プロジェクトを作成するときにバックグラウンドで処理されますが、この有効化を行うには 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 句があります。これは、基盤となる API が有効になるまで待機するよう Terraform に指示します。この句がないと、Terraform は依存関係を認識しないため、リソースを並行してプロビジョニングするときにエラーが発生する可能性があります。

構成を適用する

  1. 新しいリソースをプロビジョニングし、構成ファイルで指定された API を有効にするには、main.tf ファイル(web にする必要があります)と同じディレクトリのルートから次のコマンドを実行します。
    terraform apply
    
  2. ターミナルに、実行されるアクションのプランが表示されます。

    すべてが想定どおりであれば、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 でプロビジョニングしたリソースをテストし、操作します。このアプリはウェブアプリであるため、新しく作成した Firebase プロジェクトに Firebase ウェブアプリを登録するように Terraform に指示する必要があります。

ブロックを追加してウェブアプリを登録する

Firebase プロジェクトにウェブアプリを登録するには、main.tf ファイルに次のリソース ブロックを追加します。

ウェブアプリには独自の 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 アカウントでウェブアプリにログインできるようにするには、Firebase Authentication を有効にして、「Google でログイン」方式を設定します。

この Codelab では、Firebase Authentication を設定するための 2 つのオプションを用意しています。

  • オプション 1(推奨): コンソールで Firebase Authentication を設定します。GCIP は必要ありません。
    • このオプションを使用すると、新しいプロジェクトを Cloud 請求先アカウントに関連付ける必要はありません
  • オプション 2: Google Cloud Identity Platform(GCIP)API を使用して、Terraform で Firebase Authentication を設定する。
    • このオプションを使用する場合、GCIP ではプロジェクトに Blaze お支払いプランが適用されている必要があるため、新しいプロジェクトを Cloud 請求先アカウントに関連付ける必要があります。

オプション 1: Firebase コンソールを使用して認証を設定する

Firebase コンソールを使用して Firebase Authentication を設定するには、プロジェクトに Blaze 料金プランを適用している必要はありません。

Firebase Authentication を設定して Google でログインする方法は次のとおりです。

  1. Firebase コンソールで、左側のパネルにある [ビルド] セクションを見つけます。
  2. [Authentication]、[開始] の順にクリックし、[ログイン方法] タブをクリックします(または、こちらをクリックして直接移動します)。
  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 料金プランを適用している必要があります。Firebase プロジェクトを Blaze プランを使用するようにアップグレードするには、プロジェクトに Cloud 請求先アカウントを関連付けます。

Terraform で課金を有効にする

  1. Cloud 請求先アカウントをまだお持ちでない場合は、まず 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. GCIP を使用して Firebase Authentication をプロビジョニングするには、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 クライアントが必要です。この設定を行うには、Google Cloud コンソールの [API とサービス] セクションに移動します。
  3. このプロジェクトでクライアント ID を作成するのは今回が初めてであるため、OAuth 同意画面を構成する必要があります。
    1. OAuth 同意画面ページを開き、作成したプロジェクトを選択します。
    2. [ユーザーの種類] を [外部] に設定し、[作成] をクリックします。
    3. 次の画面で以下の手順に沿って操作し、[保存して次へ] をクリックします。
      • アプリの一般公開される [App name] を FriendlyChat などに設定します(グローバルに一意である必要はありません)。
      • プルダウン メニューから [ユーザー サポートのメールアドレス] を選択します。
      • [デベロッパーの連絡先情報] にメールアドレスを入力します。
    4. 次の画面で、次の操作を行います。
      • [スコープ] ページでデフォルト値をそのまま使用して、[保存して次へ] をクリックします。
      • [テストユーザー] ページでデフォルト値をそのままにして、[保存して次へ] をクリックします。
      • 概要を確認し、[ダッシュボードに戻る] をクリックします。
      Google Cloud コンソールを使用して OAuth2 クライアントを構成する
  4. [認証情報] ページで OAuth クライアントを設定します。手順は次のとおりです。
    1. [認証情報を作成] をクリックし、[OAuth クライアント ID] を選択します。
    2. [アプリケーションの種類] プルダウンから [ウェブ アプリケーション] を選択します。
    3. [名前] フィールドに、アプリの名前(FriendlyChat など)を入力します(グローバルに一意である必要はありません)。
    4. アプリの URL でこの 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. 構成に従って認証を設定するには、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 コンソールの左側のパネルで、[Build] セクションを見つけます。
  2. [認証] をクリックし、[ログイン方法] タブをクリックします(または、ここをクリックして [ログイン方法] タブに直接移動します)。
  3. 有効なログイン プロバイダとして Google が表示されます。Firebase コンソールの [Authentication] ページ: Google ログインが有効になっている

7. Firestore データベースとそのセキュリティ ルールを設定する

この Codelab のウェブアプリでは、エンドユーザー間のメッセージを Firestore データベースに保存します。

  1. 必要な API を有効にしてデータベース インスタンスをプロビジョニングするには、次の resource ブロックを 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> をデータベースを配置するリージョンに変更します。

    本番環境アプリを開発する場合は、これをユーザーに近いリージョンに配置し、Cloud Functions などの他の Firebase サービスとは共通ではないようにします。この 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 Database] セクションに移動し、[ルール] タブをクリックします。
    Firebase コンソールを使用して Cloud Firestore ルールを確認する

8. Cloud Storage バケットとそのセキュリティ ルールを設定する

この Codelab のウェブアプリでは、エンドユーザー間で共有される画像を 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 コンソールで [公開] ボタンをクリックするか、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. アプリをローカルで実行する

これで、ウェブアプリを初めて実行する準備ができました。Firebase Hosting エミュレータを使用して、アプリをローカルで提供します。

  1. 新しいターミナル ウィンドウを開き、web ディレクトリから次の Firebase CLI コマンドを実行してエミュレータを起動します。
    firebase emulators:start --project=<PROJECT_ID>
    
  2. ブラウザで、CLI から返されたローカル URL(通常は http://localhost:5000)でウェブアプリを開きます。

フレンドリー チャット アプリの UI が表示されますが、これはまだ機能していません。アプリはまだ Firebase に接続されていませんが、この Codelab の次のステップを完了すると接続されます。

ウェブアプリに変更を加えるたびに(この Codelab の次の手順で行うように)、ブラウザを更新して、ローカル URL に変更を適用してください。

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 SDK が使用する Firebase プロジェクトを認識できるように、Firebase 構成をアプリに追加する必要があります。

この Codelab では、Firebase 構成を取得する方法として次の 2 つがあります。

  • オプション 1: Firebase コンソールから Firebase 構成を取得します。
  • オプション 2: Terraform を使用して Firebase 構成を取得します。

オプション 1: Firebase コンソールから構成を取得してコードベースに追加する

  1. Firebase コンソールで [プロジェクトの設定] に移動します。
  2. [マイアプリ] カードまで下にスクロールし、ウェブアプリを選択します。
  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 を介して構成を取得し、コードベースに追加する

また、CLI で Terraform を介して Firebase 構成を出力値として取得することもできます。

  1. main.tf ファイルで、google_firebase_web_app リソース ブロック(プロジェクトにウェブアプリを登録したブロック)を見つけます。
  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. ウェブアプリの 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 の設定がすべて完了しました。機能するウェブアプリを試してみましょう。

  1. アプリを提供しているブラウザを更新します。
  2. これで、Google でログインしてチャットにメッセージを投稿できるようになります。画像ファイルがある場合は、アップロードすることもできます。

11. 環境間で構成を複製する

Terraform は、同じように構成した複数のインフラストラクチャの管理(本番環境プロジェクトに似たステージング Firebase プロジェクトの設定など)を得意としています。

この Codelab では、ステージング環境として 2 つ目の Firebase プロジェクトを作成します。

既存の構成を複製してこのステージング プロジェクトを作成する方法は 2 つあります。

  • オプション 1: Terraform 構成のコピーを作成します。
    このオプションでは、複製されたプロジェクトをソース プロジェクトからどの程度変更できるかについて、最も柔軟な設定が可能です。
  • オプション 2: for_each を使用して構成を再利用する。
    各プロジェクトに大きな違いがなく、すべてのプロジェクトに一度に変更を適用したい場合は、このオプションを使用してより多くのコード再利用を行うことができます。

オプション 1: Terraform 構成のコピーを作成する

このオプションを使用すると、アプリの表示名や段階的な公開など、複製されたプロジェクトとソース プロジェクトとの相違点を柔軟に行うことができます。

  1. web ディレクトリのルートに、main_staging.tf という名前の新しい Terraform 構成ファイルを作成します。
  2. main.tf ファイルからすべてのリソース ブロック(terraform ブロックと provider ブロックを除く)をコピーして、main_staging.tf ファイルに貼り付けます。
  3. 次に、ステージング プロジェクトで機能するように、main_staging.tf 内の複製された各リソースブロックを変更する必要があります。
    • リソースラベル: 競合を避けるため、新しい名前を使用します。たとえば、resource "google_project" "default" の名前を resource "google_project" "staging" に変更します。
    • リソースの参照: 各項目を更新します。たとえば、google_firebase_project.default.projectgoogle_firebase_project.staging.project に更新します。
    main_staging.tf ファイルの完全な構成は、この Codelab の GitHub リポジトリにあります。

    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_database リソース ブロックと google_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.
    
    for_each メタ引数を使用する main.tf ファイルの完全な構成は、この Codelab の GitHub リポジトリで確認できます。

    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_database リソース ブロックと google_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"]
      
      ただし、この Codelab の最初のパートで作成した既存のプロジェクトは、Terraform で次のように認識されます。
      google_project.default
      google_firebase_project.default
      google_firebase_android_app.default
      
    2. terraform plan を実行して、現在の状態で Terraform が実行するアクションを確認します。

      出力には、この Codelab の前半で作成したプロジェクトが削除され、2 つの新しいプロジェクトが作成されることが示されます。これは、アドレス google_project.default のプロジェクトが新しいアドレス google_project.default["prod"] に移動したことを Terraform が認識していないためです。
    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 を再度実行しても、この Codelab の最初のパートで作成したプロジェクトが Terraform によって削除されることを示すものではありません。
  4. terraform apply を実行して、新しい「staging」をプロビジョニングします。Firebase プロジェクトとそのすべてのリソースを作成し、そのサービスを有効にします。
  5. 前と同様に Firebase コンソールで確認し、すべてが想定どおりにプロビジョニングされ、有効になっていることを確認します。

12. ボーナス ステップ: ステージング アプリと本番環境アプリをデプロイする

  1. アプリのコードベースで、ステージング プロジェクトの Firebase 構成を使用するように firebase-config.js を変更します。

    Firebase 構成を取得してアプリに追加する方法については、この Codelab の前のステップ「Firebase 構成をアプリに追加する」をご覧ください。
  2. web ディレクトリのルートで次のコマンドを実行して、アプリをステージング Firebase プロジェクトにデプロイします。
    firebase deploy --only hosting --project=<STAGING_PROJECT_ID>
    
  3. firebase deploy の出力に出力された URL を使用して、ブラウザでステージング アプリを開きます。ログイン、メッセージの送信、画像のアップロードを試します。

    アプリを Firebase プロジェクトにデプロイすると、エミュレートされたリソースではなく、実際の Firebase リソースが使用されます。ステージング アプリを操作すると、Firebase コンソールのステージング プロジェクトにデータと画像が表示されます。
  4. ステージング環境でアプリをテストしたら、firebase-config.js を変更して、本番環境プロジェクトの Firebase 構成(この Codelab で最初に作成したプロジェクト)を使用するようにします。
  5. web ディレクトリのルートで次のコマンドを実行して、アプリを本番環境の Firebase プロジェクトにデプロイします。
    firebase deploy --only hosting --project=<PRODUCTION_PROJECT_ID>
    
  6. firebase deploy の出力に表示された URL を使用して、ブラウザで本番環境アプリを開きます。ログイン、メッセージの送信、画像のアップロードなどをお試しください。

    Firebase コンソールに本番環境プロジェクトのデータと画像が表示されます。
  7. この Codelab で 2 つのアプリを操作し終えたら、Firebase でこれらのアプリの配信を停止できます。プロジェクトごとに次のコマンドを実行します。
    firebase hosting:disable --project=<STAGING_PROJECT_ID>
    
    firebase hosting:disable --project=<PRODUCTION_PROJECT_ID>
    

13. 完了

Terraform を使用してリアルタイム チャット ウェブ アプリケーションを構成しました。また、ステージング環境と本番環境用に個別の Firebase プロジェクトを作成することで、開発環境のベスト プラクティスに従っています。

学習した内容

  • Terraform CLI を使用してクラウド リソースを管理する
  • Terraform を使用して Firebase プロダクト(Authentication、Firestore、Cloud Storage、セキュリティ ルール)を構成する
  • Firebase Local Emulator Suite を使用してウェブアプリをローカルで実行してテストする
  • Firebase をウェブアプリにインポートする
  • Terraform を使用して複数の環境に構成を複製する

Firebase と Terraform の詳細については、ドキュメントをご覧ください。Terraform をサポートしているすべての Firebase プロダクトのリスト、一般的なユースケース向けの Terraform 構成のサンプル、役立つトラブルシューティングとよくある質問をご覧いただけます。