Get started with Terraform and Firebase

Firebase is beginning to support Terraform. If you're on a team that wants to automate and standardize creating Firebase projects with specific resources provisioned and services enabled, then using Terraform with Firebase can be a good fit for you.

The basic workflow for using Terraform with Firebase includes the following:

  • Creating and customizing a Terraform configuration file (a .tf file) which specifies the infrastructure you want to provision (that is, resources you want to provision and the services you want to enable).

  • Using gcloud CLI commands that interface with Terraform to provision the infrastructure specified in the .tf file.

What can you do with Terraform and Firebase?

The example generalized workflow in this guide is creating a new Firebase project with an Android app. But you can do a lot more with Terraform, such as:

  • Delete and modify existing infrastructure using Terraform.

  • Manage product-specific configuration and tasks using Terraform, like:

    • Enabling Firebase Authentication sign-in providers.
    • Creating Cloud Storage buckets or database instances and deploying Firebase Security Rules for them.
    • Creating Firebase App Hosting backends, builds, and other related resources.

You can use standard Terraform config files and commands to accomplish all these tasks. And to help you with this, we've provided sample Terraform config files for several common use cases.



Generalized workflow for using Terraform with Firebase

Prerequisites

This guide is an introduction to using Terraform with Firebase, so it assumes basic proficiency with Terraform. Make sure that you've completed the following prerequisites before starting this workflow.

  • Install Terraform and familiarize yourself with Terraform using their official tutorials.

  • Install the Google Cloud CLI (gcloud CLI). Login using a user account or a service account.

    • If using a user account, you must have accepted the Firebase Terms of Service (Firebase ToS). You have accepted the Firebase ToS if you can view a Firebase project in the Firebase console
    • For Terraform to take certain actions (for example, create projects), the following must be true:
      • The user or service account must have the applicable IAM access for those actions.
      • If the user or service account is part of a Google Cloud organization, then the org policies must allow the account to take those actions.


Step 1: Create and customize a Terraform config file

A Terraform config file needs two main sections (which are described in detail below):

Set up your provider

A provider setup is required no matter which Firebase products or services are involved.

  1. Create a Terraform config file (like main.tf file) in your local directory.

    In this guide, you'll use this config file to specify both the provider setup and all the infrastructure that you want Terraform to create. Note, though, that you have options for how to include the provider setup.

    You have the following options for how to include a provider setup to the rest of your Terraform configuration:

    • Option 1: Include it at the top of a single Terraform .tf config file (as shown in this guide).

      • Use this option if you're just getting started with Terraform or just trying out Terraform with Firebase.
    • Option 2: Include it in a separate .tf file (like a provider.tf file), apart from the .tf file where you specify infrastructure to create (like a main.tf file).

      • Use this option if you're part of a larger team that needs to standardize setup.
      • When running Terraform commands, both the provider.tf file and the main.tf file must be in the same directory.

  2. Include the following provider setup at the top of the main.tf file.

    You must use the google-beta provider because this is a beta release of using Firebase with Terraform. Exercise caution when using in production.

    # Terraform configuration to set up providers by version. terraform {   required_providers {     google-beta = {       source  = "hashicorp/google-beta"       version = "~> 6.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 }

    Learn more about the different types of project-related attributes (including what this guide calls the "quota-check project") when using Terraform with Firebase.

  3. Continue to the next section to complete your config file and specify what infrastructure to create.

Specify what infrastructure to create using resource blocks

In your Terraform config file (for this guide, your main.tf file), you need to specify all the infrastructure you want Terraform to create (meaning all the resources you want to provision and all the services you want to enable). In this guide, find a full list of all Firebase resources that support Terraform.

  1. Open your main.tf file.

  2. Under the provider setup, include the following config of resource blocks.

    This basic example creates a new Firebase project and then creates a Firebase Android App within that project.

    # 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,   ] }

If you're not familiar with the infrastructure of projects and apps as resources, review the following documentation:

# 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,   ] }


Step 2: Run Terraform commands to create the specified infrastructure

To provision the resources and enable the services specified in your main.tf file, run the following commands from the same directory as your main.tf file. For detailed information about these commands, see the Terraform documentation.

  1. If this is the first time that you're running Terraform commands in the directory, you need to initialize the configuration directory and install the Google Terraform provider. Do this by running the following command:

    terraform init
  2. Create the infrastructure specified in your main.tf file by running the following command:

    terraform apply
  3. Confirm that everything was provisioned or enabled as expected:

    • Option 1: See the configuration printed in your terminal by running the following command:

      terraform show
    • Option 2: View your Firebase project in the Firebase console.



Firebase resources with Terraform support

The following Firebase and Google resources have Terraform support. And we're adding more resources all the time! So if you don't see the resource that you want to manage with Terraform, then check back soon to see if it's available or request it by filing an issue in the GitHub repo.


Firebase project and app management

  • google_firebase_project — enable Firebase services on an existing Google Cloud project

  • Firebase Apps


Firebase Authentication

Not yet supported:

  • Configuring multi-factor authentication (MFA) via Terraform

Firebase App Hosting


Firebase Data Connect


Firebase Realtime Database

Not yet supported:

  • Deploying Firebase Realtime Database Security Rules via Terraform (learn how to deploy these Rules using other tooling, including programmatic options)

Cloud Firestore

  • google_firestore_database — create a Cloud Firestore instance

  • google_firestore_index — enable efficient queries for Cloud Firestore

  • google_firestore_document — seed a Cloud Firestore instance with a specific document in a collection

    Important: Do not use real end-user or production data in this seed document.


Cloud Storage for Firebase

  • google_firebase_storage_bucket — make an existing Cloud Storage bucket accessible for Firebase SDKs, authentication, and Firebase Security Rules

  • google_storage_bucket_object — add an object to a Cloud Storage bucket

    Important: Do not use real end-user or production data in this file.


Firebase Security Rules (for Cloud Firestore and Cloud Storage)

Note that Firebase Realtime Database uses a different provisioning system for its Firebase Security Rules.

  • google_firebaserules_ruleset — define Firebase Security Rules that apply to a Cloud Firestore instance or a Cloud Storage bucket

  • google_firebaserules_release — deploy specific rulesets to a Cloud Firestore instance or a Cloud Storage bucket


Firebase App Check


Firebase Extensions



Sample Terraform config files for common use cases

This config creates a new Google Cloud project, associates the project with a Cloud Billing account (the Blaze pricing plan is required for Firebase Authentication with GCIP), enables Firebase services for the project, sets up Firebase Authentication with GCIP, and registers three different app types with the project.

Note that enabling GCIP is required to set up Firebase Authentication via 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" }  # 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_firebase_project.auth.project    # 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",   ] }  # Creates a Firebase Android App in the new project created above. resource "google_firebase_android_app" "auth" {   provider     = google-beta   project      = google_firebase_project.auth.project   display_name = "My Android app"   package_name = "android.package.name" }  # Creates a Firebase Apple-platforms App in the new project created above. resource "google_firebase_apple_app" "auth" {   provider     = google-beta   project      = google_firebase_project.auth.project   display_name = "My Apple app"   bundle_id    = "apple.app.12345" }  # Creates a Firebase Web App in the new project created above. resource "google_firebase_web_app" "auth" {   provider     = google-beta   project      = google_firebase_project.auth.project   display_name = "My Web app" }

This config creates a new Google Cloud project, enables Firebase services for the project, and provisions a Data Connect service.

# Creates a new Google Cloud project. resource "google_project" "dataconnect" {   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 Data Connect).   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" "services" {   provider = google-beta.no_user_project_override   project  = google_project.dataconnect.project_id   for_each = toset([     "serviceusage.googleapis.com",     "cloudresourcemanager.googleapis.com",     "firebasedataconnect.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" "dataconnect" {   provider = google-beta   project  = google_project.dataconnect.project_id    depends_on = [google_project_service.services] }  # Create a Firebase Data Connect service resource "google_firebase_data_connect_service" "dataconnect-default" {   project         = google_firebase_project.dataconnect.project   location        = "name-of-region-for-service"   service_id      = "${google_firebase_project.dataconnect.project}-default-fdc"   deletion_policy = "DEFAULT" }

This config creates a new Google Cloud project, enables Firebase services for the project, provisions the project's default Realtime Database instance, and registers three different app types with the project.

# 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" }  # 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    depends_on = [google_project_service.rtdb] }  # Provisions the default Realtime Database default instance. resource "google_firebase_database_instance" "database" {   provider    = google-beta   project     = google_firebase_project.rtdb.project   # 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" }  # Creates a Firebase Android App in the new project created above. resource "google_firebase_android_app" "rtdb" {   provider     = google-beta   project      = google_firebase_project.rtdb.project   display_name = "My Android app"   package_name = "android.package.name" }  # Creates a Firebase Apple-platforms App in the new project created above. resource "google_firebase_apple_app" "rtdb" {   provider     = google-beta   project      = google_firebase_project.rtdb.project   display_name = "My Apple app"   bundle_id    = "apple.app.12345" }  # Creates a Firebase Web App in the new project created above. resource "google_firebase_web_app" "rtdb" {   provider     = google-beta   project      = google_firebase_project.rtdb.project   display_name = "My Web app" }

This config creates a new Google Cloud project, associates the project with a Cloud Billing account (the Blaze pricing plan is required for multiple Realtime Database instances), enables Firebase services for the project, provisions multiple Realtime Database instances (including the project's default Realtime Database instance), and registers three different app types with the project.

# 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" }  # 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    depends_on = [google_project_service.rtdb-multi] }  # Provisions the default Realtime Database default instance. resource "google_firebase_database_instance" "database-default" {   provider    = google-beta   project     = google_firebase_project.rtdb-multi.project   # 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" }  # Provisions an additional Realtime Database instance. resource "google_firebase_database_instance" "database-additional" {   provider    = google-beta   project     = google_firebase_project.rtdb-multi.project   # 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" }  # Creates a Firebase Android App in the new project created above. resource "google_firebase_android_app" "rtdb-multi" {   provider     = google-beta   project      = google_firebase_project.rtdb-multi.project   display_name = "My Android app"   package_name = "android.package.name" }  # Creates a Firebase Apple-platforms App in the new project created above. resource "google_firebase_apple_app" "rtdb-multi" {   provider     = google-beta   project      = google_firebase_project.rtdb-multi.project   display_name = "My Apple app"   bundle_id    = "apple.app.12345" }  # Creates a Firebase Web App in the new project created above. resource "google_firebase_web_app" "rtdb-multi" {   provider     = google-beta   project      = google_firebase_project.rtdb-multi.project   display_name = "My Web app" }

This config creates a new Google Cloud project, enables Firebase services for the project, provisions the project's default Cloud Firestore instance, and registers three different app types with the project.

It also provisions Firebase Security Rules for the default Cloud Firestore instance, creates a Cloud Firestore index, and adds a Cloud Firestore document with seed data.

# 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" }  # 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    depends_on = [google_project_service.firestore] }  # Provisions the Firestore database instance. resource "google_firestore_database" "firestore" {   provider                    = google-beta   project                     = google_firebase_project.firestore.project   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" }  # Creates a ruleset of Firestore Security Rules from a local file. resource "google_firebaserules_ruleset" "firestore" {   provider = google-beta   project  = google_firestore_database.firestore.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")     }   } }  # 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_firestore_database.firestore.project }  # Adds a new Firestore index. resource "google_firestore_index" "indexes" {   provider = google-beta   project  = google_firestore_database.firestore.project    collection  = "quiz"   query_scope = "COLLECTION"    fields {     field_path = "question"     order      = "ASCENDING"   }    fields {     field_path = "answer"     order      = "ASCENDING"   } }  # 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_firestore_database.firestore.project   collection  = "quiz"   document_id = "question-1"   fields      = "{\"question\":{\"stringValue\":\"Favorite Database\"},\"answer\":{\"stringValue\":\"Firestore\"}}" }  # Creates a Firebase Android App in the new project created above. resource "google_firebase_android_app" "firestore" {   provider     = google-beta   project      = google_firebase_project.firestore.project   display_name = "My Android app"   package_name = "android.package.name" }  # Creates a Firebase Apple-platforms App in the new project created above. resource "google_firebase_apple_app" "firestore" {   provider     = google-beta   project      = google_firebase_project.firestore.project   display_name = "My Apple app"   bundle_id    = "apple.app.12345" }  # Creates a Firebase Web App in the new project created above. resource "google_firebase_web_app" "firestore" {   provider     = google-beta   project      = google_firebase_project.firestore.project   display_name = "My Web app" }

This is the ruleset of Cloud Firestore Security Rules that should be in a local file named firestore.rules.

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

This config creates a new Google Cloud project, associates the project with a Cloud Billing account (the Blaze pricing plan is required for additional buckets), enables Firebase services for the project, provisions additional, non-default Cloud Storage buckets, and registers three different app types with the project.

It also provisions Firebase Security Rules for each Cloud Storage bucket, and uploads a file to one of the Cloud Storage buckets.

# 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" }  # 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    depends_on = [google_project_service.storage-multi] }  # Provisions a Cloud Storage bucket. resource "google_storage_bucket" "bucket-1" {   provider = google-beta   project  = google_firebase_project.storage-multi.project   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_firebase_project.storage-multi.project   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_firebase_project.storage-multi.project   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_firebase_project.storage-multi.project   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_firebase_project.storage-multi.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")     }   } }  # 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_firebase_project.storage-multi.project }  # 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_firebase_project.storage-multi.project }  # 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_firebase_project.storage-multi.project   display_name = "My Android app"   package_name = "android.package.name" }  # Creates a Firebase Apple-platforms App in the new project created above. resource "google_firebase_apple_app" "storage-multi" {   provider     = google-beta   project      = google_firebase_project.storage-multi.project   display_name = "My Apple app"   bundle_id    = "apple.app.12345" }  # Creates a Firebase Web App in the new project created above. resource "google_firebase_web_app" "storage-multi" {   provider     = google-beta   project      = google_firebase_project.storage-multi.project   display_name = "My Web app" }

This is the ruleset of Cloud Storage Security Rules that should be in a local file named storage.rules.

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

This config creates a new Google Cloud project, enables Firebase services for the project, and sets up and enables enforcement of Firebase App Check for Cloud Firestore so that it can only be accessed from your Android app.

# 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" }  # 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" }  # 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   ] }  # 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 = [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] }

This config creates a new Google Cloud project, enables Firebase services for the project, and installs a new instance of a Firebase Extension in the project. If the instance already exists, its parameters are updated based on the values provided in the config.

# 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" }  # 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_firebase_project.extensions.project    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"     }   } }

This config creates a new Google Cloud project, enables Firebase services for the project, including Vertex AI in Firebase, and sets up and enables enforcement of Firebase App Check for Firebase AI Logic so that it can only be accessed from your apps.

# Creates a new Google Cloud project. resource "google_project" "vertex" {   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 Vertex AI in Firebase).   billing_account = "000000-000000-000000" }  # Enables required APIs. resource "google_project_service" "services" {   provider   = google-beta.no_user_project_override    project  = google_project.vertex.project_id   for_each = toset([     "cloudresourcemanager.googleapis.com",     "firebase.googleapis.com",     "serviceusage.googleapis.com",     # Required APIs for Vertex AI in Firebase     "aiplatform.googleapis.com",     "firebasevertexai.googleapis.com",     # App Check is recommended to protect Vertex AI in Firebase from abuse     "firebaseappcheck.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" "vertex" {   provider = google-beta   project  = google_project.vertex.project_id    depends_on = [google_project_service.services] }  # Creates a Firebase Web App in the new project created earlier. resource "google_firebase_web_app" "app" {   provider = google-beta   project  = google_firebase_project.vertex.project    display_name = "My Web App" }  # Creates a Firebase Android App in the new project created earlier. resource "google_firebase_android_app" "app" {   provider     = google-beta   project      = google_firebase_project.vertex.project   display_name = "My Android App"   package_name = "package.name.playintegrity"   sha256_hashes = [     # TODO: insert your Android app's SHA256 certificate   ] }  # Creates a Firebase Apple App in the new project created earlier. resource "google_firebase_apple_app" "app" {   provider     = google-beta   project      = google_firebase_project.vertex.project   display_name = "My Apple App"   bundle_id    = "bundle.id"   team_id      = "1234567890" }  ### Protects Vertex AI in Firebase with App Check.  # Turns on enforcement for Vertex AI in Firebase resource "google_firebase_app_check_service_config" "vertex" {   provider = google-beta    project          = google_firebase_project.vertex.project   service_id       = "firebaseml.googleapis.com"   enforcement_mode = "ENFORCED" }  # Enables the reCAPTCHA Enterprise API resource "google_project_service" "recaptcha_enterprise" {   provider = google-beta    project = google_firebase_project.vertex.project   service = "recaptchaenterprise.googleapis.com"    # Don't disable the service if the resource block is removed by accident.   disable_on_destroy = false }  # Enables the Play Integrity API resource "google_project_service" "play_integrity" {   provider = google-beta    project = google_firebase_project.vertex.project   service = "playintegrity.googleapis.com"    # Don't disable the service if the resource block is removed by accident.   disable_on_destroy = false }  # Allows the web app to use reCAPTCHA Enterprise with App Check resource "google_firebase_app_check_recaptcha_enterprise_config" "appcheck" {   provider = google-beta    project   = google_firebase_project.vertex.project   app_id    = google_firebase_web_app.app.app_id   site_key  = "your site key"   token_ttl = "7200s" # Optional    depends_on = [google_project_service.recaptcha_enterprise] }  # Registers the Android app with the Play Integrity provider resource "google_firebase_app_check_play_integrity_config" "appcheck" {   provider  = google-beta   project   = google_firebase_project.vertex.project   app_id    = google_firebase_android_app.app.app_id   token_ttl = "7200s" # Optional    lifecycle {     precondition {       condition     = length(google_firebase_android_app.app.sha256_hashes) > 0       error_message = "Provide a SHA-256 certificate on the Android App to use App Check"     }   }    depends_on = [google_project_service.play_integrity] }  # Registers the Apple app with the AppAttest provider resource "google_firebase_app_check_app_attest_config" "appcheck" {   provider  = google-beta   project   = google_firebase_project.vertex.project   app_id    = google_firebase_apple_app.app.app_id   token_ttl = "7200s" # Optional    lifecycle {     precondition {       condition     = google_firebase_apple_app.app.team_id != ""       error_message = "Provide a Team ID on the Apple App to use App Check"     }   } }

This configuration demonstrates how to manually provision an App Hosting backend using Terraform. This approach gives you fine-grained control over the resources created, but requires you to define each resource individually. This is useful when you need to customize the backend beyond the default options.

# Creates a new Google Cloud project. resource "google_project" "apphosting" {   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 App Hosting).   billing_account = "000000-000000-000000" }  # Enables required APIs. resource "google_project_service" "services" {   provider = google-beta.no_user_project_override   project  = google_project.apphosting.project_id   for_each = toset([     "cloudresourcemanager.googleapis.com",     "firebase.googleapis.com",     "firebaseapphosting.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" "apphosting" {   provider = google-beta   project  = google_project.apphosting.project_id    depends_on = [google_project_service.services] }  # Creates a Firebase Web App in the new project created earlier. resource "google_firebase_web_app" "apphosting" {   provider     = google-beta   project      = google_firebase_project.apphosting.project   display_name = "My web app" }  # Creates a Firebase App Hosting Backend resource "google_firebase_app_hosting_backend" "example" {   provider = google-beta   project  = google_firebase_project.apphosting.project    # Choose the region closest to your users   location         = "name-of-region-for-service"   backend_id       = "name-of-backend-for-service"   app_id           = google_firebase_web_app.apphosting.app_id   display_name     = "My Backend"   serving_locality = "GLOBAL_ACCESS" # or "REGIONAL_STRICT"   service_account  = google_service_account.service_account.email }  # Creates the service account for Firebase App Hosting resource "google_service_account" "service_account" {   provider = google-beta   project  = google_firebase_project.apphosting.project    # Must be firebase-app-hosting-compute   account_id                   = "firebase-app-hosting-compute"   display_name                 = "Firebase App Hosting compute service account"    # Do not throw if already exists   create_ignore_already_exists = true }  # Adds permission to the App Hosting service account resource "google_project_iam_member" "app_hosting_sa" {   provider = google-beta   project  = google_firebase_project.apphosting.project    for_each = toset([     "roles/firebase.sdkAdminServiceAgent",     "roles/firebaseapphosting.computeRunner"   ])    role   = each.key   member = google_service_account.service_account.member }  # Creates a Build resource "google_firebase_app_hosting_build" "example" {   provider = google-beta    project          = google_firebase_app_hosting_backend.example.project   location         = google_firebase_app_hosting_backend.example.location   backend          = google_firebase_app_hosting_backend.example.backend_id   build_id         = "my-build"    source {     container {       # TODO: use your own image       image = "us-docker.pkg.dev/cloudrun/container/hello"     }   } }  # Rolls out the Build resource "google_firebase_app_hosting_traffic" "example" {   provider = google-beta    project          = google_firebase_app_hosting_backend.example.project   location         = google_firebase_app_hosting_backend.example.location   backend          = google_firebase_app_hosting_backend.example.backend_id    target {     splits {       build = google_firebase_app_hosting_build.example.name       percent = 100     }   } }

This configuration demonstrates how to provision an App Hosting backend using application code stored in a GitHub repository. This approach allows you to manage and update your infrastructure through GitHub pull requests and CI/CD pipelines according to the typical model for App Hosting deployments.

# Creates a new Google Cloud project. resource "google_project" "apphosting" {   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 App Hosting).   billing_account = "000000-000000-000000" }  # Enables required APIs. resource "google_project_service" "services" {   provider = google-beta.no_user_project_override   project  = google_project.apphosting.project_id   for_each = toset([     "cloudresourcemanager.googleapis.com",     "firebase.googleapis.com",     "firebaseapphosting.googleapis.com",     "serviceusage.googleapis.com",     "developerconnect.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" "apphosting" {   provider = google-beta   project  = google_project.apphosting.project_id    depends_on = [google_project_service.services] }  # Creates a Firebase Web App in the new project created earlier. resource "google_firebase_web_app" "apphosting" {   provider     = google-beta   project      = google_firebase_project.apphosting.project   display_name = "My web app" }  ### Setting up Firebase App Hosting ###  # Creates a Firebase App Hosting Backend resource "google_firebase_app_hosting_backend" "example" {   provider = google-beta   project  = google_firebase_project.apphosting.project    # Choose the region closest to your users   location         = "name-of-region-for-service"   backend_id       = "name-of-backend-for-service"   app_id           = google_firebase_web_app.apphosting.app_id   display_name     = "My Backend"   serving_locality = "GLOBAL_ACCESS" # or "REGIONAL_STRICT"   service_account  = google_service_account.service_account.email    codebase {     repository = google_developer_connect_git_repository_link.my-repository.name     root_directory = "/"   } }  # Creates the service account for Firebase App Hosting resource "google_service_account" "service_account" {   provider = google-beta   project  = google_firebase_project.apphosting.project    # Must be firebase-app-hosting-compute   account_id                   = "firebase-app-hosting-compute"   display_name                 = "Firebase App Hosting compute service account"    # Do not throw if already exists   create_ignore_already_exists = true }  # Adds permission to the App Hosting service account resource "google_project_iam_member" "app_hosting_sa" {   provider = google-beta   project  = google_firebase_project.apphosting.project    for_each = toset([     "roles/developerconnect.readTokenAccessor",     "roles/firebase.sdkAdminServiceAgent",     "roles/firebaseapphosting.computeRunner"   ])    role   = each.key   member = google_service_account.service_account.member }  # Configures auto rollout from GitHub resource "google_firebase_app_hosting_traffic" "example" {   provider = google-beta    project  = google_firebase_app_hosting_backend.example.project   location = google_firebase_app_hosting_backend.example.location   backend  = google_firebase_app_hosting_backend.example.backend_id    rollout_policy {     codebase_branch = "main" # Or another branch   } }  ###  ### Setting up a connection to GitHub ###  # Provisions Service Agent for Developer Connect resource "google_project_service_identity" "devconnect-p4sa" {   provider = google-beta   project  = google_firebase_project.apphosting.project    service  = "developerconnect.googleapis.com" }  # Adds permission to Developer Connect Service Agent to manager GitHub tokens resource "google_project_iam_member" "devconnect-secret" {   provider = google-beta   project  = google_firebase_project.apphosting.project    role     = "roles/secretmanager.admin"   member   = google_project_service_identity.devconnect-p4sa.member }  # Connects to a GitHub account resource "google_developer_connect_connection" "my-connection" {   provider = google-beta   project  = google_firebase_project.apphosting.project    # Must match the google_firebase_app_hosting_backend's location   location = "name-of-region-for-service"    # Must be `firebase-app-hosting-github-oauth`   connection_id = "firebase-app-hosting-github-oauth"   github_config {     github_app = "FIREBASE"   }   depends_on = [google_project_iam_member.devconnect-secret] }  # Follow the next steps to set up the GitHub connection # Tip: Run terraform refresh to obtain the output output "next_steps" {   description = "Follow the action_uri if present to continue setup"   value = google_developer_connect_connection.my-connection.installation_state }  # Links a GitHub repo to the project resource "google_developer_connect_git_repository_link" "my-repository" {   provider = google-beta   project  = google_firebase_project.apphosting.project   location = google_developer_connect_connection.my-connection.location    git_repository_link_id = "my-repo-id"   parent_connection = google_developer_connect_connection.my-connection.connection_id   clone_uri = "https://github.com/myuser/myrepo.git" }  ###



Troubleshooting and FAQ

This guide uses the following Terraform attributes when working with "projects".

project within a resource block

Recommended: whenever possible, include the project attribute within each resource block

By including a project attribute, Terraform will create the infrastructure specified in the resource block within the specified project. This guide and our sample config files all use this practice.

See the official Terraform documentation about project.

user_project_override within the provider block

For provisioning most resources, you should use user_project_override = true, which means to check quota against your own Firebase project. However, to set up your new project so that it can accept quota checks, you first need to use user_project_override = false.

See the official Terraform documentation about user_project_override.

Make sure that the user account that you're using to run gcloud CLI commands has accepted the Firebase Terms of Service (Firebase ToS).

  • You can do this check by using a browser signed into the user account and trying to view an existing Firebase project in the Firebase console. If you can view an existing Firebase project, then the user account has accepted the Firebase ToS.

  • If you can't view any existing Firebase project, then the user account probably hasn't accepted the Firebase ToS. To fix this, create a new Firebase project via the Firebase console and accept the Firebase ToS as part of project creation. You can immediately delete this project via Project Settings in the console.

Wait a few minutes, and then try running terraform apply again.

This could be due to a propagation delay in various systems. Try to resolve this issue by importing the resource into the Terraform state by running terraform import. Then try running terraform apply again.

You can learn how to import each resource in the "Import" section of its Terraform documentation (for example, the "Import" documentation for Cloud Firestore).

As the error suggests, Terraform may be trying to provision multiple indices and/or creating a document at the same time and ran into a concurrency error. Try running terraform apply again.

This error means that Terraform doesn't know which project to check quota against. To troubleshoot, check the following in the resource block:

  • Make sure that you have specified a value for the project attribute.
  • Make sure that you're using the provider with user_project_override = true (no alias), which in the Firebase samples is google-beta.

Here are the possible reasons the project ID may already exist:

  • The project associated with that ID belongs to someone else.

    • To resolve: Choose another project ID.
  • The project associated with that ID was recently deleted (in soft-delete state).

    • To resolve: If you think that the project associated with the ID belongs to you, then check the state of the project using the projects.get REST API.
  • The project associated with that ID exists correctly under the current user. A possible cause for the error could be that a previous terraform apply got interrupted.

    • To resolve: Run the following commands:
      terraform import google_project.default PROJECT_ID and then
      terraform import google_firebase_project.default PROJECT_ID

If you provisioned your default Cloud Storage bucket (via google_app_engine_application) before you try to provision your default Cloud Firestore instance, then you'll find that your default Cloud Firestore instance has already been provisioned. Note that the provisioned database instance is in Datastore mode, which means that it's not accessible to Firebase SDKs, authentication, or Firebase Security Rules. If you want to use Cloud Firestore with these Firebase services, then you'll need to empty the database and then change its database type in the Google Cloud console.

When you provision a project's default Cloud Storage bucket (via google_app_engine_application) and the project doesn't yet have its default Cloud Firestore instance, then google_app_engine_application automatically provisions the project's default Cloud Firestore instance.

So, since your project's default Cloud Firestore instance is already provisioned, google_firestore_database will error if you try to explicitly provision that default instance again.

Once the project's default Cloud Firestore instance is provisioned, you cannot "re-provision" it or change its location. Note that the provisioned database instance is in Datastore mode, which means that it's not accessible to Firebase SDKs, authentication, or Firebase Security Rules. If you want to use Cloud Firestore with these Firebase services, then you'll need to empty the database and then change its database type in the Google Cloud console.