tuschiev.com
Back to Blog
Databricks Data Engineering DevOps CI/CD

The DAB Readiness Blueprint: From Zero to First Green Pipeline in a Day

Nikolay Tuschiev Nikolay Tuschiev
Dec 07, 2024 • 12 min read

This guide walks through setting up your first Databricks Asset Bundle—from prerequisites to a working CI/CD pipeline.

What You’ll Learn & Why DABs Matter Now

Databricks Asset Bundles (DABs) represent a paradigm shift in how we deploy and manage Databricks workloads. Instead of manually configuring jobs through the UI or maintaining fragile deployment scripts, DABs provide:

  • Infrastructure as Code for all Databricks resources
  • Environment parity across dev, QA, and production
  • Git-native workflows with branch-based deployments
  • Reproducible deployments that eliminate “works on my cluster” issues

The timing is critical. As data teams scale, the gap between ad-hoc notebook development and production-grade engineering widens. DABs bridge that gap with minimal overhead.

Readiness Signals

Before diving into bundle configuration, assess your organization’s readiness across three dimensions.

Versioned Codebase

Your code must live in version control. This seems obvious, but many data teams still operate with notebooks as the source of truth.

Minimum requirements:

  • Git repository (GitHub, GitLab, Azure DevOps)
  • Branching strategy defined (GitFlow, trunk-based, or similar)
  • Code review process established
  • Basic test coverage for critical transformations
# Verify your repo structure
my-databricks-project/
├── src/
│   ├── pipelines/
│   │   ├── bronze/
│   │   ├── silver/
│   │   └── gold/
│   └── utils/
├── tests/
│   └── unit/
├── databricks.yml
└── README.md

Environments

You need clearly defined environments with corresponding Databricks workspaces.

EnvironmentPurposeDatabricks Workspace
devIndividual developmentdev-workspace.cloud.databricks.com
qaIntegration testingqa-workspace.cloud.databricks.com
prodProduction workloadsprod-workspace.cloud.databricks.com

Each environment should have its own Unity Catalog, secret scopes, and cluster policies configured.

Secrets & Cluster Policies

Two often-overlooked prerequisites that block first deployments:

Secret Scopes: Store credentials for external systems (databases, APIs, storage accounts).

# Create a secret scope (one-time setup per workspace)
databricks secrets create-scope --scope my-project-secrets

# Add secrets
databricks secrets put --scope my-project-secrets --key storage-account-key

Cluster Policies: Define guardrails for compute resources. Without policies, DABs can’t create clusters.

{
  "spark_version": {
    "type": "fixed",
    "value": "14.3.x-scala2.12"
  },
  "node_type_id": {
    "type": "allowlist",
    "values": ["Standard_DS3_v2", "Standard_DS4_v2"]
  },
  "autoscale.min_workers": {
    "type": "range",
    "minValue": 1,
    "maxValue": 4
  }
}

The Minimal DAB Skeleton

databricks.yml Anatomy

The databricks.yml file is the heart of your bundle. Here’s the 80/20 configuration that covers most use cases:

bundle:
  name: my-data-pipeline

variables:
  catalog:
    description: Unity Catalog name
    default: dev_catalog
  schema:
    description: Schema for pipeline tables
    default: bronze

targets:
  dev:
    mode: development
    default: true
    workspace:
      host: https://dev-workspace.cloud.databricks.com
    variables:
      catalog: dev_catalog

  qa:
    workspace:
      host: https://qa-workspace.cloud.databricks.com
    variables:
      catalog: qa_catalog

  prod:
    mode: production
    workspace:
      host: https://prod-workspace.cloud.databricks.com
    variables:
      catalog: prod_catalog
    run_as:
      service_principal_name: prod-deployer-sp

resources:
  jobs:
    daily_ingestion:
      name: "[${bundle.target}] Daily Data Ingestion"
      schedule:
        quartz_cron_expression: "0 0 6 * * ?"
        timezone_id: UTC
      tasks:
        - task_key: bronze_load
          notebook_task:
            notebook_path: ./src/pipelines/bronze/ingest.py
          new_cluster:
            spark_version: 14.3.x-scala2.12
            node_type_id: Standard_DS3_v2
            num_workers: 2
            spark_conf:
              spark.databricks.catalog: ${var.catalog}

Key points:

  • bundle.name: Unique identifier for your project
  • variables: Parameterize everything that differs between environments
  • targets: Define per-environment configurations
  • resources: Declare your jobs, pipelines, and experiments

Resource Types

DABs support multiple resource types. Choose based on your use case:

Resource TypeUse CaseDeclaration
jobsScheduled or triggered workflowsresources.jobs
pipelinesDelta Live Tablesresources.pipelines
experimentsMLflow experimentsresources.experiments
modelsMLflow model registryresources.models
schemasUnity Catalog schemasresources.schemas

Local to Remote Loop

CLI Commands

The Databricks CLI provides three essential commands for the development loop:

# Validate bundle syntax and configuration
databricks bundle validate

# Deploy resources to target workspace
databricks bundle deploy --target dev

# Run a specific job or pipeline
databricks bundle run --target dev daily_ingestion

The typical development workflow:

  1. Make changes to code or configuration
  2. databricks bundle validate - catch errors early
  3. databricks bundle deploy --target dev - push to dev workspace
  4. databricks bundle run --target dev job_name - test execution
  5. Review logs and iterate

Target-Driven Parameters

Leverage variables to avoid branch-specific YAML. Never hardcode environment-specific values:

variables:
  storage_account:
    description: Azure Storage Account
  container:
    description: Container name

targets:
  dev:
    variables:
      storage_account: devstorageacct
      container: raw-dev
  prod:
    variables:
      storage_account: prodstorageacct
      container: raw-prod

resources:
  jobs:
    ingestion:
      tasks:
        - task_key: load
          notebook_task:
            notebook_path: ./src/ingest.py
            base_parameters:
              storage_path: "abfss://${var.container}@${var.storage_account}.dfs.core.windows.net/"

This approach means the same databricks.yml works across all environments without modification.

CI Fast Path

GitHub Actions Workflow

Here’s a production-ready GitHub Actions workflow that validates on PR and deploys on merge:

name: DAB CI/CD

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

env:
  DATABRICKS_HOST: ${{ secrets.DATABRICKS_HOST }}
  DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_TOKEN }}

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Databricks CLI
        run: |
          curl -fsSL https://raw.githubusercontent.com/databricks/setup-cli/main/install.sh | sh

      - name: Validate Bundle
        run: databricks bundle validate

      - name: Run Tests
        run: |
          pip install pytest
          pytest tests/ -v

  deploy-qa:
    needs: validate
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: qa
    steps:
      - uses: actions/checkout@v4

      - name: Install Databricks CLI
        run: |
          curl -fsSL https://raw.githubusercontent.com/databricks/setup-cli/main/install.sh | sh

      - name: Deploy to QA
        run: databricks bundle deploy --target qa

      - name: Run Smoke Tests
        run: databricks bundle run --target qa smoke_test_job

  deploy-prod:
    needs: deploy-qa
    runs-on: ubuntu-latest
    environment: prod
    steps:
      - uses: actions/checkout@v4

      - name: Install Databricks CLI
        run: |
          curl -fsSL https://raw.githubusercontent.com/databricks/setup-cli/main/install.sh | sh

      - name: Deploy to Production
        run: databricks bundle deploy --target prod

Artifact & Promotion Strategy

For more complex deployments, consider artifact-based promotion:

  1. Build Phase: Package your Python code as a wheel
  2. Publish Phase: Upload to an artifact repository (Nexus, Azure Artifacts) or simply store in a Unity Catalog Volume—the easiest option
  3. Deploy Phase: Reference versioned artifacts in your bundle
resources:
  jobs:
    production_pipeline:
      tasks:
        - task_key: transform
          python_wheel_task:
            package_name: my_transforms
            entry_point: main
          libraries:
            - whl: /Volumes/prod_catalog/artifacts/my_transforms-1.2.0-py3-none-any.whl

This decouples deployment from code changes, enabling rollbacks by simply redeploying a previous artifact version.

Troubleshooting First Run

Here are the top 5 errors teams encounter on their first DAB deployment:

1. Authentication Failure

Error: Error: cannot configure default credentials

Fix: Ensure your authentication is configured. For local development, use OAuth (recommended):

databricks auth login --host https://your-workspace.cloud.databricks.com

For CI/CD, use a service principal with DATABRICKS_HOST and DATABRICKS_CLIENT_ID/DATABRICKS_CLIENT_SECRET environment variables, or DATABRICKS_TOKEN for simpler setups.

2. Missing Cluster Policy

Error: INVALID_PARAMETER_VALUE: Cluster policy not found

Fix: Create a cluster policy in your workspace and reference it:

new_cluster:
  policy_id: E0123456789ABCDEF

3. Permission Denied on Secrets

Error: PERMISSION_DENIED: User does not have permission to access secret scope

Fix: Grant READ permission on the secret scope:

databricks secrets put-acl --scope my-scope --principal users --permission READ

4. Invalid Notebook Path

Error: RESOURCE_DOES_NOT_EXIST: Notebook not found

Fix: Paths are relative to bundle root. Verify:

  • Notebook exists at specified path
  • File extension is correct (.py, not .ipynb)
  • Path starts with ./ for relative paths

5. Variable Not Defined for Target

Error: Variable 'catalog' is not defined for target 'prod'

Fix: Ensure all required variables have values for each target:

targets:
  prod:
    variables:
      catalog: prod_catalog  # Don't forget this!
Nikolay Tuschiev

Nikolay Tuschiev

Senior Data Engineer