Comienza a usar Terraform y Firebase

Firebase comenzará a ser compatible con Terraform. Si perteneces a un equipo que desea automatizar y estandarizar la creación de proyectos de Firebase con recursos específicos aprovisionados y servicios habilitados, usar Terraform con Firebase es una buena opción para ti.

El flujo de trabajo básico para usar Terraform con Firebase incluye las siguientes tareas:

  • Crear y personalizar un archivo de configuración de Terraform (un archivo .tf) que especifique la infraestructura que deseas aprovisionar (es decir, los recursos que deseas aprovisionar y los servicios que deseas habilitar)

  • Usar comandos gcloud CLI que interactúan con Terraform para aprovisionar la infraestructura especificada en el archivo .tf.

¿Qué puedes hacer con Terraform y Firebase?

El ejemplo de flujo de trabajo generalizado en esta guía consiste en crear un nuevo proyecto de Firebase con una app para Android. Sin embargo, puedes hacer mucho más con Terraform, por ejemplo:

  • Borrar y modificar la infraestructura existente con Terraform

  • Administrar parámetros de configuración y tareas específicas de productos con Terraform, como en los siguientes ejemplos:

    • Habilita los proveedores de acceso de Firebase Authentication.
    • Crear buckets de Cloud Storage o instancias de bases de datos y, luego, implementar Firebase Security Rules para ellos.

Puedes usar comandos y archivos de configuración estándar de Terraform para realizar todas estas tareas. A fin de ayudarte con esto, proporcionamos archivos de configuración de Terraform de muestra para varios casos de uso comunes.



Flujo de trabajo generalizado para usar Terraform con Firebase

Requisitos previos

Esta guía es una introducción al uso de Terraform con Firebase, por lo que se supone que tienes conocimientos básicos sobre Terraform. Asegúrate de que cumples con los siguientes requisitos previos antes de comenzar este flujo de trabajo.

  • Instala Terraform y familiarízate con él mediante sus instructivos oficiales.

  • Instala Google Cloud CLI (gcloud CLI). Accede con una cuenta de usuario o una cuenta de servicio.

    • Si usas una cuenta de usuario, debes aceptar las Condiciones del Servicio de Firebase. Aceptaste las Condiciones del Servicio de Firebase si puedes ver un proyecto de Firebase en Firebase console
    • Para que Terraform realice ciertas acciones (por ejemplo, crear proyectos), se deben cumplir los siguientes requisitos:
      • El usuario o la cuenta de servicio deben tener acceso de IAM aplicable para esas acciones.
      • Si el usuario o la cuenta de servicio forman parte de una organización de Google Cloud, las políticas de la organización deben permitir que la cuenta realice esas acciones.


Paso 1: Crea y personaliza un archivo de configuración de Terraform

Un archivo de configuración de Terraform necesita dos secciones principales (que se describen en detalle a continuación):

Configura tu provider

Se requiere una configuración de provider, sin importar los productos o servicios de Firebase que participen.

  1. Crea un archivo de configuración de Terraform (como main.tf) en el directorio local.

    En esta guía, usarás este archivo de configuración para especificar la configuración de provider y toda la infraestructura que deseas que cree Terraform. Sin embargo, ten en cuenta que tienes opciones para incluir la configuración del proveedor.

    Tienes las siguientes opciones para incluir una configuración de provider en el resto de la configuración de Terraform:

    • Opción 1: Inclúyela en la parte superior de un solo archivo de configuración .tf de Terraform (como se muestra en esta guía).

      • Usa esta opción si recién comienzas a usar Terraform o estás probando Terraform con Firebase.
    • Opción 2: Inclúyela en un archivo .tf independiente (como un archivo provider.tf), además del archivo .tf, en el que especificas la infraestructura que se creará (como un archivo main.tf).

      • Usa esta opción si formas parte de un equipo más grande que necesita estandarizar la configuración.
      • Cuando se ejecutan comandos de Terraform, el archivo provider.tf y el archivo main.tf deben estar en el mismo directorio.

  2. Incluye la siguiente configuración de provider en la parte superior del archivo main.tf.

    Debes usar el proveedor google-beta porque esta es una versión beta de cómo usar Firebase con Terraform. Ten cuidado cuando lo uses en producción.

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

    Obtén más información sobre los diferentes tipos de atributos relacionados con el proyecto (incluido lo que en esta guía se llama el “proyecto de verificación de cuotas”) cuando usas Terraform con Firebase.

  3. Continúa con la siguiente sección para completar el archivo de configuración y especificar qué infraestructura crear.

Especifica qué infraestructura crear con bloques resource

En tu archivo de configuración de Terraform (para esta guía, tu archivo main.tf), debes especificar toda la infraestructura que deseas que Terraform cree (es decir, todos los recursos que deseas aprovisionar y todos los servicios que deseas habilitar). En esta guía, encontrarás una lista completa de todos los recursos de Firebase que son compatibles con Terraform.

  1. Abre el archivo main.tf.

  2. En la configuración de provider, incluye la siguiente configuración de bloques resource.

    En este ejemplo básico, se crea un proyecto de Firebase nuevo y, luego, una app para Android de Firebase dentro de ese proyecto.

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

Si no estás familiarizado con la infraestructura de proyectos y apps como recursos, revisa la siguiente documentación:

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


Paso 2: Ejecuta los comandos de Terraform para crear la infraestructura especificada

Para aprovisionar los recursos y habilitar los servicios especificados en el archivo main.tf, ejecuta los siguientes comandos desde el mismo directorio que contiene el archivo main.tf. Para obtener información detallada sobre estos comandos, consulta la documentación de Terraform.

  1. Si es la primera vez que ejecutas comandos de Terraform en el directorio, debes inicializar el directorio de configuración y, luego, instalar el proveedor de Google Terraform. Para ello, ejecuta el siguiente comando:

    terraform init
  2. Ejecuta el siguiente comando para crear la infraestructura especificada en el archivo main.tf:

    terraform apply
  3. Confirma que todo esté aprovisionado o habilitado como se esperaba:

    • Opción 1: Ejecuta el siguiente comando para ver la configuración impresa en la terminal:

      terraform show
    • Opción 2: Consulta el proyecto de Firebase en Firebase console.



Recursos de Firebase compatibles con Terraform

Los siguientes recursos de Firebase y Google son compatibles con Terraform. Además, agregamos más recursos todo el tiempo. Por lo tanto, si no ves el recurso que quieres administrar con Terraform, vuelve a consultar pronto para ver si está disponible o solicítalo presentando un problema en el repositorio de GitHub.


Administración de apps y proyectos de Firebase

  • google_firebase_project: Habilita los servicios de Firebase en un proyecto de Google Cloud existente

  • Apps de Firebase


Firebase Authentication

  • google_identity_platform_config: Habilita Google Cloud Identity Platform (GCIP), que es el backend para Firebase Authentication), y proporciona la configuración de autenticación a nivel de proyecto.

    • La configuración de Firebase Authentication a través de Terraform requiere la habilitación de GCIP. Asegúrate de revisar el archivo .tf de muestra para ver cómo configurar Firebase Authentication.

    • El proyecto en el que Terraform habilitará GCIP o Firebase Authentication debe estar en el plan de precios Blaze (es decir, el proyecto debe tener asociada una cuenta de Cloud Billing). Puedes hacerlo de manera programática; para ello, configura el atributo billing_account en el recurso google_project.

    • Este recurso también habilita más parámetros de configuración, como métodos de acceso locales, como el acceso anónimo, el acceso con correo electrónico o contraseña, y el acceso con autenticación por teléfono, así como funciones de bloqueo y dominios autorizados.

  • google_identity_platform_default_supported_idp_config: Configura proveedores de identidad federada comunes, como Google, Facebook o Apple.

  • identity_platform_oauth_idp_config: Configura las fuentes arbitrarias de proveedores de identidad (IdP) de OAuth.

  • google_identity_platform_inbound_saml_config: Configura integraciones de SAML.

Aún no se admite:

  • Configurar la autenticación de varios factores (MFA) con Terraform

Firebase Realtime Database

Aún no se admite:

  • Implementar Firebase Realtime Database Security Rules a través de Terraform (obtén información para implementar estas Rules con otras herramientas, incluidas las opciones programáticas)

Cloud Firestore

  • google_firestore_database: Crea una instancia de Cloud Firestore

  • google_firestore_index: Habilita consultas eficaces para Cloud Firestore

  • google_firestore_document: Propaga una instancia de Cloud Firestore con un documento específico en una colección

    Importante: No uses datos reales de usuario final o de producción en este documento de origen.


Cloud Storage for Firebase

  • google_firebase_storage_bucket: Hace que un bucket de Cloud Storage existente sea accesible para los SDK de Firebase, la autenticación y Firebase Security Rules

  • google_storage_bucket_object: Agrega un objeto a un bucket de Cloud Storage

    Importante: No uses datos reales de usuarios finales o de producción en este archivo.


Firebase Security Rules (para Cloud Firestore y Cloud Storage)

Ten en cuenta que Firebase Realtime Database usa un sistema de aprovisionamiento diferente para su Firebase Security Rules.

  • google_firebaserules_ruleset: Define las Firebase Security Rules que se aplican a una instancia de Cloud Firestore o a un bucket de Cloud Storage

  • google_firebaserules_release: Implementa conjuntos de reglas específicos en una instancia de Cloud Firestore o en un bucket de Cloud Storage


Firebase App Check


Firebase Extensions



Archivos de configuración de Terraform de muestra para casos de uso comunes

Con esta configuración, se crea un proyecto de Google Cloud nuevo, se lo asocia con una cuenta de Cloud Billing (se requiere el plan de precios Blaze para Firebase Authentication con GCIP), se habilitan los servicios de Firebase para el proyecto, se configura Firebase Authentication con GCIP y se registran tres tipos de apps diferentes con el proyecto.

Ten en cuenta que es necesario habilitar GCIP para configurar Firebase Authentication a través de 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,
  ]
}

Con esta configuración, se crea un proyecto de Google Cloud nuevo, se habilitan los servicios de Firebase para el proyecto, se aprovisiona la instancia predeterminada de Realtime Database del proyecto y se registran tres tipos de apps diferentes con el proyecto.

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

Con esta configuración, se crea un proyecto de Google Cloud nuevo, se lo asocia con una cuenta de Cloud Billing (se requiere el plan de precios Blaze para varias instancias de Realtime Database), se habilitan los servicios de Firebase para el proyecto, se aprovisionan varias instancias de Realtime Database (incluida la instancia predeterminada de Realtime Database del proyecto) y se registran tres tipos de apps diferentes con el proyecto.

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

Con esta configuración, se crea un proyecto de Google Cloud nuevo, se habilitan los servicios de Firebase para el proyecto, se aprovisiona la instancia predeterminada de Cloud Firestore del proyecto y se registran tres tipos de apps diferentes con el proyecto.

También aprovisiona Firebase Security Rules para la instancia predeterminada de Cloud Firestore, crea un índice de Cloud Firestore y agrega un documento de Cloud Firestore con datos de origen.

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

Este es el conjunto de reglas de Cloud Firestore Security Rules que debería estar en un archivo local llamado firestore.rules.

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

Con esta configuración, se crea un proyecto de Google Cloud nuevo, se lo asocia con una cuenta de Cloud Billing (se requiere el plan de precios Blaze para buckets adicionales), se habilitan los servicios de Firebase para el proyecto, se aprovisionan buckets de Cloud Storage adicionales que no son predeterminados y se registran tres tipos de apps diferentes con el proyecto.

También se aprovisiona Firebase Security Rules para cada bucket de Cloud Storage y se sube un archivo a uno de los buckets de 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,
  ]
}

Este es el conjunto de reglas de Cloud Storage Security Rules que debería estar en un archivo local llamado storage.rules.

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

Con esta configuración, se crea un proyecto de Google Cloud nuevo, se habilitan los servicios de Firebase para el proyecto y se configura y habilita la aplicación forzosa de Firebase App Check para Cloud Firestore, de modo que solo se pueda acceder a ella desde tu app para 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]
}

Con esta configuración, se crea un proyecto de Google Cloud nuevo, se habilitan los servicios de Firebase para el proyecto y se instala una instancia nueva de una Firebase Extension en el proyecto. Si la instancia ya existe, sus parámetros se actualizan según los valores proporcionados en la configuración.

# 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"
    }
  }
}



Solución de problemas y preguntas frecuentes

En esta guía, se usan los siguientes atributos de Terraform cuando se trabaja con “proyectos”.

project dentro de un bloque resource

Recomendado: Siempre que sea posible, incluye el atributo project en cada bloque resource.

Cuando incluyes un atributo de proyecto, Terraform creará la infraestructura especificada en el bloque de recursos del proyecto indicado. En esta guía y en nuestros archivos de configuración de muestra, se usa esta práctica.

Consulta la documentación oficial de Terraform sobre project.

user_project_override dentro del bloque provider

Para aprovisionar la mayoría de los recursos, debes usar user_project_override = true, lo que significa que debes verificar la cuota en tu propio proyecto de Firebase. Sin embargo, si quieres que tu proyecto acepte verificaciones de cuotas, debes debes usar user_project_override = false.

Consulta la documentación oficial de Terraform sobre user_project_override.

Asegúrate de que en la cuenta de usuario que usas para ejecutar comandos de gcloud CLI se acepten las Condiciones del Servicio de Firebase.

  • Para verificarlo, usa un navegador en el que hayas accedido a la cuenta del usuario y revisa si puedes ver un proyecto de Firebase en Firebase console. Si es así, la cuenta de usuario aceptó las Condiciones del Servicio de Firebase.

  • Si no puedes ver ningún proyecto de Firebase, es probable que la cuenta de usuario no haya aceptado las Condiciones del Servicio de Firebase. Para solucionar este problema, crea un proyecto de Firebase nuevo a través de Firebase console y acepta las Condiciones del Servicio de Firebase como parte de su creación. Puedes borrar este proyecto de inmediato en la Configuración del proyecto de la consola.

Espera unos minutos y vuelve a intentar ejecutar terraform apply.

Esto podría deberse a una demora en la propagación en varios sistemas. Para resolver este problema, importa el recurso al estado de Terraform mediante la ejecución del siguiente comando: terraform import. Luego, vuelve a ejecutar terraform apply.

Puedes aprender a importar cada recurso en la sección “Importar” de la documentación de Terraform (por ejemplo, la documentación de "importación" para Cloud Firestore).

Como sugiere el error, Terraform puede intentar aprovisionar varios índices o crear un documento al mismo tiempo, y encontrar un error de simultaneidad. Vuelve a ejecutar terraform apply.

Este error significa que Terraform no sabe en qué proyecto verificar la cuota. Para solucionar el problema, verifica lo siguiente en el bloque resource:

  • Asegúrate especificar un valor para el atributo project.
  • Asegúrate de usar el proveedor con user_project_override = true (sin alias), que aparece como google-beta en las muestras de Firebase.

Estos son algunos de los motivos por los que es posible que ya exista el ID del proyecto:

  • El proyecto asociado con ese ID le pertenece a otra persona.

    • Solución: Elige otro ID del proyecto.
  • El proyecto asociado con ese ID se borró recientemente (en estado de eliminación no definitiva).

  • El proyecto asociado con ese ID existe correctamente en el usuario actual. Una posible causa del error podría ser que se interrumpió un terraform apply anterior.

    • Solución: Ejecuta los siguientes comandos:
      terraform import google_project.default PROJECT_ID y, luego,
      terraform import google_firebase_project.default PROJECT_ID

Si aprovisionaste tu bucket predeterminado de Cloud Storage (a través de google_app_engine_application) antes de intentar aprovisionar tu instancia predeterminada de Cloud Firestore, verás que dicha instancia predeterminada de Cloud Firestore ya se aprovisionó. Ten en cuenta que la instancia de base de datos aprovisionada está en modo Datastore, lo que significa que ni los SDKs de Firebase, la autenticación ni Firebase Security Rules podrán acceder a ella. Si quieres usar Cloud Firestore con estos servicios de Firebase, deberás vaciar la base de datos y, luego, cambiar su tipo de base de datos en la consola de Google Cloud.

Cuando aprovisionas el bucket predeterminado de Cloud Storage de un proyecto (a través de google_app_engine_application) y el proyecto aún no tiene la instancia predeterminada de Cloud Firestore, google_app_engine_application aprovisiona automáticamente la instancia predeterminada de Cloud Firestore del proyecto.

Por lo tanto, como la instancia predeterminada de Cloud Firestore de tu proyecto ya se aprovisionó, google_firestore_database fallará si intentas aprovisionar de manera explícita esa instancia predeterminada de nuevo.

Una vez que se aprovisiona la instancia predeterminada de Cloud Firestore del proyecto, no puedes “aprovisionarla de nuevo” ni cambiar su ubicación. Ten en cuenta que la instancia de base de datos aprovisionada está en modo Datastore, lo que significa que ni los SDKs de Firebase, la autenticación ni Firebase Security Rules podrán acceder a ella. Si quieres usar Cloud Firestore con estos servicios de Firebase, deberás vaciar la base de datos y, luego, cambiar su tipo de base de datos en la consola de Google Cloud.