Databricks DE · Topic 11.1
Infrastructure-as-code for the lakehouse — declare jobs, pipelines & resources in databricks.yml and deploy to dev/prod targets via the CLI, with source control & CI/CD.
▸ init → validate → deploy → run → destroy
databricks.yml skeleton — top-level mappingsMechanism: one root file defines the bundle. Top-level mappings: bundle, include, variables, artifacts, resources, targets, sync, run_as, workspace, permissions. Only bundle.name is strictly required. Trade-off: a monolith gets unwieldy — split with include.
# databricks.yml — the root file (only bundle.name is strictly required) bundle: name: sales_platform # logical name; namespaces deployed resources variables: warehouse_id: description: SQL warehouse for SQL tasks default: "" # overridable per target / via --var
include, artifacts, syncMechanism: include globs in extra YAML files; artifacts builds things at deploy (e.g. a wheel); sync picks which local paths upload. Trade-off: include paths are relative to root and top-level only — a mis-scoped glob silently includes nothing.
include: - resources/*.yml # jobs.yml, pipelines.yml, dashboards.yml … artifacts: sales_wheel: type: whl path: ./libs/sales # built and uploaded on deploy sync: paths: [ ./src ] # local code synced to the workspace
Mechanism: under resources, each type (jobs, pipelines, dashboards, alerts, sql_warehouses, models…) holds named declarations mapping to the REST API object. Trade-off: the bundle owns them — UI edits revert on the next deploy.
resources: jobs: sales_etl: name: sales-etl tasks: - task_key: ingest notebook_task: { notebook_path: ./src/ingest.py } - task_key: report sql_task: warehouse_id: ${var.warehouse_id} file: { path: ./sql/report.sql } depends_on: [{ task_key: ingest }] pipelines: sales_sdp: name: sales-sdp libraries: [{ notebook: { path: ./src/pipeline.py } }]
Mechanism: targets declares each environment. mode: development namespaces resources per user & pauses schedules (safe to iterate); mode: production uses real names + active schedules. One target is default. Trade-off: wrong target is easy — dev mode renames resources, so check -t.
targets: dev: mode: development # prefixes names, pauses schedules — safe to iterate default: true workspace: { host: https://dev.cloud.databricks.com } prod: mode: production # real names, schedules active workspace: { host: https://prod.cloud.databricks.com } run_as: { service_principal_name: ${var.sp_app_id} } # who prod runs as
Mechanism: variables set defaults; any target overrides them, and --var overrides at the CLI. Reference with ${var.name} (and ${resources....id} to wire resources). Trade-off: empty vars surface only at validate/deploy — set defaults, validate in CI.
# top-level default above; override inside a target: targets: prod: variables: warehouse_id: 0a1b2c3d4e5f6789 # prod SQL warehouse # deploy-time override: # databricks bundle deploy -t prod --var="warehouse_id=..."
Mechanism: init scaffolds; validate checks (warns on unknown props); summary/plan preview; deploy -t pushes; run executes; destroy -t tears down. Trade-off: destroy is irreversible — guard it in automation.
databricks bundle init # scaffold from a template databricks bundle validate # check config (run in CI on PRs) databricks bundle deploy -t dev # push to the dev target databricks bundle run sales_etl -t dev # execute a deployed job databricks bundle deploy -t prod # promote (usually CI on merge) databricks bundle destroy -t dev # tear down dev (irreversible)
▸ click a target to see the deploy command & behavior
databricks.yml lives in Git; a GitHub Action runs bundle validate on PRs and bundle deploy -t prod on merge — prod always matches reviewed code, no manual UI edits.bundle generate + deployment bind.development vs production mode changes behavior (dev prefixes names, pauses schedules); run_as sets the prod identity (use a service principal).deploy overwrites it; the bundle is the source of truth.-t.validate in CI → broken config reaches the workspace.destroy against prod by habit — it's irreversible; gate it.