אנחנו מתחילים להוסיף תמיכה ב-Terraform ל-Firebase.
אם אתם עובדים בצוות שרוצה להפוך את תהליך היצירה של פרויקטים ב-Firebase לאוטומטי ולסטנדרטי, עם הקצאה של משאבים ספציפיים והפעלה של שירותים, השימוש ב-Terraform עם Firebase יכול להתאים לכם.
תהליך העבודה הבסיסי לשימוש ב-Terraform עם Firebase כולל את השלבים הבאים:
יצירת קובץ תצורה של Terraform (קובץ .tf) בהתאמה אישית, שמציין את התשתית שרוצים להקצות (כלומר, המשאבים שרוצים להקצות והשירותים שרוצים להפעיל).
באמצעות פקודות gcloud CLI שמתחברות ל-Terraform כדי להקצות את התשתית שצוינה בקובץ .tf.
ניהול הגדרות ומשימות ספציפיות למוצר באמצעות Terraform, כמו:
הפעלת ספקי כניסה של Firebase Authentication.
יצירת קטגוריות או מכונות של מסדי נתונים ב-Cloud Storage ופריסה של Firebase Security Rules בשבילן.
אפשר להשתמש בקובצי תצורה ובפקודות רגילים של Terraform כדי לבצע את כל המשימות האלה. כדי לעזור לכם בכך, סיפקנו דוגמאות לקובצי תצורה של Terraform לכמה תרחישים נפוצים.
תהליך עבודה כללי לשימוש ב-Terraform עם Firebase
דרישות מוקדמות
המדריך הזה הוא מבוא לשימוש ב-Terraform עם Firebase, ולכן הוא מבוסס על היכרות בסיסית עם Terraform. לפני שמתחילים בתהליך העבודה הזה, חשוב לוודא שהתנאים המוקדמים הבאים מתקיימים.
אם אתם משתמשים בחשבון משתמש, עליכם לאשר את התנאים וההגבלות של Firebase. אם אתם יכולים להציג פרויקט Firebase במסוף Firebase, סימן שאישרתם את התנאים וההגבלות של Firebase.
כדי ש-Terraform יבצע פעולות מסוימות (לדוגמה, יצירה של פרויקטים), צריכים להתקיים התנאים הבאים:
לחשבון המשתמש או לחשבון השירות צריכה להיות הרשאת הגישה הרלוונטית ב-IAM לביצוע הפעולות האלה.
אם חשבון המשתמש או חשבון השירות שייכים לארגון Google Cloud, מדיניות הארגון חייבת לאפשר לחשבון לבצע את הפעולות האלה.
שלב 1: יוצרים קובץ תצורה של Terraform ומתאימים אותו אישית
קובץ תצורה של Terraform צריך לכלול שני קטעים עיקריים (שמתוארים בפירוט בהמשך):
צריך להגדיר את provider ללא קשר למוצרים או לשירותים של Firebase שקשורים לפרויקט.
יוצרים קובץ תצורה של Terraform (כמו קובץ main.tf) בספרייה המקומית.
במדריך הזה, תשתמשו בקובץ התצורה הזה כדי לציין גם את ההגדרה של provider וגם את כל התשתית שתרצו ש-Terraform תיצור. עם זאת, חשוב לזכור שיש לכם אפשרויות שונות להכללת הגדרת הספק.
הצגת האפשרויות להכללת ההגדרה של provider
יש לכם את האפשרויות הבאות כדי לכלול הגדרה של provider בשאר התצורה של Terraform:
אפשרות 1: כוללים אותו בחלק העליון של קובץ תצורה יחיד של .tf ב-Terraform (כפי שמתואר במדריך הזה).
כדאי להשתמש באפשרות הזו אם אתם רק מתחילים לעבוד עם Terraform או אם אתם רק רוצים לנסות את Terraform עם Firebase.
אפשרות 2: אפשר לכלול את הפרטים בקובץ .tf נפרד (כמו קובץ provider.tf), בנפרד מהקובץ .tf שבו מציינים את התשתית שרוצים ליצור (כמו קובץ main.tf).
כדאי להשתמש באפשרות הזו אם אתם חלק מצוות גדול יותר שצריך לבצע הגדרה סטנדרטית.
כשמריצים פקודות של Terraform, גם הקובץ provider.tf וגם הקובץ main.tf צריכים להיות באותה ספרייה.
מוסיפים את ההגדרה הבאה של provider בחלק העליון של הקובץ main.tf.
צריך להשתמש בספק google-beta כי זוהי גרסה בטא של השימוש ב-Firebase עם Terraform. חשוב להפעיל שיקול דעת כשמשתמשים ב-Cloud Functions בסביבת ייצור.
# 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
}
ממשיכים לקטע הבא כדי להשלים את קובץ התצורה ולציין את התשתית שרוצים ליצור.
ציון התשתית שרוצים ליצור באמצעות בלוקים של resource
בקובץ התצורה של Terraform (בקובץ main.tf במדריך הזה), צריך לציין את כל התשתית שרוצים ש-Terraform תיצור (כלומר, את כל המשאבים שרוצים להקצות ואת כל השירותים שרוצים להפעיל). במדריך הזה מופיעה רשימה מלאה של כל משאבי Firebase שתומכים ב-Terraform.
פותחים את קובץ ה-main.tf.
בהגדרה של 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.
אם זו הפעם הראשונה שאתם מריצים פקודות של Terraform בספרייה, עליכם לאתחל את ספריית התצורה ולהתקין את הספק של Google Terraform. כדי לעשות זאת, מריצים את הפקודה הבאה:
terraform init
יוצרים את התשתית שצוינה בקובץ main.tf על ידי הפעלת הפקודה הבאה:
terraform apply
מוודאים שכל ההקצאות בוצעו או שהכל הופעל כצפוי:
אפשרות 1: כדי להדפיס את ההגדרות במסוף, מריצים את הפקודה הבאה:
למשאבים הבאים של Firebase ו-Google יש תמיכה ב-Terraform. אנחנו מוסיפים כל הזמן עוד מקורות מידע. לכן, אם לא מצאתם את המשאב שאתם רוצים לנהל באמצעות Terraform, כדאי לבדוק שוב בקרוב אם הוא זמין או לשלוח דיווח על בעיה במאגר GitHub.
google_identity_platform_config – מפעילים את Google Cloud Identity Platform (GCIP) (הקצה העורפי של Firebase Authentication) ומספקים הגדרות אימות ברמת הפרויקט
הפרויקט שבו Terraform תפעיל את GCIP ו/או את Firebase Authentication חייב להיות בתוכנית התמחור Blaze (כלומר, לפרויקט צריך להיות חשבון Cloud Billing משויך). אפשר לעשות זאת באופן פרוגרמטי על ידי הגדרת המאפיין billing_account במשאב google_project.
המשאב הזה מאפשר גם הגדרות נוספות, כמו שיטות כניסה מקומיות, כמו אימות אנונימי, אימות באמצעות אימייל/סיסמה ואימות באמצעות טלפון, וגם פונקציות חסימה ודומיינים מורשים.
ההגדרה הזו יוצרת פרויקט Google Cloud חדש, משייכת את הפרויקט לחשבון Cloud Billing (תוכנית התמחור Blaze נדרשת ל-Firebase Authentication עם GCIP), מפעילה את שירותי Firebase בפרויקט, מגדירה את Firebase Authentication עם GCIP ומרשמת שלושה סוגים שונים של אפליקציות בפרויקט.
חשוב לזכור שצריך להפעיל את GCIP כדי להגדיר את Firebase Authentication דרך Terraform.
# 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,
]
}
הקצאת מכונה Firebase Realtime Database שמוגדרת כברירת מחדל
ההגדרה הזו יוצרת פרויקט 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,
]
}
הקצאת מכונות Firebase Realtime Database מרובות
ההגדרה הזו יוצרת פרויקט Google Cloud חדש, משייכת את הפרויקט לחשבון Cloud Billing (תוכנית התמחור Blaze נדרשת לכמה מכונות Realtime Database), מפעילה את שירותי 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,
]
}
הקצאת מכונה Cloud Firestore שמוגדרת כברירת מחדל
ההגדרה הזו יוצרת פרויקט Google Cloud חדש, מפעילה את שירותי Firebase בפרויקט, מקצה את מכונה Cloud Firestore שמוגדרת כברירת מחדל בפרויקט ומרשמת שלושה סוגים שונים של אפליקציות בפרויקט.
הוא גם מקצה את Firebase Security Rules למכונה Cloud Firestore שמוגדרת כברירת מחדל, יוצר אינדקס 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,
]
}
זוהי מערכת הכללים של Cloud Firestore Security Rules, שצריכה להיות בקובץ מקומי בשם firestore.rules.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
allow read: if request.auth != null;
allow create: if request.auth != null;
allow update: if request.auth != null;
}
}
הקצאת קטגוריית ברירת המחדל Cloud Storage
הקצאת קטגוריות Cloud Storage נוספות
ההגדרה הזו יוצרת פרויקט Google Cloud חדש, משייכת את הפרויקט לחשבון Cloud Billing (תוכנית התמחור Blaze נדרשת לקטגוריות נוספות), מפעילה את שירותי Firebase בפרויקט, מקצה קטגוריות Cloud Storage נוספות שאינן ברירת המחדל ומרשמת בפרויקט שלושה סוגים שונים של אפליקציות.
הוא גם מקצה Firebase Security Rules לכל קטגוריה Cloud Storage, ומעלה קובץ לאחת מהקטגוריות 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,
]
}
זוהי מערכת הכללים של Cloud Storage Security Rules, שצריכה להיות בקובץ מקומי בשם storage.rules.
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
הגנה על משאב API באמצעות Firebase App Check
ההגדרה הזו יוצרת פרויקט Google Cloud חדש, מפעילה את שירותי Firebase בפרויקט ומגדירה ומפעילה אכיפה של Firebase App Check עבור Cloud Firestore, כך שאפשר לגשת אליו רק מהאפליקציה ל-Android.
# Creates a new Google Cloud project.
resource "google_project" "appcheck" {
provider = google-beta.no_user_project_override
folder_id = "folder-id-for-new-project"
name = "Project Display Name"
project_id = "project-id-for-new-project"
# Required for the project to display in a list of Firebase projects.
labels = {
"firebase" = "enabled"
}
}
# Enables required APIs.
resource "google_project_service" "services" {
provider = google-beta.no_user_project_override
project = google_project.appcheck.project_id
for_each = toset([
"cloudresourcemanager.googleapis.com",
"firebase.googleapis.com",
"firebaseappcheck.googleapis.com",
"firestore.googleapis.com",
"serviceusage.googleapis.com",
])
service = each.key
# Don't disable the service if the resource block is removed by accident.
disable_on_destroy = false
}
# Enables Firebase services for the new project created earlier.
resource "google_firebase_project" "appcheck" {
provider = google-beta
project = google_project.appcheck.project_id
depends_on = [google_project_service.services]
}
# Provisions the Firestore database instance.
resource "google_firestore_database" "database" {
provider = google-beta
project = google_firebase_project.appcheck.project
name = "(default)"
# See available locations: https://firebase.google.com/docs/projects/locations#default-cloud-location
location_id = "name-of-region"
# "FIRESTORE_NATIVE" is required to use Firestore with Firebase SDKs, authentication, and Firebase Security Rules.
type = "FIRESTORE_NATIVE"
concurrency_mode = "OPTIMISTIC"
# Wait for Firebase to be enabled in the Google Cloud project before initializing Firestore.
depends_on = [
google_firebase_project.appcheck,
]
}
# Creates a Firebase Android App in the new project created earlier.
resource "google_firebase_android_app" "appcheck" {
provider = google-beta
project = google_firebase_project.appcheck.project
display_name = "Play Integrity app"
package_name = "package.name.playintegrity"
sha256_hashes = [
# TODO: insert your Android app's SHA256 certificate
]
}
# It takes a while for App Check to recognize the new app
# If your app already exists, you don't have to wait 30 seconds.
resource "time_sleep" "wait_30s" {
depends_on = [google_firebase_android_app.appcheck]
create_duration = "30s"
}
# Register the Android app with the Play Integrity provider
resource "google_firebase_app_check_play_integrity_config" "appcheck" {
provider = google-beta
project = google_firebase_project.appcheck.project
app_id = google_firebase_android_app.appcheck.app_id
depends_on = [time_sleep.wait_30s, google_firestore_database.database]
lifecycle {
precondition {
condition = length(google_firebase_android_app.appcheck.sha256_hashes) > 0
error_message = "Provide a SHA-256 certificate on the Android App to use App Check"
}
}
}
# Enable enforcement of App Check for Firestore
resource "google_firebase_app_check_service_config" "firestore" {
provider = google-beta
project = google_firebase_project.appcheck.project
service_id = "firestore.googleapis.com"
depends_on = [google_project_service.services]
}
התקנה של מכונה של Firebase Extension
ההגדרה הזו יוצרת פרויקט 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"
}
}
}
פתרון בעיות ושאלות נפוצות
אתם רוצים לקבל מידע נוסף על כל המאפיינים השונים שקשורים לפרויקט (כמו project ו-user_project_override)
במדריך הזה נעשה שימוש במאפייני Terraform הבאים כשעובדים עם 'projects'.
project בתוך בלוק resource
מומלץ: ככל האפשר, כדאי לכלול את המאפיין project בכל בלוק resource
אם תכללו מאפיין פרויקט, Terraform תיצור את התשתית שצוינה בבלוק המשאבים בתוך הפרויקט שצוין. המדריך הזה וקובצי התצורה לדוגמה שלנו מבוססים על השיטה הזו.
מידע נוסף זמין במסמכי התיעוד הרשמיים של Terraform בנושא project.
user_project_override בתוך הבלוק provider
כדי להקצות את רוב המשאבים, צריך להשתמש ב-user_project_override = true, כלומר לבדוק את המכסה בפרויקט Firebase שלכם. עם זאת, כדי להגדיר את הפרויקט החדש כך שיוכל לקבל בדיקות מכסות, קודם צריך להשתמש ב-user_project_override = false.
מופיעה הודעת השגיאה הבאה:
generic::permission_denied: Firebase Tos Not Accepted.
חשוב לוודא שחשבון המשתמש שבו אתם משתמשים להרצת הפקודות gcloud CLI אישר את התנאים וההגבלות של Firebase (TOS של Firebase).
כדי לבצע את הבדיקה הזו, פותחים דפדפן שמחובר לחשבון המשתמש ומנסים להציג פרויקט Firebase קיים במסוף Firebase. אם אתם יכולים לראות פרויקט קיים ב-Firebase, סימן שחשבון המשתמש אישר את התנאים וההגבלות של Firebase.
אם לא ניתן לראות פרויקטים קיימים ב-Firebase, סביר להניח שחשבון המשתמש לא אישר את התנאים וההגבלות של Firebase. כדי לפתור את הבעיה, צריך ליצור פרויקט חדש ב-Firebase דרך מסוף Firebase ולאשר את התנאים וההגבלות של Firebase כחלק מתהליך יצירת הפרויקט. אפשר למחוק את הפרויקט הזה באופן מיידי דרך Project Settings במסוף.
אחרי שמריצים את terraform apply, מופיעה השגיאה generic::permission_denied: IAM authority does not have the
permission.
מחכים כמה דקות ומנסים להריץ את terraform apply שוב.
יצירת המשאב נכשלה, אבל כשמריצים שוב את terraform apply מופיעה ההודעה ALREADY_EXISTS.
יכול להיות שהסיבה לכך היא עיכוב בהפצה במערכות שונות. כדי לפתור את הבעיה, אפשר לייבא את המשאב למצב של Terraform באמצעות הפקודה terraform import. לאחר מכן, מנסים להריץ שוב את terraform apply.
כשעובדים עם Cloud Firestore, מופיעה השגיאה הבאה: Error creating Index: googleapi:
Error 409;...Concurrent access -- try again
כפי שמצוין בשגיאה, יכול להיות ש-Terraform מנסה להקצות כמה אינדקסים ו/או ליצור מסמך בו-זמנית, ונתקלה בשגיאה של בו-זמניות.
מנסים להריץ שוב את terraform apply.
מופיעה השגיאה הבאה:
"you may need to specify 'X-Goog-User-Project' HTTP header for quota and
billing purposes".
משמעות השגיאה הזו היא ש-Terraform לא יודע באיזה פרויקט לבדוק את המכסה. כדי לפתור את הבעיה, בודקים את הפרטים הבאים בבלוק resource:
מוודאים שציינתם ערך למאפיין project.
חשוב לוודא שאתם משתמשים בספק עם user_project_override = true
(ללא כינוי), שמופיע בדוגמאות של Firebase בתור google-beta.
כשיוצרים פרויקט Google Cloud חדש, מופיעה השגיאה שמזהה הפרויקט שצוין לפרויקט החדש כבר קיים.
אלה הסיבות האפשריות לכך שמזהה הפרויקט כבר קיים:
הפרויקט שמשויך למזהה הזה שייך למישהו אחר.
הפתרון: בוחרים מזהה פרויקט אחר.
הפרויקט שמשויך למזהה הזה נמחק לאחרונה (במצב מחיקה רכה).
הפתרון: אם לדעתכם הפרויקט שמשויך למזהה שייך לכם, עליכם לבדוק את מצב הפרויקט באמצעות projects.get API ל-REST.
הפרויקט שמשויך למזהה הזה קיים כראוי אצל המשתמש הנוכחי. יכול להיות שהסיבה לשגיאה היא שקריאה קודמת של terraform apply הופסקה.
הפתרון: מריצים את הפקודות הבאות: terraform import google_project.default PROJECT_ID
ואז terraform import google_firebase_project.default PROJECT_ID
למה צריך להקצות את המכונה Cloud Firestore שמוגדרת כברירת מחדל לפני הקטגוריה Cloud Storage שמוגדרת כברירת מחדל?
אם הקצית את הקטגוריה Cloud Storage שמוגדרת כברירת מחדל (דרך google_app_engine_application) לפני שניסית להקצות את המכונה Cloud Firestore שמוגדרת כברירת מחדל, תגלה שהמכונה Cloud Firestore שמוגדרת כברירת מחדל כבר הוקצה. לתשומת ליבכם: מכונה של מסד נתונים שהוקצה נמצאת במצב Datastore, כלומר אין גישה אליה ל-Firebase SDK, לאימות או ל-Firebase Security Rules. אם רוצים להשתמש ב-Cloud Firestore עם שירותי Firebase האלה, צריך לרוקן את מסד הנתונים ואז לשנות את סוג מסד הנתונים במסוף Google Cloud.
כשמנסים להקצות את Cloud Storage (דרך google_app_engine_application) ואז את מכונה Cloud Firestore שמוגדרת כברירת מחדל, מופיעה השגיאה הבאה: Error: Error creating Database: googleapi: Error 409: Database already
exists. Please use another database_id.
כשמקצים את הקטגוריה Cloud Storage שמוגדרת כברירת מחדל לפרויקט (דרך google_app_engine_application) ולפרויקט עדיין אין מכונה Cloud Firestore שמוגדרת כברירת מחדל, המערכת מקצה את מכונה Cloud Firestore שמוגדרת כברירת מחדל לפרויקט באופן אוטומטי באמצעות google_app_engine_application.
מכיוון שמכונה Cloud Firestore שמוגדרת כברירת המחדל בפרויקט כבר הוקצה, תופיע שגיאה ב-google_firestore_database אם תנסו להקצות אותה שוב באופן מפורש.
אחרי שמקצים את המכונה Cloud Firestore שמוגדרת כברירת מחדל בפרויקט, אי אפשר להקצות אותה מחדש או לשנות את המיקום שלה. חשוב לזכור שמכונה של מסד נתונים שהוקצה נמצאת במצב Datastore, כלומר לא ניתן לגשת אליה באמצעות Firebase SDK, אימות או Firebase Security Rules. אם רוצים להשתמש ב-Cloud Firestore עם שירותי Firebase האלה, צריך לרוקן את מסד הנתונים ואז לשנות את סוג מסד הנתונים במסוף Google Cloud.