Stages 2–7 locked the doors and hallways (planes, SCC, Private Link, NCC, firewalls). Stage 8 protects what's inside the rooms — the data and the workload — and installs the cameras. Four subtopics, four customer answers: encryption & key custody (8.1), serverless tenant/user isolation (8.2), the certified compliance posture (8.3), and the audit trail + alerting (8.4).
Locked safes & sealed envelopes (8.1) · a secure hotel with no master key (8.2) · Russian dolls in a certified building (8.3) · one audit stream, two taps (8.4). Encryption is on by default and CMK only changes who holds the key; serverless isolation is proven by architecture — scoped, short-lived tokens and wiped compute; CSP is the permanent certified posture you scope to regulated workspaces; and you prove all of it with system tables + a SIEM export.
| Subtopic | What it protects | Path it touches | The one-liner |
|---|---|---|---|
| 8.1 Encryption / CMK | Bytes in motion + at rest; key custody | Rides all three paths | "On by default; CMK = your key, your kill switch." |
| 8.2 Isolation | One tenant/user can't touch another | Path ③ + in-box under all | "Scoped ~1h token, used direct to storage, wiped after." |
| 8.3 ESC/ESM/CSP | Hardened, certified compute posture | Path ② + classic VMs | "CSP is a one-way door — scope it to regulated data." |
| 8.4 Audit/monitoring | Evidence + alerting | Watches all three | "Run both taps — account events live only in system tables." |
"Your data is encrypted in transit and at rest by default; with CMK you hold the key and the kill switch. Serverless isolation is proven by architecture — dedicated compute wiped between workloads, and short-lived, path-scoped tokens used directly to your ADLS. Regulated data runs under the Compliance Security Profile, and every action is captured in an immutable audit trail you can query in SQL and stream to your SIEM."
Encryption is on by default — TLS 1.2+ in transit, AES-256 at rest. CMK doesn't add a second safe; it swaps whose key opens the existing one from Microsoft's to yours in Key Vault. Switch the diagram between the three CMK features and the TLS-in-transit view; click any box for the "what + why."
None of the three CMK features encrypt your ADLS Gen2 data lake — that's a separate storage account you CMK directly in Azure Storage. CMK-for-DBFS-root only covers the workspace root container (which you shouldn't use for governed data anyway).
| Dimension | Microsoft-managed (default) | CMK |
|---|---|---|
| Who holds the key | Microsoft | You, in Azure Key Vault |
| Setup | None — automatic | Premium + Key Vault + grant + workspace config |
| Revocation ("kill switch") | No | Yes — disable/delete key → data unreadable |
| Re-encrypts existing data | n/a | No — future writes only |
| When to use | Most workloads; "is it encrypted?" = yes | Regulated data needing customer key custody |
Terraform (azurerm) — the encryption arguments. Verify exact names against current provider docs; full CMK + Key Vault IaC is the Stage 8 hands-on artifact.
resource "azurerm_databricks_workspace" "this" {
sku = "premium" # CMK requires Premium
managed_services_cmk_key_vault_key_id = azurerm_key_vault_key.ms.id # control-plane data
managed_disk_cmk_key_vault_key_id = azurerm_key_vault_key.disk.id # classic VM disks
customer_managed_key_enabled = true # DBFS-root CMK
infrastructure_encryption_enabled = true # DOUBLE encryption (deploy-time only)
}
Portal: Entra ID → Enterprise apps → AzureDatabricks · Key Vault (purge protection) →
RSA key → IAM → Key Vault Crypto Service Encryption User to the app + storage MI · Create workspace →
Encryption tab → Use your own key → paste the Key Identifier.
Managed-services CMK cannot be disabled; double encryption is deploy-time only;
use a specific key version (never latest); and a deleted key without purge protection
permanently bricks the data and workspace login. Inter-node shuffle is unencrypted by default —
use Azure VNet encryption.
The serverless trust question: "if compute runs in Databricks' account, what stops another tenant — or Databricks — from reading our data?" The answer is architecture, not your perimeter. Switch the diagram between the scoped-token data flow and the isolation layers; click any box.
Authorization happens before the credential is minted, the credential is scoped and short-lived (~1h), and the control plane hands it over but is never on the data path. A stolen token buys an attacker one path for one hour — that's the confused-deputy defence.
| Dimension | Classic | Serverless |
|---|---|---|
| Primary isolation | "Natural" — your subscription + VNet | Platform-enforced: dedicated compute, private no-public-IP net, wipe on completion |
| Credential to storage | UC down-scoped token (or legacy cluster identity) | ~1h scoped token, per workload, direct to storage |
| Compute reuse | Cluster persists until terminated | Per-workload; wiped, no cross-tenant reuse |
| In-cluster multi-user | Lakeguard (Standard mode) | Lakeguard (incl. Python UDF isolation) |
| Storage firewall | NCC subnet IDs / endpoints | NSP + AzureDatabricksServerless tag, or NCC PE |
Terraform — core isolation (wipe, 1h token) is platform-enforced; you wire the plumbing.
resource "azurerm_databricks_access_connector" "uc" { # the managed identity UC uses
identity { type = "SystemAssigned" }
}
resource "azurerm_role_assignment" "uc_blob" { # least privilege — NOT Owner/account key
scope = azurerm_storage_account.adls.id
role_definition_name = "Storage Blob Data Contributor"
principal_id = azurerm_databricks_access_connector.uc.identity[0].principal_id
}
resource "databricks_external_location" "this" { # binds credential to one path (isolation unit)
url = "abfss://data@${azurerm_storage_account.adls.name}.dfs.core.windows.net/"
credential_name = databricks_storage_credential.this.name
}
Portal: NSP → Create (region = workspace region) → Associate ADLS (Transition mode) →
Inbound rule: Service Tag AzureDatabricksServerless.<region> · Access Connector → ADLS IAM →
Storage Blob Data Contributor → External Locations bind the abfss:// path · Compute → access mode =
Standard (Lakeguard).
By 2026-06-09 a storage account that allowlisted serverless subnet IDs must move to an
NSP + the AzureDatabricksServerless service tag. Stay in Transition
mode (Enforced blocks all non-matching traffic). The 1-hour token is not a network control — still need
the storage firewall + egress policy. NSP Terraform coverage lags the Portal.
abfss:// path access denied outside UC securables; Standard-mode capability gaps (no DBR ML/GPU/RDD/Scala) push some to Dedicated.Three layers customers conflate, nested like Russian dolls: ESC (the paid add-on/bill),
ESM (hardened image + agents, classic only), CSP (the umbrella posture +
standards). CSP ⊃ ESM ⊃ hardened image. Switch the diagram between the nesting and
the classic-plane impact; click any box.
Enabling CSP or attaching a standard is intended to be permanent — you can't remove it from a workspace that has processed regulated data; the only path back is delete + rebuild. Scope CSP to the workspaces that actually touch regulated data, and settle topology before the first regulated row lands.
| ESC (add-on) | ESM | CSP | |
|---|---|---|---|
| What it is | Paid entitlement/SKU | Hardened image + agents | Umbrella posture + standards |
| Scope | Account/billing | Classic only | Classic + serverless (by standard/region) |
| Reversible? | n/a | Yes | No — permanent once regulated data processed |
| Restricts features | No | No | Yes — GA + named-preview allow-list only |
| New network req | No | No | TCP 2443 (if egress restricted) + Azure VNet encryption |
Terraform (azurerm) — NOTE: only HIPAA / PCI_DSS / NONE here; other standards need Portal/CLI/PS/ARM.
resource "azurerm_databricks_workspace" "this" {
sku = "premium" # ESC requires Premium
enhanced_security_compliance {
automatic_cluster_update_enabled = true # permanent under CSP
compliance_security_profile_enabled = true # CSP on (PERMANENT!)
compliance_security_profile_standards = ["HIPAA", "PCI_DSS"] # or ["NONE"]
enhanced_security_monitoring_enabled = true # ESM agents + hardened image
}
}
# On a restricted-egress VNet, also allow: Outbound TCP -> * : 2443
Portal: workspace → Settings → Security & compliance → enable compliance security profile → pick standard(s) in-region or None → enable ESM → enable automatic cluster update. Confirm: a shield icon appears by the workspace name.
HIPAA / HITRUST / IRAP become CSP-mandatory on 2026-09-01. Serverless standard support is narrower & region-specific than classic — always verify the standard × region × plane cell. Gen2/Arm64 VMs are blocked under ESM/CSP.
NONE); hardened image/monitoring on classic without permanence (ESM-only).One audit stream, two taps. System tables (system.access.audit) you query in SQL —
the only tap carrying account-level events; Azure diagnostic settings export to your SIEM — workspace-level
only. Switch the diagram between the two taps and the SIEM bridge; click any box.
Account-admin changes, account-level SCIM, and metastore-level grants appear only in
system.access.audit (workspace_id = 0), never in Azure diagnostic settings. A
SIEM-only strategy has a blind spot — the enterprise answer is always both taps.
| Need | Use | Why |
|---|---|---|
| Ad-hoc "who touched X" | system.access.audit | One SQL query, 365-day, incl. account events |
| Central enterprise SIEM | Diagnostic → Event Hub / Log Analytics → Sentinel/Splunk | Native Azure bridge; single pane of glass |
| > 365-day / immutable archive | Diagnostic → Storage (WORM) | System-table free retention is 365 days |
| Sub-minute detection | Diagnostic → Event Hub → SIEM | System tables lag (mins–hours) |
| In-platform scheduled detection | Databricks SQL Alert over system tables | No extra infra; email/Slack/PagerDuty |
SQL — least-privilege grant + a failed-login alert (the event_date predicate is required).
GRANT SELECT ON TABLE system.access.audit TO `secops-analysts`;
SELECT user_identity.email AS user_email, count(*) AS failed_logins
FROM system.access.audit
WHERE event_date >= current_date() - INTERVAL 1 DAY -- prune partitions (REQUIRED)
AND service_name = 'accounts'
AND action_name IN ('login','aadBrowserLogin','tokenLogin')
AND response.statusCode <> 200 -- 200 = success; non-200 = the signal
AND event_time >= current_timestamp() - INTERVAL 15 MINUTES
GROUP BY user_identity.email HAVING failed_logins >= 5;
Terraform — export workspace logs to Log Analytics for Sentinel.
resource "azurerm_monitor_diagnostic_setting" "adb_audit" {
target_resource_id = azurerm_databricks_workspace.this.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.sec.id
enabled_log { category = "accounts" } # auth, tokens, IP ACLs
enabled_log { category = "unityCatalog" } # grants, securable access
# use a dynamic block — Databricks adds categories over time
}
Portal: Catalog Explorer → system → confirm system.access.audit ·
workspace resource → Monitoring → Diagnostic settings → + Add → categories → Log Analytics /
Storage / Event Hub · SQL → Alerts → Create → paste query → condition → schedule → notifications.
Audit table is Public Preview (free during preview); 365-day free retention;
not real-time (match alert cadence to ingestion latency); broad scans rejected (always filter
event_date); a few services (cluster policies, vector search, legacy groups) don't emit to diagnostic
settings.
This module ships a runnable companion: audit_monitoring.sql (grants + failed-login,
sensitive-table, privilege-change, ESM-agent and cost-anomaly queries).
NONE for a strict internal bar with no attestationlatest as the key version or skip purge protectionEncryption defaults are enough until a regulator wants the key. Serverless + CSP is the regulated default; classic + VNet injection only when the network path must live in the customer's subscription. CSP is permanent — scope it. And audit = system tables for completeness + SIEM export for correlation, never one alone. Premium is the non-negotiable baseline (CMK / CSP / IP ACLs / Private Link are all Premium; CSP/ESM also bill the ESC add-on).
| Customer asks… | Architect answers… |
|---|---|
| "Encrypted in transit & at rest, and who holds the keys?" | Yes to both by default (TLS 1.2+ / Microsoft-managed AES-256); CMK is how you hold the key. |
| "Can we revoke Databricks' access unilaterally?" | Yes — disable/delete the Key Vault key (kill switch within the DEK cache window). |
| "Does CMK cover our ADLS data lake?" | No — that's a separate storage account you CMK directly. (The #1 misunderstanding.) |
| "If serverless runs in Databricks' account, what stops another tenant reading our data?" | Dedicated per-workload compute wiped on completion, no warm pool; UC vends ~1h path-scoped tokens used direct to ADLS; the control plane mints but never proxies bytes. |
| "Are you HIPAA / PCI / IRAP?" | The platform holds the attestations, but processing regulated data needs CSP + the matching standard on the workspace — and it's permanent. |
| "Where's the audit trail / can you feed our SIEM?" | System tables (365-day, incl. account-level) and Diagnostic export to Sentinel/Splunk. Account-level events are only in the system table — run both. |
Key disabled/expired or the wrap/unwrap role was removed.First check: Key Vault key enabled + AzureDatabricks app / storage MI still hold wrap/unwrap.
CMK was set on the workspace, not the data lake.First check: CMK on the ADLS Gen2 storage account itself.
Wrong region tag or a stale subnet-ID rule.First check: regional AzureDatabricksServerless.<region> tag, NSP in Transition.
Missing the new outbound rule.First check: outbound TCP 2443 (+ Azure VNet encryption / supported VM type).
Unsupported instance family.First check: VM isn't Gen2 or Arm64.
They're ACCOUNT_LEVEL, never in diagnostic settings.First check: query system.access.audit where workspace_id = 0.
No partition pruning on the system table.First check: add an event_date predicate.
Wrong access mode / legacy cluster.First check: access mode is Standard (Lakeguard), not Dedicated-misused.