IP Access List → CBI migration
Migrates this workspace's existing IP access list into a context-based ingress (CBI) account
network policy, as-is — no audit-log analysis, no enrichment, nothing added. The engine is the
single command dbx-migrate-ip-acls (this repo).
For traffic-based suggestions, threat-intel / cloud enrichment, or identity/destination scoping, use
the separate databricks-network-policy-helper tool (dbx-nwp-helper ingress / egress) instead.
When to use
The user wants to: migrate / convert an existing IP access list to a network policy, recreate their
IP ACL as CBI, or create a network policy from the current ACL without analysing traffic.
Setup
uv sync, then uv run dbx-migrate-ip-acls … (or uv tool install . then dbx-migrate-ip-acls …).
Auth is the SDK's unified auth (--profile or DATABRICKS_*). This tool always needs
account-admin access (pass --account-id with account-admin credentials): even a propose-only /
--export run performs account-level pre-checks (existing network policy + PrivateLink). No SQL
warehouse is needed (no traffic analysis). It is a single command — there is no subcommand, so
options go straight after the program name.
What it does
- Right after the workspace is chosen, decides whether there's anything to migrate, from the
workspace-wide
enableIpAccessLists toggle × the number of IP access lists:
- disabled + 0 rules → "disabled and have no rules. Nothing to migrate." → exit.
- disabled + 1+ rules → the rules aren't in effect, so there's nothing enabled to migrate. It
prints the current IP-ACL config and (interactively) offers to enable them: yes →
sets
enableIpAccessLists=true and continues the migration in the same run; no → exits
(nothing to migrate). --yes never auto-flips the toggle — it exits with guidance.
- enabled + 0 rules → "have no rules. Nothing to migrate." → exit.
- enabled + 1+ rules → proceed. (Unreadable toggle → warn + proceed.)
- Reads the workspace's IP access lists (
w.ip_access_lists.list()). Individual lists that are
disabled are flagged and NOT migrated — only enabled lists are; the disabled ones are called out
in the final printout (below the old + new policy) so you can vet them. Maps ALLOW lists → allow
rules, BLOCK lists → deny rules (IPv4 only; CBI is IPv4-only), recreating each rule
verbatim — the original ACL label, with no prefix and no mode suffix. The one thing it adds: if
the ACL has only BLOCK lists, a catch-all allow (all public IPs) is added, because CBI
RESTRICTED_ACCESS is default-deny — without it a deny-only policy would block everything, flipping
the ACL's default-allow-except-blocked meaning.
- Runs account-level pre-checks before migrating. The two PrivateLink checks below are skipped
entirely on Azure (the cloud is resolved from the account workspaces API
cloud field, falling
back to the workspace host's domain when the API doesn't populate it) — an Azure workspace has
neither a Private Access Settings object nor VPC endpoints, so it only needs its IP ACLs migrated:
- PAS attached? If this workspace has a Private Access Settings object (front-end
PrivateLink), migration to CBI isn't supported yet — it aborts.
- Account has registered VPC endpoints? If the account has ≥1 registered VPC (PrivateLink)
endpoint — front-end or back-end, any use case — it aborts too, even if this workspace
isn't attached to one. CBI private access isn't GA yet and defaults to allow-all endpoints, so
any PrivateLink account is blocked for now (this limitation lifts once CBI private access GAs).
- Existing restrictive CBI ingress policy? Only matters when the run will assign the new
policy, and only for a policy that actually restricts traffic — an allow-all policy such as
the account's baseline
default-policy is ignored. When assigning over a restrictive one —
enforced or dry-run — it aborts (migrating on top of an existing CBI ingress policy
isn't supported yet). When not assigning (propose-only, --export, --no-auto-assign), it
just warns.
- Names the new policy from
--policy-name; if not given it prompts for one (leave blank there
to use the profile name). It only creates new policies, so if the chosen name already exists it
re-prompts (interactively) or aborts. --create-policy is on by default (an interactive review
gate still confirms before the write); it creates the policy and, if --auto-assign (default on),
binds the current workspace to it. For a propose-only run pass --no-create-policy --no-auto-assign.
- With
--disable-existing-ip-acls (off by default), after the policy is created and assigned,
turns off the workspace's IP access list enforcement (enableIpAccessLists=false) so the old ACL
and the new CBI policy don't both apply. The lists themselves are preserved (reversible). The CLI
refuses this flag unless the run also creates and assigns the policy, so it can't leave the
workspace with no ingress control.
This tool deliberately does not auto-allow Databricks' own control-plane IPs or do any
enrichment — it assumes the existing ACL is what the customer wants. It migrates the ingress
(the IP ACLs) and, for egress, copies the egress block of the policy the workspace currently
runs under verbatim — its enforcement mode, allowed internet (FQDN) + storage destinations, and
blocked-internet lists — so egress posture is preserved rather than reset. The source is the
workspace's assigned policy, or the account baseline default-policy when nothing is assigned; only
when neither is readable does it fall back to a permissive FULL_ACCESS egress. There is no egress
option — it always mirrors the current policy.
Options
--policy-mode — enforce (default) or dry_run (log-only trial).
--policy-name — the new policy's id. If omitted you're prompted (blank there = the profile
name; falls back to the workspace id). Normalised to a lowercase, --safe, length-capped id.
--export <path> — write the proposed network-policy JSON (a curl / REST-ready
AccountNetworkPolicy body) and a sibling best-effort Terraform .tf
(databricks_account_network_policy — review before terraform apply). If <path> is a directory
it writes <policy-id>.json + <policy-id>.tf inside it (use --export . for the current
directory); missing parent dirs are created. Works in propose-only mode too.
--auto-assign / --no-auto-assign — bind the current workspace (default on).
--create-policy / --no-create-policy — write the policy. On by default (a review gate still
confirms). For a propose-only run use --no-create-policy --no-auto-assign.
--disable-existing-ip-acls — after create + assign, turn off the workspace's IP access lists;
requires create + assign and --policy-mode enforce. Off by default.
--account-id (+ account-admin creds) — account-level (pre-checks and create/assign). When unset it
defaults to the workspace profile's own account_id, else prompts.
--account-host / --account-profile — account API host / a dedicated profile for account-level
calls. When --account-host isn't set it's derived from the workspace's environment (so an AWS
staging, GCP or Azure workspace reaches the matching account console instead of the AWS prod
default); pass --account-host to override. When --account-profile isn't set, the CLI
auto-selects a config profile whose host + account_id both match the target account (matching
on both, since an id alone is ambiguous), so account calls don't fall back to an ambient credential;
one match → used, none → unified auth (the account-access check then fails fast if it's wrong),
several → the first, with a note.
Invalid flag combinations (rejected up front): --no-create-policy with --auto-assign (nothing
to bind); --disable-existing-ip-acls without both create + assign, or with --policy-mode dry_run
(both would leave the workspace with no enforced ingress control).
Safety
The tool's job is to create the policy, so --create-policy is on by default — but an
interactive review gate still confirms before any write, and by default the CLI steps through
each section — pausing after the existing-ACL analysis and after the proposed-policy preview to ask
whether to continue (no aborts cleanly). --yes runs non-interactively, skipping the pauses and
the review/write gate, so dbx-migrate-ip-acls --yes will create + assign. For a read-only run use
--no-create-policy --no-auto-assign (optionally with --export) — it writes nothing. Default
--policy-mode enforce will block non-matching source IPs on the assigned workspace — trial with
--policy-mode dry_run first if unsure.
1---2name: ip-acl-migration3description: Migrate a Databricks workspace's existing IP access list (ACL) into a context-based ingress (CBI) account network policy, as-is, using the dbx-migrate-ip-acls CLI. Use when the user wants to convert / migrate an existing IP access list to a network policy, recreate their IP ACL as CBI, or stand up a network policy from the current ACL without audit-log analysis. Runs `dbx-migrate-ip-acls`, which reads this workspace's ACL (ALLOW->allow rules, BLOCK->deny rules) and recreates it verbatim — nothing added — then creates the account network policy (enforce or dry-run) and optionally auto-assigns it to the current workspace.4---56# IP Access List → CBI migration78Migrates **this workspace's existing IP access list** into a context-based ingress (CBI) account9network policy, **as-is** — no audit-log analysis, no enrichment, nothing added. The engine is the10single command `dbx-migrate-ip-acls` (this repo).1112For traffic-based suggestions, threat-intel / cloud enrichment, or identity/destination scoping, use13the separate **databricks-network-policy-helper** tool (`dbx-nwp-helper ingress` / `egress`) instead.1415## When to use1617The user wants to: migrate / convert an existing IP access list to a network policy, recreate their18IP ACL as CBI, or create a network policy from the current ACL without analysing traffic.1920## Setup2122`uv sync`, then `uv run dbx-migrate-ip-acls …` (or `uv tool install .` then `dbx-migrate-ip-acls …`).23Auth is the SDK's unified auth (`--profile` or `DATABRICKS_*`). **This tool always needs24account-admin access** (pass `--account-id` with account-admin credentials): even a propose-only /25`--export` run performs account-level pre-checks (existing network policy + PrivateLink). No SQL26warehouse is needed (no traffic analysis). It is a **single command** — there is no subcommand, so27options go straight after the program name.2829## What it does30311. **Right after the workspace is chosen**, decides whether there's anything to migrate, from the32 workspace-wide `enableIpAccessLists` toggle × the number of IP access lists:33 - **disabled + 0 rules** → "disabled and have no rules. Nothing to migrate." → **exit**.34 - **disabled + 1+ rules** → the rules aren't in effect, so there's nothing enabled to migrate. It35 **prints the current IP-ACL config** and (interactively) offers to **enable** them: **yes** →36 sets `enableIpAccessLists=true` and **continues** the migration in the same run; **no** → exits37 (nothing to migrate). `--yes` never auto-flips the toggle — it exits with guidance.38 - **enabled + 0 rules** → "have no rules. Nothing to migrate." → **exit**.39 - **enabled + 1+ rules** → **proceed**. (Unreadable toggle → warn + proceed.)402. Reads the workspace's IP access lists (`w.ip_access_lists.list()`). **Individual lists that are41 disabled are flagged and NOT migrated** — only enabled lists are; the disabled ones are called out42 in the final printout (below the old + new policy) so you can vet them. Maps **ALLOW lists → allow43 rules**, **BLOCK lists → deny rules** (IPv4 only; CBI is IPv4-only), recreating each rule44 **verbatim** — the original ACL label, with no prefix and no mode suffix. The one thing it adds: if45 the ACL has **only BLOCK lists**, a catch-all allow (all public IPs) is added, because CBI46 RESTRICTED_ACCESS is default-deny — without it a deny-only policy would block everything, flipping47 the ACL's default-allow-except-blocked meaning.483. Runs account-level **pre-checks** before migrating. The two PrivateLink checks below are **skipped49 entirely on Azure** (the cloud is resolved from the account workspaces API `cloud` field, falling50 back to the workspace host's domain when the API doesn't populate it) — an Azure workspace has51 neither a Private Access Settings object nor VPC endpoints, so it only needs its IP ACLs migrated:52 - **PAS attached?** If **this workspace** has a Private Access Settings object (front-end53 PrivateLink), migration to CBI isn't supported yet — it **aborts**.54 - **Account has registered VPC endpoints?** If the **account** has ≥1 registered VPC (PrivateLink)55 endpoint — front-end or back-end, any use case — it **aborts** too, even if *this* workspace56 isn't attached to one. CBI private access isn't GA yet and defaults to allow-all endpoints, so57 any PrivateLink account is blocked for now (this limitation lifts once CBI private access GAs).58 - **Existing *restrictive* CBI ingress policy?** Only matters when the run will **assign** the new59 policy, and only for a policy that **actually restricts traffic** — an allow-all policy such as60 the account's baseline `default-policy` is ignored. When assigning over a restrictive one —61 **enforced or dry-run** — it **aborts** (migrating on top of an existing CBI ingress policy62 isn't supported yet). When **not** assigning (propose-only, `--export`, `--no-auto-assign`), it63 just **warns**.644. Names the new policy from `--policy-name`; if not given it **prompts** for one (leave blank there65 to use the profile name). It only **creates new** policies, so if the chosen name already exists it66 re-prompts (interactively) or aborts. `--create-policy` is **on by default** (an interactive review67 gate still confirms before the write); it creates the policy and, if `--auto-assign` (default on),68 binds the current workspace to it. For a propose-only run pass `--no-create-policy --no-auto-assign`.695. With `--disable-existing-ip-acls` (off by default), after the policy is created **and** assigned,70 turns off the workspace's IP access list enforcement (`enableIpAccessLists=false`) so the old ACL71 and the new CBI policy don't both apply. The lists themselves are preserved (reversible). The CLI72 refuses this flag unless the run also creates and assigns the policy, so it can't leave the73 workspace with no ingress control.7475> This tool deliberately does **not** auto-allow Databricks' own control-plane IPs or do any76> enrichment — it assumes the existing ACL is what the customer wants. It migrates the **ingress**77> (the IP ACLs) and, for **egress**, copies the egress block of the policy the workspace currently78> runs under **verbatim** — its enforcement mode, allowed internet (FQDN) + storage destinations, and79> blocked-internet lists — so egress posture is preserved rather than reset. The source is the80> workspace's assigned policy, or the account baseline `default-policy` when nothing is assigned; only81> when neither is readable does it fall back to a permissive `FULL_ACCESS` egress. There is no egress82> option — it always mirrors the current policy.8384## Options8586- `--policy-mode` — **`enforce`** (default) or `dry_run` (log-only trial).87- `--policy-name` — the new policy's id. If omitted you're **prompted** (blank there = the profile88 name; falls back to the workspace id). Normalised to a lowercase, `-`-safe, length-capped id.89- `--export <path>` — write the proposed network-policy JSON (a curl / REST-ready90 `AccountNetworkPolicy` body) **and** a sibling best-effort Terraform `.tf`91 (`databricks_account_network_policy` — review before `terraform apply`). If `<path>` is a directory92 it writes `<policy-id>.json` + `<policy-id>.tf` inside it (use `--export .` for the current93 directory); missing parent dirs are created. Works in propose-only mode too.94- `--auto-assign` / `--no-auto-assign` — bind the current workspace (default on).95- `--create-policy` / `--no-create-policy` — write the policy. **On by default** (a review gate still96 confirms). For a propose-only run use `--no-create-policy --no-auto-assign`.97- `--disable-existing-ip-acls` — after create + assign, turn off the workspace's IP access lists;98 requires create + assign **and** `--policy-mode enforce`. Off by default.99- `--account-id` (+ account-admin creds) — account-level (pre-checks and create/assign). When unset it100 **defaults to the workspace profile's own `account_id`**, else prompts.101- `--account-host` / `--account-profile` — account API host / a dedicated profile for account-level102 calls. When `--account-host` isn't set it's **derived from the workspace's environment** (so an AWS103 staging, GCP or Azure workspace reaches the matching account console instead of the AWS prod104 default); pass `--account-host` to override. When `--account-profile` isn't set, the CLI105 **auto-selects a config profile whose host + `account_id` both match** the target account (matching106 on both, since an id alone is ambiguous), so account calls don't fall back to an ambient credential;107 one match → used, none → unified auth (the account-access check then fails fast if it's wrong),108 several → the first, with a note.109110**Invalid flag combinations** (rejected up front): `--no-create-policy` with `--auto-assign` (nothing111to bind); `--disable-existing-ip-acls` without both create + assign, or with `--policy-mode dry_run`112(both would leave the workspace with no enforced ingress control).113114## Safety115116The tool's job **is** to create the policy, so `--create-policy` is **on by default** — but an117interactive **review gate** still confirms before any write, and by default the CLI **steps through**118each section — pausing after the existing-ACL analysis and after the proposed-policy preview to ask119whether to continue (*no* aborts cleanly). **`--yes` runs non-interactively**, skipping the pauses and120the review/write gate, so `dbx-migrate-ip-acls --yes` *will* create + assign. For a read-only run use121`--no-create-policy --no-auto-assign` (optionally with `--export`) — it writes nothing. Default122`--policy-mode enforce` will block non-matching source IPs on the assigned workspace — trial with123`--policy-mode dry_run` first if unsure.