From d94f31c34e88179915b2815accd235be38c68d15 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:13:11 +0000 Subject: [PATCH 1/9] knowledge_base: add run_as_service_principal example Dev target runs as the deploying user (mode: development). Prod target uses mode: production, run_as.service_principal_name from a required variable, group-only permissions (CAN_MANAGE deployers, CAN_VIEW viewers), and an explicit workspace.root_path. A serverless whoami job (environment_version 4) prints current_user() and the SDK identity. README (STE) covers SP setup, CI deploy via OAuth M2M, identity model, and run_as edge cases verified against CLI v1.20.0. Closes #13 Co-authored-by: Isaac Co-authored-by: omnigent --- .../run_as_service_principal/README.md | 309 ++++++++++++++++++ .../run_as_service_principal/databricks.yml | 50 +++ .../resources/whoami.job.yml | 19 ++ .../run_as_service_principal/src/whoami.py | 13 + 4 files changed, 391 insertions(+) create mode 100644 knowledge_base/run_as_service_principal/README.md create mode 100644 knowledge_base/run_as_service_principal/databricks.yml create mode 100644 knowledge_base/run_as_service_principal/resources/whoami.job.yml create mode 100644 knowledge_base/run_as_service_principal/src/whoami.py diff --git a/knowledge_base/run_as_service_principal/README.md b/knowledge_base/run_as_service_principal/README.md new file mode 100644 index 0000000..1625c4b --- /dev/null +++ b/knowledge_base/run_as_service_principal/README.md @@ -0,0 +1,309 @@ +# Run as a service principal + +This Declarative Automation Bundles example runs a prod job as a service principal. +The dev job runs as the user who deploys it. + +For more information, see the [run_as documentation](https://docs.databricks.com/dev-tools/bundles/run-as.html). + +## What this example shows + +* The `dev` target uses `mode: development`. The job runs as the user who deploys it. +* The `prod` target uses `mode: production` and `run_as.service_principal_name`. The job runs as the service principal. +* A variable supplies the application ID of the service principal. The bundle contains no IDs, hosts, or secrets. +* The `prod` target gives `CAN_MANAGE` to a deployer group and `CAN_VIEW` to a viewer group. +* No individual user gets `CAN_MANAGE` from the bundle. +* The `prod` target sets an explicit `workspace.root_path`. This keeps one copy of the prod deployment. +* The `whoami` job runs on serverless compute with `environment_version: "4"`. +* The job prints `current_user()` and the SDK identity. The output shows which identity ran the job. + +The bundle contains these files: + +| File | Content | +| --- | --- | +| `databricks.yml` | Variables, the `dev` target, and the `prod` target with `run_as` and `permissions`. | +| `resources/whoami.job.yml` | The serverless `whoami` job. The job has no schedule. | +| `src/whoami.py` | The task that prints the run identity. | + +## Prerequisites + +* Databricks CLI. We tested this example with v1.20.0. +* A Databricks workspace with Unity Catalog and serverless jobs. +* Workspace admin access, or a workspace admin who can do the steps in the next section. + +The bundle has no cloud-specific settings. It applies to AWS, Azure, and GCP. +This example uses serverless compute only. We did not test classic compute. + +## Create the service principal and grant it access + +Do these steps one time for each workspace. + +1. Create the service principal. + In the workspace, open **Settings > Identity and access > Service principals**. + Click **Add service principal**. + You can also create it in the account console under **User management**. +2. If you created the service principal in the account console, assign it to the workspace. + Open **Workspaces**, select the workspace, and open **Permissions**. +3. Copy the **Application ID** of the service principal. This value is a UUID. + Use this value for `service_principal_application_id`. +4. Make sure that the service principal has the **Workspace access** entitlement. +5. Create a workspace group for deployers. The default name in this example is `bundle-deployers`. +6. Add each person who deploys to prod to the deployer group. +7. Add the service principal to the deployer group. CI deploys as the service principal. +8. Give the deployer group the `Service Principal: User` role on the service principal. + Open the service principal and then open the **Permissions** tab. + Click **Grant access**, select the deployer group, and select **Service Principal: User**. + The `Service Principal: Manager` role also includes this permission. +9. Grant the Unity Catalog privileges that your job needs to the service principal. + Use the application ID as the principal name. + The `whoami` job reads no tables, so it needs no grants. + A job that reads a table needs grants such as these: + + ```sql + GRANT USE CATALOG ON CATALOG TO ``; + GRANT USE SCHEMA, SELECT ON SCHEMA . TO ``; + ``` + +10. If your workspace uses serverless budget policies, give the service principal access to a policy. +11. For CI, create an OAuth secret for the service principal. + Open the service principal and then open the **Secrets** tab. + Click **Generate secret**. + Keep the secret in the secret store of your CI system. + Do not put the secret in the bundle or in Git. + +## Configure + +Set the variables with `--var` or with `BUNDLE_VAR_` environment variables. + +| Variable | Default | Target | Description | +| --- | --- | --- | --- | +| `service_principal_application_id` | None. This value is mandatory for `prod`. | `prod` | The application ID of the service principal that runs the prod job. | +| `deployer_group` | `bundle-deployers` | `prod` | The group that gets `CAN_MANAGE`. Its members and CI deploy to prod. | +| `viewer_group` | `users` | `prod` | The group that gets `CAN_VIEW` on the prod job and its runs. | + +The `dev` target sets `service_principal_application_id` to `not-used-in-dev`. +The `dev` target does not use this value. + +The bundle does not set `workspace.host`. +Select the workspace with a profile, for example `-p `. +Alternatively, set the `DATABRICKS_HOST` environment variable. + +## Deploy and run in dev + +1. Validate the bundle: + + ```sh + databricks bundle validate -t dev + ``` + +2. Deploy the bundle: + + ```sh + databricks bundle deploy -t dev + ``` + +3. Run the job: + + ```sh + databricks bundle run -t dev whoami + ``` + +Development mode adds the `[dev ]` prefix to the job name. +The bundle deploys to your home folder. + +## Deploy and run in prod + +Deploy prod from CI as the service principal. +Use OAuth machine-to-machine (M2M) authentication. +Do not use a personal access token. + +1. In the CI secret store, keep the OAuth secret of the service principal. +2. In the CI job, set these environment variables: + + ```sh + export DATABRICKS_HOST=https:// + export DATABRICKS_CLIENT_ID= + export DATABRICKS_CLIENT_SECRET= + export BUNDLE_VAR_service_principal_application_id=$DATABRICKS_CLIENT_ID + ``` + +3. Validate the bundle. Make sure that the output contains `run_as`: + + ```sh + databricks bundle validate -t prod -o json + ``` + +4. Deploy the bundle: + + ```sh + databricks bundle deploy -t prod + ``` + +5. Run the job: + + ```sh + databricks bundle run -t prod whoami + ``` + +You can also use workload identity federation in CI. +Federation removes the stored OAuth secret. +For more information, see [OAuth token federation](https://docs.databricks.com/dev-tools/auth/oauth-federation.html). + +A member of the deployer group can also deploy prod from a laptop: + +```sh +databricks bundle deploy -t prod --var service_principal_application_id= +``` + +Do not mix the two methods. +The `root_path` contains the user name of the deploying identity. +A deploy by a different identity makes a second copy of the prod job. + +## Verify the identity + +The `whoami` job prints three lines. +`bundle run` shows the task output when the run completes. + +In dev, the output shows your user name: + +``` +current_user(): +SDK user_name: +SDK display_name: +``` + +In prod, the output shows the application ID of the service principal: + +``` +current_user(): +SDK user_name: +SDK display_name: +``` + +You can also open the job in the workspace. +The **Run as** field of the job shows the identity. + +## Identity model + +| Target | Deployment identity | Run identity | Owner (`IS_OWNER`) | +| --- | --- | --- | --- | +| `dev` | You | You | You | +| `prod`, deployed by CI | The service principal | The service principal | The service principal | +| `prod`, deployed by a person | The person in the deployer group | The service principal | The person who deployed | + +* The deployment identity is the identity that runs `databricks bundle deploy`. +* The run identity comes from `run_as`. If `run_as` is not set, the run identity is the deployment identity. +* The owner is always the deployment identity. The bundle adds `IS_OWNER` for it. + +## Edge cases and common errors + +### Missing value for the service principal + +If you do not set `service_principal_application_id` for `prod`, the CLI shows this error: + +``` +Error: no value assigned to required variable service_principal_application_id. +``` + +Set the variable with `--var` or `BUNDLE_VAR_service_principal_application_id`. +Use the application ID. Do not use the display name. + +### Missing Service Principal User role + +A person who deploys prod must have the `Service Principal: User` role on the service principal. +If the role is missing, `bundle validate` passes, but `bundle deploy` fails. +The Jobs API rejects the `run_as` setting when the CLI creates or updates the job. +The text of the error comes from the API. We did not record the exact text. +To fix the error, do step 8 in [Create the service principal and grant it access](#create-the-service-principal-and-grant-it-access). + +### run_as in development mode + +CLI v1.20.0 accepts `run_as` in a `mode: development` target. +The CLI shows no error and no warning. +Do not set `run_as` in dev. +A dev job that runs as the service principal hides your own permission problems. +Also, each developer then needs the `Service Principal: User` role. + +### Deploy performed by the service principal + +When CI deploys as the service principal, the deployment identity and the run identity are the same. +The service principal needs no `Service Principal: User` role on itself. +The service principal needs the **Workspace access** entitlement. +The `root_path` resolves to `/Workspace/Users//.bundle/run_as_service_principal/prod`. + +Run `databricks bundle init` as a person, not as the service principal. +CI runs only `validate`, `deploy`, `run`, and `destroy`. + +### Validate recommendations for prod + +When a person who is not in the deployer group validates prod, the CLI shows this recommendation: + +``` +Recommendation: permissions section should explicitly include the current deployment identity '' or one of its groups +If it is not included, CAN_MANAGE permissions are only applied if the present identity is used to deploy. +``` + +This is a recommendation, not an error. +Add the deploying identity to the deployer group. +For CI, add the service principal to the deployer group. + +The CLI can also show this warning: + +``` +Warning: workspace folder has permissions not configured in bundle +``` + +The `root_path` is in the home folder of the deploying identity. +That identity has `CAN_MANAGE` on its home folder, and the bundle does not list it. +The warning does not stop the deploy. + +### Pipelines and other resources with run_as + +Older CLI versions do not support `run_as` for pipelines. +CLI v0.241.0 added this support. +Before v0.241.0, a bundle with a pipeline and a different `run_as` identity fails with this error: + +``` +Error: pipelines do not support a setting a run_as user that is different from the owner. +``` + +The rule is: the run identity of the resource must be the same as the owner. +The owner is the deployment identity. + +CLI v1.20.0 supports `run_as` for jobs and pipelines. +Some resource types still use the old rule. +For example, `model_serving_endpoints` and `quality_monitors` fail with this error: + +``` +Error: model_serving_endpoints do not support a setting a run_as user that is different from the owner. +Current identity: . Run as identity: . +``` + +To fix the error, do one of these steps: + +* Deploy as the service principal. Then the owner and the run identity are the same. +* Move the resource to a bundle that does not set `run_as`. + +The `experimental.use_legacy_run_as` option changes the owner of pipelines to the `run_as` identity. +This option is experimental. +It requires a workspace admin to deploy, and a metastore admin for Unity Catalog pipelines. +Do not use this option for new bundles. + +## Clean up + +1. Remove the dev deployment: + + ```sh + databricks bundle destroy -t dev + ``` + +2. Remove the prod deployment. Use the identity that deployed prod: + + ```sh + databricks bundle destroy -t prod --var service_principal_application_id= + ``` + +3. If you made an OAuth secret only for this example, delete the secret. +4. If you made the service principal only for this example, delete the service principal. + +`bundle destroy` removes only the resources that this bundle deployed. +It does not remove the service principal, the groups, or the Unity Catalog grants. diff --git a/knowledge_base/run_as_service_principal/databricks.yml b/knowledge_base/run_as_service_principal/databricks.yml new file mode 100644 index 0000000..fe40d79 --- /dev/null +++ b/knowledge_base/run_as_service_principal/databricks.yml @@ -0,0 +1,50 @@ +# This is a Declarative Automation Bundle definition for run_as_service_principal. +# See https://docs.databricks.com/dev-tools/bundles/run-as.html for documentation. +bundle: + name: run_as_service_principal + +include: + - resources/*.yml + +variables: + service_principal_application_id: + description: >- + Application ID (a UUID) of the service principal that runs the prod job. + Find it in the workspace admin settings under Identity and access > Service principals. + deployer_group: + description: Workspace group whose members (and CI) can deploy and manage the prod bundle + default: bundle-deployers + viewer_group: + description: Workspace group that can view the prod job and its runs + default: users + +# The workspace host is not set here. Use a profile (--profile / -p) or the +# DATABRICKS_HOST environment variable, so the same file works locally and in CI. + +targets: + dev: + # Development mode prefixes resource names with "[dev ]", pauses + # schedules, and runs everything as the deploying user. + # Do not set run_as here: dev deployments are per-user by design. + mode: development + default: true + variables: + # Dev does not use run_as, so this value is never read. The variable has no + # default on purpose: a prod deploy fails fast if you forget to set it. + service_principal_application_id: not-used-in-dev + + prod: + mode: production + workspace: + # Deploy to a single, explicit location. When CI deploys as the service + # principal, current_user is the service principal, so this resolves to + # its home folder and there is exactly one copy of the prod deployment. + root_path: /Workspace/Users/${workspace.current_user.userName}/.bundle/${bundle.name}/${bundle.target} + run_as: + service_principal_name: ${var.service_principal_application_id} + permissions: + # Grant access to groups only. No individual user gets CAN_MANAGE. + - group_name: ${var.deployer_group} + level: CAN_MANAGE + - group_name: ${var.viewer_group} + level: CAN_VIEW diff --git a/knowledge_base/run_as_service_principal/resources/whoami.job.yml b/knowledge_base/run_as_service_principal/resources/whoami.job.yml new file mode 100644 index 0000000..f2a76a2 --- /dev/null +++ b/knowledge_base/run_as_service_principal/resources/whoami.job.yml @@ -0,0 +1,19 @@ +resources: + jobs: + whoami: + name: run_as_service_principal_whoami + description: Prints the identity that runs the job, so you can see the effect of run_as. + + # No trigger or schedule: run the job on demand with `databricks bundle run whoami`. + + tasks: + - task_key: whoami + environment_key: default + spark_python_task: + python_file: ../src/whoami.py + + environments: + - environment_key: default + # See https://docs.databricks.com/api/workspace/jobs/create#environments-spec + spec: + environment_version: "4" diff --git a/knowledge_base/run_as_service_principal/src/whoami.py b/knowledge_base/run_as_service_principal/src/whoami.py new file mode 100644 index 0000000..0eac950 --- /dev/null +++ b/knowledge_base/run_as_service_principal/src/whoami.py @@ -0,0 +1,13 @@ +# Prints the identity that runs this task. +# In dev this is the user who deployed the bundle; in prod it is the run_as service principal. +from databricks.sdk import WorkspaceClient +from pyspark.sql import SparkSession + +spark = SparkSession.builder.getOrCreate() +sql_user = spark.sql("SELECT current_user() AS user").first()["user"] + +me = WorkspaceClient().current_user.me() + +print(f"current_user(): {sql_user}") +print(f"SDK user_name: {me.user_name}") +print(f"SDK display_name: {me.display_name}") From 9c234859bde7789839dcdae6fc46baad05a0c5c7 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:18:00 +0000 Subject: [PATCH 2/9] knowledge_base/run_as_service_principal: shorten README to sibling shape Cut README from 309 to 59 lines: intro, Prerequisites, Usage (dev, prod, identity check, one identity table), and five Notes. Config is unchanged. Co-authored-by: Isaac Co-authored-by: omnigent --- .../run_as_service_principal/README.md | 308 ++---------------- 1 file changed, 29 insertions(+), 279 deletions(-) diff --git a/knowledge_base/run_as_service_principal/README.md b/knowledge_base/run_as_service_principal/README.md index 1625c4b..0dadceb 100644 --- a/knowledge_base/run_as_service_principal/README.md +++ b/knowledge_base/run_as_service_principal/README.md @@ -1,309 +1,59 @@ -# Run as a service principal +# Run a job as a service principal This Declarative Automation Bundles example runs a prod job as a service principal. -The dev job runs as the user who deploys it. +In dev, the job runs as the user who deploys it. +The serverless `whoami` job prints its identity, so you can see the effect of `run_as`. For more information, see the [run_as documentation](https://docs.databricks.com/dev-tools/bundles/run-as.html). -## What this example shows - -* The `dev` target uses `mode: development`. The job runs as the user who deploys it. -* The `prod` target uses `mode: production` and `run_as.service_principal_name`. The job runs as the service principal. -* A variable supplies the application ID of the service principal. The bundle contains no IDs, hosts, or secrets. -* The `prod` target gives `CAN_MANAGE` to a deployer group and `CAN_VIEW` to a viewer group. -* No individual user gets `CAN_MANAGE` from the bundle. -* The `prod` target sets an explicit `workspace.root_path`. This keeps one copy of the prod deployment. -* The `whoami` job runs on serverless compute with `environment_version: "4"`. -* The job prints `current_user()` and the SDK identity. The output shows which identity ran the job. - -The bundle contains these files: - -| File | Content | -| --- | --- | -| `databricks.yml` | Variables, the `dev` target, and the `prod` target with `run_as` and `permissions`. | -| `resources/whoami.job.yml` | The serverless `whoami` job. The job has no schedule. | -| `src/whoami.py` | The task that prints the run identity. | - ## Prerequisites * Databricks CLI. We tested this example with v1.20.0. -* A Databricks workspace with Unity Catalog and serverless jobs. -* Workspace admin access, or a workspace admin who can do the steps in the next section. - -The bundle has no cloud-specific settings. It applies to AWS, Azure, and GCP. -This example uses serverless compute only. We did not test classic compute. - -## Create the service principal and grant it access - -Do these steps one time for each workspace. - -1. Create the service principal. - In the workspace, open **Settings > Identity and access > Service principals**. - Click **Add service principal**. - You can also create it in the account console under **User management**. -2. If you created the service principal in the account console, assign it to the workspace. - Open **Workspaces**, select the workspace, and open **Permissions**. -3. Copy the **Application ID** of the service principal. This value is a UUID. - Use this value for `service_principal_application_id`. -4. Make sure that the service principal has the **Workspace access** entitlement. -5. Create a workspace group for deployers. The default name in this example is `bundle-deployers`. -6. Add each person who deploys to prod to the deployer group. -7. Add the service principal to the deployer group. CI deploys as the service principal. -8. Give the deployer group the `Service Principal: User` role on the service principal. - Open the service principal and then open the **Permissions** tab. - Click **Grant access**, select the deployer group, and select **Service Principal: User**. - The `Service Principal: Manager` role also includes this permission. -9. Grant the Unity Catalog privileges that your job needs to the service principal. - Use the application ID as the principal name. - The `whoami` job reads no tables, so it needs no grants. - A job that reads a table needs grants such as these: - - ```sql - GRANT USE CATALOG ON CATALOG TO ``; - GRANT USE SCHEMA, SELECT ON SCHEMA . TO ``; - ``` - -10. If your workspace uses serverless budget policies, give the service principal access to a policy. -11. For CI, create an OAuth secret for the service principal. - Open the service principal and then open the **Secrets** tab. - Click **Generate secret**. - Keep the secret in the secret store of your CI system. - Do not put the secret in the bundle or in Git. +* A workspace with Unity Catalog and serverless jobs. +* A service principal in the workspace. Copy its application ID. +* A deployer group (default `bundle-deployers`) that contains the deployers and the service principal. +* The `Service Principal: User` role on the service principal for the deployer group. +* The Unity Catalog grants that your job needs, given to the service principal. `whoami` needs none. -## Configure - -Set the variables with `--var` or with `BUNDLE_VAR_` environment variables. - -| Variable | Default | Target | Description | -| --- | --- | --- | --- | -| `service_principal_application_id` | None. This value is mandatory for `prod`. | `prod` | The application ID of the service principal that runs the prod job. | -| `deployer_group` | `bundle-deployers` | `prod` | The group that gets `CAN_MANAGE`. Its members and CI deploy to prod. | -| `viewer_group` | `users` | `prod` | The group that gets `CAN_VIEW` on the prod job and its runs. | - -The `dev` target sets `service_principal_application_id` to `not-used-in-dev`. -The `dev` target does not use this value. +## Usage The bundle does not set `workspace.host`. -Select the workspace with a profile, for example `-p `. -Alternatively, set the `DATABRICKS_HOST` environment variable. - -## Deploy and run in dev +Select the workspace with `--profile ` or the `DATABRICKS_HOST` environment variable. -1. Validate the bundle: - - ```sh - databricks bundle validate -t dev - ``` - -2. Deploy the bundle: +1. Deploy and run the job in dev: ```sh databricks bundle deploy -t dev - ``` - -3. Run the job: - - ```sh databricks bundle run -t dev whoami ``` -Development mode adds the `[dev ]` prefix to the job name. -The bundle deploys to your home folder. - -## Deploy and run in prod - -Deploy prod from CI as the service principal. -Use OAuth machine-to-machine (M2M) authentication. -Do not use a personal access token. - -1. In the CI secret store, keep the OAuth secret of the service principal. -2. In the CI job, set these environment variables: +2. Deploy and run the job in prod. Set the application ID of the service principal: ```sh - export DATABRICKS_HOST=https:// - export DATABRICKS_CLIENT_ID= - export DATABRICKS_CLIENT_SECRET= - export BUNDLE_VAR_service_principal_application_id=$DATABRICKS_CLIENT_ID - ``` - -3. Validate the bundle. Make sure that the output contains `run_as`: - - ```sh - databricks bundle validate -t prod -o json + databricks bundle validate -t prod --var service_principal_application_id= + databricks bundle deploy -t prod --var service_principal_application_id= + databricks bundle run -t prod whoami ``` -4. Deploy the bundle: +3. Read the job output to see which identity ran the job. + In dev, the output shows your user name. + In prod, the output shows the application ID: - ```sh - databricks bundle deploy -t prod ``` - -5. Run the job: - - ```sh - databricks bundle run -t prod whoami + current_user(): + SDK user_name: + SDK display_name: ``` -You can also use workload identity federation in CI. -Federation removes the stored OAuth secret. -For more information, see [OAuth token federation](https://docs.databricks.com/dev-tools/auth/oauth-federation.html). - -A member of the deployer group can also deploy prod from a laptop: - -```sh -databricks bundle deploy -t prod --var service_principal_application_id= -``` - -Do not mix the two methods. -The `root_path` contains the user name of the deploying identity. -A deploy by a different identity makes a second copy of the prod job. - -## Verify the identity - -The `whoami` job prints three lines. -`bundle run` shows the task output when the run completes. - -In dev, the output shows your user name: - -``` -current_user(): -SDK user_name: -SDK display_name: -``` - -In prod, the output shows the application ID of the service principal: - -``` -current_user(): -SDK user_name: -SDK display_name: -``` - -You can also open the job in the workspace. -The **Run as** field of the job shows the identity. - -## Identity model - -| Target | Deployment identity | Run identity | Owner (`IS_OWNER`) | +| Target | Deploys | Runs as | Owner | | --- | --- | --- | --- | | `dev` | You | You | You | -| `prod`, deployed by CI | The service principal | The service principal | The service principal | -| `prod`, deployed by a person | The person in the deployer group | The service principal | The person who deployed | - -* The deployment identity is the identity that runs `databricks bundle deploy`. -* The run identity comes from `run_as`. If `run_as` is not set, the run identity is the deployment identity. -* The owner is always the deployment identity. The bundle adds `IS_OWNER` for it. - -## Edge cases and common errors - -### Missing value for the service principal - -If you do not set `service_principal_application_id` for `prod`, the CLI shows this error: - -``` -Error: no value assigned to required variable service_principal_application_id. -``` - -Set the variable with `--var` or `BUNDLE_VAR_service_principal_application_id`. -Use the application ID. Do not use the display name. - -### Missing Service Principal User role - -A person who deploys prod must have the `Service Principal: User` role on the service principal. -If the role is missing, `bundle validate` passes, but `bundle deploy` fails. -The Jobs API rejects the `run_as` setting when the CLI creates or updates the job. -The text of the error comes from the API. We did not record the exact text. -To fix the error, do step 8 in [Create the service principal and grant it access](#create-the-service-principal-and-grant-it-access). - -### run_as in development mode - -CLI v1.20.0 accepts `run_as` in a `mode: development` target. -The CLI shows no error and no warning. -Do not set `run_as` in dev. -A dev job that runs as the service principal hides your own permission problems. -Also, each developer then needs the `Service Principal: User` role. - -### Deploy performed by the service principal - -When CI deploys as the service principal, the deployment identity and the run identity are the same. -The service principal needs no `Service Principal: User` role on itself. -The service principal needs the **Workspace access** entitlement. -The `root_path` resolves to `/Workspace/Users//.bundle/run_as_service_principal/prod`. - -Run `databricks bundle init` as a person, not as the service principal. -CI runs only `validate`, `deploy`, `run`, and `destroy`. - -### Validate recommendations for prod - -When a person who is not in the deployer group validates prod, the CLI shows this recommendation: - -``` -Recommendation: permissions section should explicitly include the current deployment identity '' or one of its groups -If it is not included, CAN_MANAGE permissions are only applied if the present identity is used to deploy. -``` - -This is a recommendation, not an error. -Add the deploying identity to the deployer group. -For CI, add the service principal to the deployer group. - -The CLI can also show this warning: - -``` -Warning: workspace folder has permissions not configured in bundle -``` - -The `root_path` is in the home folder of the deploying identity. -That identity has `CAN_MANAGE` on its home folder, and the bundle does not list it. -The warning does not stop the deploy. - -### Pipelines and other resources with run_as - -Older CLI versions do not support `run_as` for pipelines. -CLI v0.241.0 added this support. -Before v0.241.0, a bundle with a pipeline and a different `run_as` identity fails with this error: - -``` -Error: pipelines do not support a setting a run_as user that is different from the owner. -``` - -The rule is: the run identity of the resource must be the same as the owner. -The owner is the deployment identity. - -CLI v1.20.0 supports `run_as` for jobs and pipelines. -Some resource types still use the old rule. -For example, `model_serving_endpoints` and `quality_monitors` fail with this error: - -``` -Error: model_serving_endpoints do not support a setting a run_as user that is different from the owner. -Current identity: . Run as identity: . -``` - -To fix the error, do one of these steps: - -* Deploy as the service principal. Then the owner and the run identity are the same. -* Move the resource to a bundle that does not set `run_as`. - -The `experimental.use_legacy_run_as` option changes the owner of pipelines to the `run_as` identity. -This option is experimental. -It requires a workspace admin to deploy, and a metastore admin for Unity Catalog pipelines. -Do not use this option for new bundles. - -## Clean up - -1. Remove the dev deployment: - - ```sh - databricks bundle destroy -t dev - ``` - -2. Remove the prod deployment. Use the identity that deployed prod: - - ```sh - databricks bundle destroy -t prod --var service_principal_application_id= - ``` +| `prod` | CI as the service principal, or a deployer | The service principal | The identity that deploys | -3. If you made an OAuth secret only for this example, delete the secret. -4. If you made the service principal only for this example, delete the service principal. +## Notes -`bundle destroy` removes only the resources that this bundle deployed. -It does not remove the service principal, the groups, or the Unity Catalog grants. +* Do not set `run_as` in dev. The CLI accepts it, but each developer then needs the `Service Principal: User` role. +* In CI, deploy as the service principal with OAuth M2M. Set `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, and `DATABRICKS_CLIENT_SECRET`. Do not use personal access tokens. +* Deploy prod from one identity only. The `root_path` contains the deployer name, so a different identity makes a second copy. +* `validate -t prod` can show a recommendation to include the deployment identity in `permissions`. Add that identity to the deployer group. +* `validate -t prod` can show the warning `workspace folder has permissions not configured in bundle`. The deployer owns its home folder. The deploy continues. From d7f9295e70851b893b2c86f5ad9b1036765af192 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:32:06 +0000 Subject: [PATCH 3/9] knowledge_base/run_as_service_principal: address review on PR #51 - Document that prod deploys only as the service principal (from CI), so root_path resolves to its home folder and one prod copy exists; a deploy by a person creates a separate copy. root_path itself is unchanged. - README: require Databricks CLI v1.20.0 or above, one action per usage step, label the expected output, qualify ownership as IS_OWNER set by the bundle, add the idempotent redeploy / destroy-scope note, and say when the permissions recommendation appears. Co-authored-by: Isaac Co-authored-by: omnigent --- .../run_as_service_principal/README.md | 43 ++++++++----------- .../run_as_service_principal/databricks.yml | 7 +-- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/knowledge_base/run_as_service_principal/README.md b/knowledge_base/run_as_service_principal/README.md index 0dadceb..57a6e1e 100644 --- a/knowledge_base/run_as_service_principal/README.md +++ b/knowledge_base/run_as_service_principal/README.md @@ -8,7 +8,7 @@ For more information, see the [run_as documentation](https://docs.databricks.com ## Prerequisites -* Databricks CLI. We tested this example with v1.20.0. +* Databricks CLI v1.20.0 or above * A workspace with Unity Catalog and serverless jobs. * A service principal in the workspace. Copy its application ID. * A deployer group (default `bundle-deployers`) that contains the deployers and the service principal. @@ -20,40 +20,35 @@ For more information, see the [run_as documentation](https://docs.databricks.com The bundle does not set `workspace.host`. Select the workspace with `--profile ` or the `DATABRICKS_HOST` environment variable. -1. Deploy and run the job in dev: +1. Deploy the bundle to dev: `databricks bundle deploy -t dev` +2. Run the job in dev: `databricks bundle run -t dev whoami` +3. In CI, authenticate as the service principal. See the Notes. +4. In CI, deploy the bundle to prod: ```sh - databricks bundle deploy -t dev - databricks bundle run -t dev whoami - ``` - -2. Deploy and run the job in prod. Set the application ID of the service principal: - - ```sh - databricks bundle validate -t prod --var service_principal_application_id= databricks bundle deploy -t prod --var service_principal_application_id= - databricks bundle run -t prod whoami ``` -3. Read the job output to see which identity ran the job. - In dev, the output shows your user name. - In prod, the output shows the application ID: +5. In CI, run the job in prod: `databricks bundle run -t prod whoami` +6. Read the job output to see which identity ran the job. - ``` - current_user(): - SDK user_name: - SDK display_name: - ``` +Expected output in prod (in dev, the lines show your user name): + +``` +current_user(): +SDK user_name: +SDK display_name: +``` -| Target | Deploys | Runs as | Owner | +| Target | Deploys | Runs as | `IS_OWNER` set by the bundle | | --- | --- | --- | --- | | `dev` | You | You | You | -| `prod` | CI as the service principal, or a deployer | The service principal | The identity that deploys | +| `prod` | The service principal, from CI | The service principal | The deploying identity (the service principal) | ## Notes * Do not set `run_as` in dev. The CLI accepts it, but each developer then needs the `Service Principal: User` role. -* In CI, deploy as the service principal with OAuth M2M. Set `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, and `DATABRICKS_CLIENT_SECRET`. Do not use personal access tokens. -* Deploy prod from one identity only. The `root_path` contains the deployer name, so a different identity makes a second copy. -* `validate -t prod` can show a recommendation to include the deployment identity in `permissions`. Add that identity to the deployer group. +* Deploy `prod` only as the service principal, from CI. Use OAuth M2M: set `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, and `DATABRICKS_CLIENT_SECRET`. Do not use personal access tokens. The `root_path` then resolves to the home folder of the service principal, so one prod copy exists. A deploy by a person makes a separate copy in the home folder of that person. +* A new deploy to the same target updates the same resources. `databricks bundle destroy -t prod` removes only the job of this bundle. +* If the deployer is not in the deployer group, `validate -t prod` shows a recommendation. It asks you to add the deployment identity to `permissions`. Add that identity to the deployer group. * `validate -t prod` can show the warning `workspace folder has permissions not configured in bundle`. The deployer owns its home folder. The deploy continues. diff --git a/knowledge_base/run_as_service_principal/databricks.yml b/knowledge_base/run_as_service_principal/databricks.yml index fe40d79..fa01e88 100644 --- a/knowledge_base/run_as_service_principal/databricks.yml +++ b/knowledge_base/run_as_service_principal/databricks.yml @@ -36,9 +36,10 @@ targets: prod: mode: production workspace: - # Deploy to a single, explicit location. When CI deploys as the service - # principal, current_user is the service principal, so this resolves to - # its home folder and there is exactly one copy of the prod deployment. + # Deploy prod only as the service principal (from CI). Then current_user is + # the service principal, this resolves to its home folder, and there is + # exactly one prod copy. A deploy by a person creates a separate copy under + # that person's home folder. root_path: /Workspace/Users/${workspace.current_user.userName}/.bundle/${bundle.name}/${bundle.target} run_as: service_principal_name: ${var.service_principal_application_id} From 6d45eca119cb65518ae19febd116b4b2dbac0ce6 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:34:27 +0000 Subject: [PATCH 4/9] knowledge_base/run_as_service_principal: set SP variable once in CI - Usage: export BUNDLE_VAR_service_principal_application_id once in CI, so prod deploy, run, and destroy all get the required variable. - Notes: one bullet for cloud support (serverless only), no secrets in the bundle, and no job schedule. Co-authored-by: Isaac Co-authored-by: omnigent --- knowledge_base/run_as_service_principal/README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/knowledge_base/run_as_service_principal/README.md b/knowledge_base/run_as_service_principal/README.md index 57a6e1e..7bd001b 100644 --- a/knowledge_base/run_as_service_principal/README.md +++ b/knowledge_base/run_as_service_principal/README.md @@ -23,14 +23,10 @@ Select the workspace with `--profile ` or the `DATABRICKS_HOST` environ 1. Deploy the bundle to dev: `databricks bundle deploy -t dev` 2. Run the job in dev: `databricks bundle run -t dev whoami` 3. In CI, authenticate as the service principal. See the Notes. -4. In CI, deploy the bundle to prod: - - ```sh - databricks bundle deploy -t prod --var service_principal_application_id= - ``` - -5. In CI, run the job in prod: `databricks bundle run -t prod whoami` -6. Read the job output to see which identity ran the job. +4. In CI, set the variable once: `export BUNDLE_VAR_service_principal_application_id=` +5. In CI, deploy the bundle to prod: `databricks bundle deploy -t prod` +6. In CI, run the job in prod: `databricks bundle run -t prod whoami` +7. Read the job output to see which identity ran the job. Expected output in prod (in dev, the lines show your user name): @@ -49,6 +45,7 @@ SDK display_name: * Do not set `run_as` in dev. The CLI accepts it, but each developer then needs the `Service Principal: User` role. * Deploy `prod` only as the service principal, from CI. Use OAuth M2M: set `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, and `DATABRICKS_CLIENT_SECRET`. Do not use personal access tokens. The `root_path` then resolves to the home folder of the service principal, so one prod copy exists. A deploy by a person makes a separate copy in the home folder of that person. -* A new deploy to the same target updates the same resources. `databricks bundle destroy -t prod` removes only the job of this bundle. +* A new deploy to the same target updates the same resources. With the same variable, `databricks bundle destroy -t prod` removes only the job of this bundle. * If the deployer is not in the deployer group, `validate -t prod` shows a recommendation. It asks you to add the deployment identity to `permissions`. Add that identity to the deployer group. * `validate -t prod` can show the warning `workspace folder has permissions not configured in bundle`. The deployer owns its home folder. The deploy continues. +* The bundle works on AWS, Azure, and GCP with serverless compute. We did not test classic compute. The bundle contains no secrets: the OAuth M2M secret stays in the CI secret store. The job has no schedule. From 8211b6114a84cd24e84d1652bb99bbeff3e4a785 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:52:07 +0000 Subject: [PATCH 5/9] knowledge_base/run_as_service_principal: root_path under /Workspace/bundle-examples, live-test findings - root_path: dev -> /Workspace/bundle-examples/${bundle.name}/${bundle.target}/${workspace.current_user.short_name}, prod -> /Workspace/bundle-examples/${bundle.name}/${bundle.target} (one stable prod copy). - README Notes from a live deploy/run/destroy test: deployers need Service Principal: User (Manager is not sufficient); a non-admin identity cannot take over an existing prod job; output label is now observed output. Drop the validate recommendation/warning notes. Co-authored-by: Isaac Co-authored-by: omnigent --- knowledge_base/run_as_service_principal/README.md | 8 ++++---- .../run_as_service_principal/databricks.yml | 14 +++++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/knowledge_base/run_as_service_principal/README.md b/knowledge_base/run_as_service_principal/README.md index 7bd001b..383dcb9 100644 --- a/knowledge_base/run_as_service_principal/README.md +++ b/knowledge_base/run_as_service_principal/README.md @@ -28,7 +28,7 @@ Select the workspace with `--profile ` or the `DATABRICKS_HOST` environ 6. In CI, run the job in prod: `databricks bundle run -t prod whoami` 7. Read the job output to see which identity ran the job. -Expected output in prod (in dev, the lines show your user name): +Output in prod (in dev, the lines show your user name): ``` current_user(): @@ -44,8 +44,8 @@ SDK display_name: ## Notes * Do not set `run_as` in dev. The CLI accepts it, but each developer then needs the `Service Principal: User` role. -* Deploy `prod` only as the service principal, from CI. Use OAuth M2M: set `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, and `DATABRICKS_CLIENT_SECRET`. Do not use personal access tokens. The `root_path` then resolves to the home folder of the service principal, so one prod copy exists. A deploy by a person makes a separate copy in the home folder of that person. +* In CI, deploy `prod` as the service principal with OAuth M2M. Set `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, and `DATABRICKS_CLIENT_SECRET`. Do not use personal access tokens. +* Each deployer needs the `Service Principal: User` role. The `Service Principal: Manager` role is not sufficient. Without it, the deploy fails with `Cannot bind the service principal provided in 'run_as' field`. +* Deploy `prod` from one identity. Another identity that is not a workspace admin cannot take over the job. The deploy fails with `only workspace admins can change the owner of a job`. * A new deploy to the same target updates the same resources. With the same variable, `databricks bundle destroy -t prod` removes only the job of this bundle. -* If the deployer is not in the deployer group, `validate -t prod` shows a recommendation. It asks you to add the deployment identity to `permissions`. Add that identity to the deployer group. -* `validate -t prod` can show the warning `workspace folder has permissions not configured in bundle`. The deployer owns its home folder. The deploy continues. * The bundle works on AWS, Azure, and GCP with serverless compute. We did not test classic compute. The bundle contains no secrets: the OAuth M2M secret stays in the CI secret store. The job has no schedule. diff --git a/knowledge_base/run_as_service_principal/databricks.yml b/knowledge_base/run_as_service_principal/databricks.yml index fa01e88..05ca923 100644 --- a/knowledge_base/run_as_service_principal/databricks.yml +++ b/knowledge_base/run_as_service_principal/databricks.yml @@ -32,15 +32,19 @@ targets: # Dev does not use run_as, so this value is never read. The variable has no # default on purpose: a prod deploy fails fast if you forget to set it. service_principal_application_id: not-used-in-dev + workspace: + # Each developer gets a separate dev copy under the shared example folder. + # Change root_path to deploy somewhere else. + root_path: /Workspace/bundle-examples/${bundle.name}/${bundle.target}/${workspace.current_user.short_name} prod: mode: production workspace: - # Deploy prod only as the service principal (from CI). Then current_user is - # the service principal, this resolves to its home folder, and there is - # exactly one prod copy. A deploy by a person creates a separate copy under - # that person's home folder. - root_path: /Workspace/Users/${workspace.current_user.userName}/.bundle/${bundle.name}/${bundle.target} + # One stable prod copy. Deploy it from one identity (CI as the service + # principal): a different identity that is not a workspace admin cannot + # take over ownership of the job. The deploying identity needs CAN_MANAGE + # on /Workspace/bundle-examples. Change root_path to deploy somewhere else. + root_path: /Workspace/bundle-examples/${bundle.name}/${bundle.target} run_as: service_principal_name: ${var.service_principal_application_id} permissions: From b55dcdf0e91701676f18c8caa5b39d72c7f4bafe Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:53:47 +0000 Subject: [PATCH 6/9] knowledge_base/run_as_service_principal: drop folder guidance comments Remove the root_path folder guidance from databricks.yml comments. root_path values are unchanged. Co-authored-by: Isaac Co-authored-by: omnigent --- knowledge_base/run_as_service_principal/databricks.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/knowledge_base/run_as_service_principal/databricks.yml b/knowledge_base/run_as_service_principal/databricks.yml index 05ca923..6865a2c 100644 --- a/knowledge_base/run_as_service_principal/databricks.yml +++ b/knowledge_base/run_as_service_principal/databricks.yml @@ -34,7 +34,6 @@ targets: service_principal_application_id: not-used-in-dev workspace: # Each developer gets a separate dev copy under the shared example folder. - # Change root_path to deploy somewhere else. root_path: /Workspace/bundle-examples/${bundle.name}/${bundle.target}/${workspace.current_user.short_name} prod: @@ -42,8 +41,7 @@ targets: workspace: # One stable prod copy. Deploy it from one identity (CI as the service # principal): a different identity that is not a workspace admin cannot - # take over ownership of the job. The deploying identity needs CAN_MANAGE - # on /Workspace/bundle-examples. Change root_path to deploy somewhere else. + # take over ownership of the job. root_path: /Workspace/bundle-examples/${bundle.name}/${bundle.target} run_as: service_principal_name: ${var.service_principal_application_id} From 3b14724b703d27b03d8e438ce04f5cc03f0c8635 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:55:16 +0000 Subject: [PATCH 7/9] knowledge_base/run_as_service_principal: README in sibling shape Remove the Notes section. Move the Service Principal: User role into Prerequisites and the CI OAuth M2M and BUNDLE_VAR settings into Usage. Shorten the identity table. Co-authored-by: Isaac Co-authored-by: omnigent --- .../run_as_service_principal/README.md | 60 ++++++++----------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/knowledge_base/run_as_service_principal/README.md b/knowledge_base/run_as_service_principal/README.md index 383dcb9..4eb8978 100644 --- a/knowledge_base/run_as_service_principal/README.md +++ b/knowledge_base/run_as_service_principal/README.md @@ -12,40 +12,32 @@ For more information, see the [run_as documentation](https://docs.databricks.com * A workspace with Unity Catalog and serverless jobs. * A service principal in the workspace. Copy its application ID. * A deployer group (default `bundle-deployers`) that contains the deployers and the service principal. -* The `Service Principal: User` role on the service principal for the deployer group. -* The Unity Catalog grants that your job needs, given to the service principal. `whoami` needs none. +* Deployers need the `Service Principal: User` role on the service principal. ## Usage -The bundle does not set `workspace.host`. -Select the workspace with `--profile ` or the `DATABRICKS_HOST` environment variable. - -1. Deploy the bundle to dev: `databricks bundle deploy -t dev` -2. Run the job in dev: `databricks bundle run -t dev whoami` -3. In CI, authenticate as the service principal. See the Notes. -4. In CI, set the variable once: `export BUNDLE_VAR_service_principal_application_id=` -5. In CI, deploy the bundle to prod: `databricks bundle deploy -t prod` -6. In CI, run the job in prod: `databricks bundle run -t prod whoami` -7. Read the job output to see which identity ran the job. - -Output in prod (in dev, the lines show your user name): - -``` -current_user(): -SDK user_name: -SDK display_name: -``` - -| Target | Deploys | Runs as | `IS_OWNER` set by the bundle | -| --- | --- | --- | --- | -| `dev` | You | You | You | -| `prod` | The service principal, from CI | The service principal | The deploying identity (the service principal) | - -## Notes - -* Do not set `run_as` in dev. The CLI accepts it, but each developer then needs the `Service Principal: User` role. -* In CI, deploy `prod` as the service principal with OAuth M2M. Set `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, and `DATABRICKS_CLIENT_SECRET`. Do not use personal access tokens. -* Each deployer needs the `Service Principal: User` role. The `Service Principal: Manager` role is not sufficient. Without it, the deploy fails with `Cannot bind the service principal provided in 'run_as' field`. -* Deploy `prod` from one identity. Another identity that is not a workspace admin cannot take over the job. The deploy fails with `only workspace admins can change the owner of a job`. -* A new deploy to the same target updates the same resources. With the same variable, `databricks bundle destroy -t prod` removes only the job of this bundle. -* The bundle works on AWS, Azure, and GCP with serverless compute. We did not test classic compute. The bundle contains no secrets: the OAuth M2M secret stays in the CI secret store. The job has no schedule. +1. Deploy the bundle to dev: `databricks bundle deploy -t dev --profile ` +2. Run the job in dev: `databricks bundle run -t dev whoami --profile ` +3. In CI, authenticate as the service principal with OAuth M2M and set the application ID: + + ```sh + export DATABRICKS_HOST=https:// + export DATABRICKS_CLIENT_ID= + export DATABRICKS_CLIENT_SECRET= + export BUNDLE_VAR_service_principal_application_id= + ``` + +4. In CI, deploy the bundle to prod: `databricks bundle deploy -t prod` +5. In CI, run the job in prod: `databricks bundle run -t prod whoami` +6. Read the job output. In dev, it shows your user name. In prod, it shows the service principal: + + ``` + current_user(): + SDK user_name: + SDK display_name: + ``` + +| Target | Runs as | +| --- | --- | +| `dev` | The user who deploys | +| `prod` | The service principal | From d8866be8ef92b66a81dfc0b4f93011363cd9f491 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Wed, 21 Oct 2026 22:56:47 +0000 Subject: [PATCH 8/9] Add destroy steps to run_as README Co-authored-by: omnigent --- knowledge_base/run_as_service_principal/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/knowledge_base/run_as_service_principal/README.md b/knowledge_base/run_as_service_principal/README.md index 4eb8978..0f03681 100644 --- a/knowledge_base/run_as_service_principal/README.md +++ b/knowledge_base/run_as_service_principal/README.md @@ -37,6 +37,9 @@ For more information, see the [run_as documentation](https://docs.databricks.com SDK display_name: ``` +7. Remove the dev resources: `databricks bundle destroy -t dev --profile ` +8. In CI, remove the prod resources: `databricks bundle destroy -t prod` + | Target | Runs as | | --- | --- | | `dev` | The user who deploys | From 445b17c83c94177a1480b92255cff1864dd8c565 Mon Sep 17 00:00:00 2001 From: Andres Garcia Date: Thu, 22 Oct 2026 02:35:21 +0000 Subject: [PATCH 9/9] Update run-as serverless environment Co-authored-by: omnigent --- .../run_as_service_principal/resources/whoami.job.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knowledge_base/run_as_service_principal/resources/whoami.job.yml b/knowledge_base/run_as_service_principal/resources/whoami.job.yml index f2a76a2..b1af1de 100644 --- a/knowledge_base/run_as_service_principal/resources/whoami.job.yml +++ b/knowledge_base/run_as_service_principal/resources/whoami.job.yml @@ -16,4 +16,4 @@ resources: - environment_key: default # See https://docs.databricks.com/api/workspace/jobs/create#environments-spec spec: - environment_version: "4" + environment_version: "6"