Terraform 및 Firebase 시작하기

Firebase에서 Terraform을 지원하기 시작했습니다. 특정 리소스로 프로비저닝하고 서비스를 사용 설정한 Firebase 프로젝트 생성을 자동화하고 표준화하려는 팀에 속한 경우 Firebase에서 Terraform을 사용하는 것이 좋습니다.

Firebase에서 Terraform을 사용하기 위한 기본 워크플로에는 다음이 포함됩니다.

  • 프로비저닝할 인프라(즉, 프로비저닝할 리소스 및 사용 설정할 서비스)를 지정하는 Terraform 구성 파일(.tf 파일)을 만들고 맞춤설정합니다.

  • Terraform과 상호작용하는 gcloud CLI 명령어를 사용하면 .tf 파일에 지정된 인프라가 프로비저닝됩니다.

Terraform과 Firebase로 무엇을 할 수 있나요?

이 가이드의 일반화된 워크플로 예시는 Android 앱을 사용하여 새 Firebase 프로젝트를 만드는 것입니다. 하지만 Terraform으로 다음과 같은 더 많은 작업을 할 수 있습니다.

  • Terraform을 사용하여 기존 인프라를 삭제하고 수정합니다.

  • Terraform을 사용하여 다음과 같은 제품별 구성 및 작업을 관리합니다.

    • Firebase Authentication 로그인 제공업체 사용 설정
    • Cloud Storage 버킷 또는 데이터베이스 인스턴스 만들기 및 이를 위한 Firebase Security Rules 배포

표준 Terraform 구성 파일과 명령어를 사용하여 이러한 모든 작업을 수행할 수 있습니다. 이를 지원하기 위해 몇 가지 일반적인 사용 사례에 관한 샘플 Terraform 구성 파일을 제공했습니다.



Firebase에서 Terraform을 사용하기 위한 일반화된 워크플로

기본 요건

이 가이드에서는 Firebase에서 Terraform을 사용하는 방법을 소개하므로 Terraform에 대한 기본 숙련도를 갖춘 것을 전제로 합니다. 이 워크플로를 시작하기 전에 다음 기본 요건을 완료해야 합니다.

  • Terraform을 설치하고 공식 튜토리얼을 따라 Terraform을 숙지하세요.

  • Google Cloud CLI(gcloud CLI)를 설치합니다. 사용자 계정 또는 서비스 계정을 사용하여 로그인하세요.

    • 사용자 계정을 사용하는 경우 Firebase 서비스 약관에 동의해야 합니다. Firebase 콘솔에서 Firebase 프로젝트를 볼 수 있는 경우 Firebase 서비스 약관에 동의한 것입니다.
    • Terraform이 특정 작업(예: 프로젝트 만들기)을 실행하려면 다음 조건을 충족해야 합니다.
      • 사용자 또는 서비스 계정에 이러한 작업과 관련된 IAM 액세스 권한이 있어야 합니다.
      • 사용자 또는 서비스 계정이 Google Cloud 조직의 일부인 경우 조직 정책이 계정에서 해당 작업을 수행할 수 있도록 허용해야 합니다.


1단계: Terraform 구성 파일 만들기 및 맞춤설정

Terraform 구성 파일에는 두 가지 기본 섹션이 필요합니다(아래에 자세히 설명되어 있음).

provider 설정

provider 설정은 관련된 Firebase 제품 또는 서비스와 관계없이 필요합니다.

  1. 로컬 디렉터리에 Terraform 구성 파일(예: main.tf 파일)을 만듭니다.

    이 가이드에서는 이 구성 파일을 사용하여 provider 설정과 Terraform에서 만들려는 모든 인프라를 지정합니다. 그러나 제공업체 설정을 포함하는 방법에 관한 옵션이 있습니다.

    나머지 Terraform 구성에 provider 설정을 포함하는 방법에 관한 옵션은 다음과 같습니다.

    • 옵션 1: 단일 Terraform .tf 구성 파일 상단에 포함합니다(이 가이드 참조).

      • Terraform을 처음 시작하거나 Firebase에서 Terraform을 사용해 보려는 경우 이 옵션을 사용하세요.
    • 옵션 2: 생성할 인프라를 지정하는 .tf 파일(예: main.tf 파일) 외에 별도의 .tf 파일(예: provider.tf 파일)에 포함합니다.

      • 설정을 표준화해야 하는 대규모 팀에 속한 경우 이 옵션을 사용합니다.
      • Terraform 명령어를 실행할 때 provider.tf 파일과 main.tf 파일이 모두 같은 디렉터리에 있어야 합니다.

  2. main.tf 파일 상단에 다음 provider 설정을 포함합니다.

    Firebase에서 Terraform을 사용하는 베타 버전이므로 google-beta 제공업체를 사용해야 합니다. 프로덕션에서 사용할 때는 주의해야 합니다.

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

    Firebase에서 Terraform을 사용할 때 프로젝트 관련 속성 유형(이 가이드에서 언급된 '할당량 확인 프로젝트' 포함)에 대해 자세히 알아보세요.

  3. 다음 섹션으로 이동하여 구성 파일을 작성하고 생성할 인프라를 지정합니다.

resource 블록을 사용하여 만들 인프라 지정

Terraform 구성 파일(이 가이드의 경우 main.tf 파일)에서 Terraform에서 만들려는 모든 인프라(프로비저닝할 모든 리소스 및 사용 설정할 모든 서비스)를 지정해야 합니다. 이 가이드에서 Terraform을 지원하는 모든 Firebase 리소스의 전체 목록을 확인하세요.

  1. main.tf 파일을 엽니다.

  2. provider 설정에서 다음 resource 블록 구성을 포함합니다.

    이 기본 예시에서는 새 Firebase 프로젝트를 만든 다음 해당 프로젝트 내에 Firebase Android 앱을 만듭니다.

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

리소스로써의 프로젝트 및 앱 인프라에 관해 잘 모른다면 다음 문서를 검토하세요.

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

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

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

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

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

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

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

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

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

  project  = google_project.default.project_id

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

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

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

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


2단계: Terraform 명령어를 실행하여 지정한 인프라 만들기

main.tf 파일에 지정된 리소스를 프로비저닝하고 서비스를 사용 설정하려면 main.tf 파일과 동일한 디렉터리에서 다음 명령어를 실행합니다. 이러한 명령어에 대한 자세한 내용은 Terraform 문서를 참조하세요.

  1. 디렉터리에서 Terraform 명령어를 처음 실행하는 경우 구성 디렉터리를 초기화하고 Google Terraform 제공업체를 설치해야 합니다. 다음 명령어를 실행하면 됩니다.

    terraform init
  2. 다음 명령어를 실행하여 main.tf 파일에 지정된 인프라를 만듭니다.

    terraform apply
  3. 모든 것이 예상대로 프로비저닝되거나 사용 설정되었는지 확인합니다.

    • 옵션 1: 다음 명령어를 실행하여 터미널에 출력된 구성을 확인합니다.

      terraform show
    • 옵션 2: Firebase 콘솔에서 Firebase 프로젝트를 확인합니다.



Terraform을 지원하는 Firebase 리소스

다음 Firebase 및 Google 리소스에 Terraform이 지원됩니다. 계속해서 더 많은 리소스를 추가하고 있습니다. 따라서 Terraform으로 관리하려는 리소스가 표시되지 않으면 나중에 다시 확인하여 리소스를 사용할 수 있는지 확인하거나 GitHub 저장소에서 문제를 제출하여 요청하세요.


Firebase 프로젝트 및 앱 관리

  • google_firebase_project - 기존 Google Cloud 프로젝트에서 Firebase 서비스 사용 설정

  • Firebase 앱


Firebase Authentication

아직 지원되지 않음:

  • Terraform을 통한 다중 인증(MFA) 구성

Firebase Realtime Database

아직 지원되지 않음:

  • Terraform을 통해 Firebase Realtime Database Security Rules 배포(프로그래매틱 옵션을 포함한 다른 도구를 사용하여 Rules를 배포하는 방법 알아보기)

Cloud Firestore

  • google_firestore_databaseCloud Firestore 인스턴스를 만듭니다.

  • google_firestore_indexCloud Firestore에 효율적인 쿼리 사용 설정

  • google_firestore_document — 컬렉션에 특정 문서가 있는 Cloud Firestore 인스턴스 시드

    중요: 이 시드 문서에 실제 최종 사용자 또는 프로덕션 데이터를 사용하지 마세요.


Cloud Storage for Firebase

  • google_firebase_storage_bucket - Firebase SDK, 인증, Firebase Security Rules에서 액세스할 수 있는 기존 Cloud Storage 버킷 만들기

  • google_storage_bucket_objectCloud Storage 버킷에 객체 추가

    중요: 이 파일에 실제 최종 사용자 또는 프로덕션 데이터를 사용하지 마세요.


Firebase Security Rules(Cloud FirestoreCloud Storage에 대한)

참고로, Firebase Realtime DatabaseFirebase Security Rules에 다른 프로비저닝 시스템을 사용합니다.

  • google_firebaserules_ruleset - Cloud Firestore 인스턴스나 Cloud Storage 버킷에 적용되는 Firebase Security Rules 정의

  • google_firebaserules_releaseCloud Firestore 인스턴스나 Cloud Storage 버킷에 특정 규칙 세트 배포


Firebase App Check


Firebase Extensions



일반적인 사용 사례를 위한 샘플 Terraform 구성 파일

이 구성은 새 Google Cloud 프로젝트를 만들고 프로젝트를 Cloud Billing 계정과 연결하고(GCIP에서 Firebase Authentication을 사용하려면 Blaze 요금제 필요) 프로젝트에 Firebase 서비스를 사용 설정하고 GCIP를 사용하여 Firebase Authentication을 설정하며 프로젝트에 세 가지 앱 유형을 등록합니다.

Terraform을 통해 Firebase Authentication을 설정하려면 GCIP를 사용 설정해야 합니다.

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

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

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

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

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

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

  depends_on = [
    google_project_service.auth,
  ]
}

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

  # Auto-deletes anonymous users
  autodelete_anonymous_users = true

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

    anonymous {
      enabled = true
    }

    email {
      enabled = true
      password_required = false
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

이 구성은 새 Google Cloud 프로젝트를 만들고 프로젝트에 Firebase 서비스를 사용 설정하고 프로젝트의 기본 Realtime Database 인스턴스를 프로비저닝하며 프로젝트에 세 가지 앱 유형을 등록합니다.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

이 구성은 새 Google Cloud 프로젝트를 만들고 프로젝트를 Cloud Billing 계정과 연결하고(여러 Realtime Database 인스턴스를 사용하려면 Blaze 요금제 필요) 프로젝트에 Firebase 서비스를 사용 설정하고 여러 Realtime Database 인스턴스(프로젝트의 기본 Realtime Database 인스턴스 포함)를 프로비저닝하며 프로젝트에 세 가지 앱 유형을 등록합니다.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

이 구성은 새 Google Cloud 프로젝트를 만들고 프로젝트에 Firebase 서비스를 사용 설정하고 프로젝트의 기본 Cloud Firestore 인스턴스를 프로비저닝하며 프로젝트에 세 가지 앱 유형을 등록합니다.

또한 기본 Cloud Firestore 인스턴스의 Firebase Security Rules를 프로비저닝하고 Cloud Firestore 색인을 만들며 시드 데이터가 있는 Cloud Firestore 문서를 추가합니다.

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

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

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

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

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

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

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

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

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

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

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

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

  collection  = "quiz"
  query_scope = "COLLECTION"

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

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

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

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

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

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

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

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

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

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

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

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

이는 firestore.rules라는 로컬 파일에 있어야 하는 Cloud Firestore Security Rules의 규칙 세트입니다.

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

이 구성은 새 Google Cloud 프로젝트를 만들고 프로젝트를 Cloud Billing 계정에 연결하고(추가 버킷을 사용하려면 Blaze 요금제 필요) 프로젝트에 Firebase 서비스를 사용 설정하고 기본이 아닌 추가 Cloud Storage 버킷을 프로비저닝하며 프로젝트에 세 가지 앱 유형을 등록합니다.

또한 Cloud Storage 버킷마다 Firebase Security Rules를 프로비저닝하고 파일을 Cloud Storage 버킷 중 하나에 업로드합니다.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

이는 storage.rules라는 로컬 파일에 있어야 하는 Cloud Storage Security Rules의 규칙 세트입니다.

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

이 구성은 새 Google Cloud 프로젝트를 만들고 프로젝트에 Firebase 서비스를 사용 설정하며 Android 앱에서만 액세스할 수 있도록 Cloud FirestoreFirebase App Check 적용을 설정 및 사용 설정합니다.

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

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

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

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

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

  depends_on = [google_project_service.services]
}

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

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

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

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

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

  depends_on = [time_sleep.wait_30s, google_firestore_database.database]

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

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

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

  depends_on = [google_project_service.services]
}

이 구성은 새 Google Cloud 프로젝트를 만들고 프로젝트에 Firebase 서비스를 사용 설정하며 프로젝트에 Firebase Extension의 새 인스턴스를 설치합니다. 인스턴스가 이미 있으면 매개변수가 구성에 제공된 값을 기반으로 업데이트됩니다.

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

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

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

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

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

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

  depends_on = [
    google_project_service.extensions,
  ]
}

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

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

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

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



문제해결 및 FAQ

이 가이드에서는 '프로젝트'를 수행할 때 다음 Terraform 속성을 사용합니다.

resource 블록 내 project

권장: 가능하면 각 resource 블록 내에 project 속성을 포함하세요.

프로젝트 속성을 포함하면 Terraform은 지정된 프로젝트 내의 리소스 블록에 지정된 인프라를 만듭니다. 이 가이드와 샘플 구성 파일은 모두 이 방법을 사용합니다.

project에 대한 공식 Terraform 문서를 참조하세요.

provider 블록 내 user_project_override

대부분의 리소스 프로비저닝은 user_project_override = true를 사용해야 합니다. 즉, 자체 Firebase 프로젝트와 비교하여 할당량을 확인합니다. 그러나 할당량 확인을 허용할 수 있도록 새 프로젝트를 설정하려면 먼저 user_project_override = false를 사용해야 합니다.

user_project_override에 대한 공식 Terraform 문서를 참조하세요.

gcloud CLI 명령어를 실행하는 데 사용하는 사용자 계정이 Firebase 서비스 약관에 동의했는지 확인합니다.

  • 사용자 계정으로 로그인한 브라우저를 사용하고 Firebase 콘솔에서 기존 Firebase 프로젝트를 확인하면 됩니다. 기존 Firebase 프로젝트를 볼 수 있으면 사용자 계정이 Firebase 서비스 약관에 동의한 것입니다.

  • 기존 Firebase 프로젝트를 볼 수 없는 경우 사용자 계정이 Firebase 서비스 약관에 동의하지 않은 것일 수 있습니다. 이 문제를 해결하려면 FirebaseFirebase 콘솔을 통해 새 Firebase 프로젝트를 만들고 프로젝트 생성 과정에서 Firebase 서비스 약관에 동의합니다. 콘솔의 프로젝트 설정에서 이 프로젝트를 즉시 삭제할 수 있습니다.

몇 분 정도 기다린 후 terraform apply를 다시 실행해 봅니다.

이는 여러 시스템의 전파 지연으로 인해 발생할 수 있습니다. terraform import를 실행하여 Terraform 상태로 리소스를 가져와 이 문제를 해결해 보세요. 그런 다음 terraform apply를 다시 실행해 보세요.

Terraform 문서의 '가져오기' 섹션에서 각 리소스를 가져오는 방법을 알아볼 수 있습니다(예: Cloud Cloud Firestoree의 '가져오기' 문서).

오류에서 알 수 있듯이 Terraform은 여러 색인을 프로비저닝하려고 하거나 문서를 동시에 만들려고 하는데 동시 실행 오류가 발생했을 수 있습니다. terraform apply를 다시 실행해 보세요.

Terraform에서 할당량을 확인할 비교 대상 프로젝트를 알 수 없을 때 이 오류가 발생합니다. 문제를 해결하려면 resource 블록에서 다음을 확인합니다.

  • project 속성 값을 지정했는지 확인합니다.
  • Firebase 샘플에서 google-betauser_project_override = true(별칭 없음)가 있는 제공업체를 사용하고 있는지 확인합니다.

프로젝트 ID가 이미 존재할 수 있는 이유는 다음과 같습니다.

  • 해당 ID와 연결된 프로젝트가 다른 사용자의 소유입니다.

    • 해결 방법: 다른 프로젝트 ID를 선택합니다.
  • 이 ID와 연결된 프로젝트가 최근에 삭제되었습니다(소프트 삭제 상태).

    • 해결 방법: ID와 연결된 프로젝트가 본인 소유라고 생각되면 projects.get REST API를 사용하여 프로젝트 상태를 확인하세요.
  • 이 ID와 연결된 프로젝트가 현재 사용자 아래에 올바르게 존재합니다. 이 오류는 이전 terraform apply가 중단된 것이 원인일 수 있습니다.

    • 해결 방법: 다음 명령어를 실행합니다.
      terraform import google_project.default PROJECT_ID 다음
      terraform import google_firebase_project.default PROJECT_ID

기본 Cloud Firestore 인스턴스를 프로비저닝하려고 하기 전에 google_app_engine_application을 통해 기본 Cloud Storage 버킷을 프로비저닝한 경우 기본 Cloud Firestore 인스턴스가 이미 프로비저닝된 것을 확인할 수 있습니다. 프로비저닝된 데이터베이스 인스턴스는 Datastore 모드이므로 Firebase SDK, 인증 또는 Firebase Security Rules에 액세스할 수 없습니다. 이러한 Firebase 서비스와 함께 Cloud Firestore를 사용하려면 데이터베이스를 비운 후 Google Cloud 콘솔에서 데이터베이스 유형을 변경해야 합니다.

google_app_engine_application을 통해 프로젝트의 기본 Cloud Storage 버킷을 프로비저닝하고 프로젝트에 아직 기본 Cloud Firestore 인스턴스가 없으면 google_app_engine_application에서 프로젝트의 기본 Cloud Firestore 인스턴스를 자동으로 프로비저닝합니다.

따라서 프로젝트의 기본 Cloud Firestore 인스턴스가 이미 프로비저닝되었으므로 기본 인스턴스를 다시 명시적으로 프로비저닝하려고 하면 google_firestore_database에서 오류가 발생합니다.

프로젝트의 기본 Cloud Firestore 인스턴스가 프로비저닝되면 '다시 프로비저닝'하거나 위치를 변경할 수 없습니다. 프로비저닝된 데이터베이스 인스턴스는 Datastore 모드이므로 Firebase SDK, 인증 또는 Firebase Security Rules에 액세스할 수 없습니다. 이러한 Firebase 서비스와 함께 Cloud Firestore를 사용하려면 데이터베이스를 비운 후 Google Cloud 콘솔에서 데이터베이스 유형을 변경해야 합니다.