Configuration classique du projet Lakebase avec Terraform

Cette page présente une configuration Terraform complète pour un projet de mise à l’échelle automatique Lakebase prêt pour la production avec les fonctionnalités les plus couramment utilisées :

  • Branche de production protégée
  • Point de terminaison de haute disponibilité en lecture-écriture avec des réplicas secondaires lisibles
  • Principal de service avec les privilèges DATABRICKS_SUPERUSER sur la base de données
  • Base de données Postgres appartenant à l’application
  • Base de données Postgres enregistrée dans Unity Catalog pour les requêtes Lakehouse Federation depuis Databricks SQL et des notebooks
  • Diffusion en continu de tables synchronisées à partir du catalogue Unity
  • Databricks App connecté au projet Lakebase

Pour une présentation pas à pas de Terraform avec Lakebase, consultez Prise en main de Terraform pour Lakebase.

Prerequisites

Avant de commencer la lecture cet article, vous devez disposer des éléments suivants :

Configuration complète

Lorsque vous créez un projet, Azure Databricks crée automatiquement une production branche, un point de terminaison en lecture-écriture, un primary rôle Postgres propriétaire lié à votre identité et une databricks_postgres base de données. Pour configurer ces ressources créées implicitement, déclarez-les dans Terraform avec replace_existing = true. Pour plus d’informations, consultez databricks_postgres_branch, databricks_postgres_endpoint, databricks_postgres_roleet databricks_postgres_database.

Avertissement

Cette configuration définit is_protected = true sur la production branche et inclut une unprotect_for_destroy variable câblée dans la spécification de branche. Terraform ne peut pas supprimer un projet contenant des branches protégées et la production branche ne peut pas être supprimée directement, car son cycle de vie est contrôlé par le projet. Pour détruire les ressources correctement, utilisez une destruction en deux étapes :

# Step 1: unprotect the branch
terraform apply -var="unprotect_for_destroy=true"

# Step 2: destroy all resources
terraform destroy -var="unprotect_for_destroy=true"

Après l’exécution terraform destroy, le projet est supprimé de manière réversible et conservé pendant 7 jours avant la suppression définitive. Pour le supprimer définitivement immédiatement, définissez purge_on_delete = true sur la ressource databricks_postgres_project avant d’exécuter destroy.

variable "admin_sp_app_id" {
  description = "Application ID of the service principal to grant admin access"
  type        = string
}

variable "unprotect_for_destroy" {
  description = "Set to true before destroy to unprotect the production branch"
  type        = bool
  default     = false
}

# Project — top-level container for branches, endpoints, databases, and roles.
resource "databricks_postgres_project" "this" {
  project_id = "my-lakebase-project"
  # purge_on_delete = true  # Uncomment to permanently delete on destroy (default: soft delete, 7-day retention).
  spec = {
    pg_version   = 17
    display_name = "My Lakebase Project"
    default_endpoint_settings = {
      autoscaling_limit_min_cu = 0.5
      autoscaling_limit_max_cu = 4.0
      suspend_timeout_duration = "300s"
    }
  }
}

# Configure the implicitly created production branch as protected.
resource "databricks_postgres_branch" "production" {
  branch_id = "production"
  parent    = databricks_postgres_project.this.name
  spec = {
    no_expiry    = true
    is_protected = var.unprotect_for_destroy ? false : true
  }
  replace_existing = true
}

# Configure the implicitly created primary endpoint with HA.
# HA requires no_suspension = true. group.min = 2 adds a standby for automatic failover.
resource "databricks_postgres_endpoint" "primary" {
  endpoint_id = "primary"
  parent      = databricks_postgres_branch.production.name
  spec = {
    endpoint_type            = "ENDPOINT_TYPE_READ_WRITE"
    autoscaling_limit_min_cu = 0.5
    autoscaling_limit_max_cu = 4.0
    no_suspension            = true
    group = {
      min                         = 2
      max                         = 2
      enable_readable_secondaries = true
    }
  }
  replace_existing = true
}

# Grant workspace-level CAN_MANAGE on the project to the service principal.
# Use status.project_id (bare ID) not .name (full resource path) — the permissions
# API rejects the full path with a "resource type not found" error.
resource "databricks_permissions" "project" {
  database_project_name = databricks_postgres_project.this.status.project_id
  access_control {
    service_principal_name = var.admin_sp_app_id
    permission_level       = "CAN_MANAGE"
  }
}

# Create a Postgres role backed by the service principal with full database privileges.
# depends_on serializes creation — Lakebase processes one branch operation at a time.
resource "databricks_postgres_role" "admin_sp" {
  role_id = "admin-sp"
  parent  = databricks_postgres_branch.production.name
  spec = {
    identity_type    = "SERVICE_PRINCIPAL"
    postgres_role    = var.admin_sp_app_id
    auth_method      = "LAKEBASE_OAUTH_V1"
    membership_roles = ["DATABRICKS_SUPERUSER"]
    attributes = {
      createdb   = true
      createrole = true
      bypassrls  = true
    }
  }
  depends_on = [databricks_postgres_endpoint.primary]
}

# Create a Postgres database owned by the admin SP role.
resource "databricks_postgres_database" "app" {
  database_id = "app"
  parent      = databricks_postgres_branch.production.name
  spec = {
    postgres_database = "app"
    role              = databricks_postgres_role.admin_sp.name
  }
}

# Register the Postgres database in Unity Catalog. This makes the database queryable
# from Databricks SQL and notebooks through Lakehouse Federation, and serves as the
# parent namespace for synced tables that live inside the Lakebase Catalog.
# create_database_if_missing is set explicitly because the database is managed by
# the databricks_postgres_database resource above.
resource "databricks_postgres_catalog" "app_catalog" {
  catalog_id = "app_catalog"
  spec = {
    postgres_database          = databricks_postgres_database.app.status.postgres_database
    branch                     = databricks_postgres_branch.production.name
    create_database_if_missing = false
  }
}

# Sync a Unity Catalog Delta table into the Lakebase database continuously.
# Prefixing synced_table_id with the Lakebase Catalog name places the synced table
# inside the catalog so it's discoverable alongside the rest of the catalog's contents.
# postgres_database references the catalog's status, which implicitly orders this
# resource after the catalog without an explicit depends_on.
resource "databricks_postgres_synced_table" "orders" {
  synced_table_id = "app_catalog.default.orders_synced"
  spec = {
    branch                             = databricks_postgres_branch.production.name
    postgres_database                  = databricks_postgres_catalog.app_catalog.status.postgres_database
    source_table_full_name             = "my_catalog.default.orders"
    primary_key_columns                = ["order_id"]
    scheduling_policy                  = "CONTINUOUS"
    create_database_objects_if_missing = true
    new_pipeline_spec = {
      storage_catalog = "my_catalog"
      storage_schema  = "default"
    }
  }
}

# Databricks App connected to the Lakebase project.
# database must be the full resource name (databricks_postgres_database.app.name),
# not the Postgres database name. permission must be "CAN_CONNECT_AND_CREATE".
resource "databricks_app" "this" {
  name        = "my-lakebase-app"
  description = "App backed by Lakebase autoscaling project"
  depends_on  = [databricks_postgres_database.app]
  resources = [{
    name = "lakebase-db"
    postgres = {
      branch     = databricks_postgres_branch.production.name
      database   = databricks_postgres_database.app.name
      permission = "CAN_CONNECT_AND_CREATE"
    }
  }]
}

Note

Cette configuration crée un rôle distinct admin_sp et une base de données distincte app plutôt que de gérer le rôle propriétaire implicite et la base de données databricks_postgres. Pour que ces ressources implicites soient gérées par Terraform, déclarez-les avec replace_existing = true à l’aide de leurs ID existants. L’ID de base de données est toujours databricks-postgres. L’ID de rôle est dérivé de l’identité qui a créé la branche : partie de l’e-mail avant @ (caractères minuscules, non alphanumériques remplacés par des traits d’union) pour un utilisateur ou sp-<application-id> pour un principal de service. Si vous n’êtes pas sûr de la valeur exacte, lisez-la à partir de l’interface utilisateur Lakebase ou de l’API Postgres au lieu de la dériver manuellement.

spec.membership_roles remplace les appartenances du rôle à chaque application plutôt que de les fusionner avec elles. Conservez DATABRICKS_SUPERUSER dans la liste ; si vous l’omettez, toutes les appartenances associées au rôle seront supprimées.

resource "databricks_postgres_role" "owner" {
  role_id = "jane-doe" # normalized login of the creating identity
  parent  = databricks_postgres_branch.production.name
  spec = {
    postgres_role    = "jane.doe@databricks.com" # the raw login
    membership_roles = ["DATABRICKS_SUPERUSER"]
    attributes = {
      createdb   = true
      createrole = true
      bypassrls  = true
    }
  }
  replace_existing = true
}

resource "databricks_postgres_database" "databricks_postgres" {
  database_id = "databricks-postgres"
  parent      = databricks_postgres_branch.production.name
  spec = {
    postgres_database = "databricks_postgres"
    # spec.role is omitted, so the database keeps its existing owner.
  }
  replace_existing = true
}

Ressources supplémentaires