Skip to content

internal: Add support for 'sensitive' JSON tag for bundle config fields - #5896

Merged
andrewnester merged 14 commits into
mainfrom
feat/sensitive-fields
Jul 22, 2026
Merged

internal: Add support for 'sensitive' JSON tag for bundle config fields#5896
andrewnester merged 14 commits into
mainfrom
feat/sensitive-fields

Conversation

@andrewnester

Copy link
Copy Markdown
Contributor

Changes

Add support for 'sensitive' JSON tag for bundle config fields

Why

This allows us to mark certain fields (for example, secrets.value) as sensitive and let CLI handle masking it in command outputs.

Used in #5861

Tests

Added unit tests. Will be tests end to end with unit tests with UC secrets

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 49ea5f6

Run: 29924738401

Env🟨​KNOWN🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
💚​aws linux44314105712:59
💚​aws windows4431610556:15
🔄​azure linux144313105613:41
💚​azure windows4431610546:48
🟨​gcp linux314313105811:34
🟨​gcp windows314315105610:09
9 interesting tests: 4 SKIP, 3 KNOWN, 1 RECOVERED, 1 flaky
Test Nameaws linuxaws windowsazure linuxazure windowsgcp linuxgcp windows
💚​TestAccept💚​R💚​R💚​R💚​R💚​R💚​R
🙈​TestAccept/bundle/invariant/no_drift🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🔄​TestSyncIncrementalFileOverwritesFolder✅​p✅​p🔄​f✅​p✅​p✅​p
🟨​TestFetchRepositoryInfoAPI_FromRepo💚​R💚​R💚​R💚​R🟨​K🟨​K
🟨​TestFetchRepositoryInfoAPI_FromRepo/root💚​R💚​R💚​R💚​R🟨​K🟨​K
🟨​TestFetchRepositoryInfoAPI_FromRepo/subdir💚​R💚​R💚​R💚​R🟨​K🟨​K
Top 8 slowest tests (at least 2 minutes):
durationenvtestname
3:31gcp windowsTestAccept
3:26azure windowsTestAccept
3:22aws windowsTestAccept
2:23aws linuxTestFilerRecursiveDelete/workspace_files
2:23aws linuxTestLock
2:21azure windowsTestFilerWorkspaceFilesExtensionsReadDir
2:11aws linuxTestFilerWorkspaceFilesExtensionsReadDir
2:09gcp linuxTestAccept/bundle/deploy/mlops-stacks/DATABRICKS_BUNDLE_ENGINE=direct

Comment threadbundle/config/mask.go Outdated
Comment threadbundle/config/mask.go Outdated
Comment threadcmd/bundle/validate.go Outdated
Comment threadlibs/dyn/convert/struct_info.go
Comment threadlibs/dyn/convert/struct_info.go Outdated
}
return si.Sensitive
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a few unit tests here in dyn/convert that demonstrates that this works in cases with embedded structs, anonymous structs, etc. Would also be good to include a user of these fields in the same package.

Comment threadcmd/bundle/validate.go Outdated
Comment threadbundle/config/mask_test.go Outdated
Comment threadbundle/config/validate/no_reference_to_sensitive_fields.go Outdated
Comment threadlibs/dyn/yamlsaver/saver.go Outdated
Comment threadlibs/dyn/value.go Outdated
Comment threadlibs/dyn/convert/from_typed.go
Comment threadlibs/dyn/convert/from_typed.go Outdated
Comment threadlibs/dyn/convert/from_typed_test.go
case dyn.KindString:
// Preserve sensitive strings: NewValue would strip the secretString wrapper.
if src.IsSensitive() {
return src, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means it is not possible to update the secret value in the typed tree.

If expected, please include this expectation in the comment.

@andrewnester
andrewnester added this pull request to the merge queueJul 22, 2026
Merged via the queue into main with commit 40ee185Jul 22, 2026
23 checks passed
@andrewnester
andrewnester deleted the feat/sensitive-fields branch July 22, 2026 15:08
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 40ee185

Run: 29932021784

Env❌​FAIL🟨​KNOWN🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
❌​aws linux21332972879186:24
❌​aws windows20242912898171:38
❌​azure linux42442880915156:06
❌​azure windows4242823934141:52
❌​gcp linux20512856921143:01
❌​gcp windows20512795940132:39
37 interesting tests: 27 FAIL, 6 KNOWN, 2 flaky, 2 SKIP
Test Nameaws linuxaws windowsazure linuxazure windowsgcp linuxgcp windows
🟨​TestAccept🟨​K🟨​K🟨​K🟨​K🟨​K🟨​K
❌​TestAccept/bundle/invariant/continue_293❌​F❌​F❌​F❌​F❌​F❌​F
❌​TestAccept/bundle/invariant/continue_293/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=instance_pool.yml.tmpl❌​F❌​F❌​F❌​F❌​F❌​F
🟨​TestAccept/bundle/invariant/no_drift🟨​K🟨​K🟨​K🟨​K🟨​K🟨​K
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=✅​p❌​F✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1✅​p❌​F✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=instance_pool.yml.tmpl/READPLAN=❌​F❌​F❌​F❌​F❌​F❌​F
❌​TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=instance_pool.yml.tmpl/READPLAN=1❌​F❌​F❌​F❌​F❌​F❌​F
❌​TestAccept/bundle/resources/catalogs/basic❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/catalogs/basic/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/catalogs/with-schemas✅​p❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/catalogs/with-schemas/DATABRICKS_BUNDLE_ENGINE=direct✅​p❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/clusters/lifecycle-started❌​F✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct❌​F✅​p✅​p✅​p✅​p✅​p
❌​TestAccept/bundle/resources/dashboards/change-name❌​F✅​p🔄​f✅​p✅​p✅​p
❌​TestAccept/bundle/resources/dashboards/change-name/DATABRICKS_BUNDLE_ENGINE=terraform❌​F✅​p🔄​f✅​p✅​p✅​p
❌​TestAccept/bundle/resources/grants/catalogs❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/catalogs/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/schemas/change_privilege❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/schemas/change_privilege/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/schemas/duplicate_principals❌​F✅​p✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/schemas/duplicate_principals/DATABRICKS_BUNDLE_ENGINE=direct❌​F✅​p✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/schemas/out_of_band_principal❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/schemas/out_of_band_principal/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/volumes✅​p❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/grants/volumes/DATABRICKS_BUNDLE_ENGINE=direct✅​p❌​F✅​p✅​p❌​F❌​F
🔄​TestAccept/bundle/resources/permissions/jobs/delete_one✅​p✅​p🔄​f✅​p✅​p✅​p
🔄​TestAccept/bundle/resources/permissions/jobs/delete_one/DATABRICKS_BUNDLE_ENGINE=terraform✅​p✅​p🔄​f✅​p✅​p✅​p
🟨​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name🟨​K💚​R💚​R💚​R💚​R💚​R
❌​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name/DATABRICKS_BUNDLE_ENGINE=direct❌​F✅​p✅​p✅​p✅​p✅​p
🙈​TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
❌​TestAccept/bundle/resources/volumes/uppercase-name❌​F❌​F✅​p✅​p❌​F❌​F
❌​TestAccept/bundle/resources/volumes/uppercase-name/DATABRICKS_BUNDLE_ENGINE=direct❌​F❌​F✅​p✅​p❌​F❌​F
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🟨​TestFetchRepositoryInfoAPI_FromRepo💚​R💚​R💚​R💚​R🟨​K🟨​K
🟨​TestFetchRepositoryInfoAPI_FromRepo/root💚​R💚​R💚​R💚​R🟨​K🟨​K
🟨​TestFetchRepositoryInfoAPI_FromRepo/subdir💚​R💚​R💚​R💚​R🟨​K🟨​K
Top 50 slowest tests (at least 2 minutes):
durationenvtestname
14:45aws linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
13:43gcp windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
13:34gcp linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
12:58gcp linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
12:53aws windowsTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
12:47gcp windowsTestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
12:37gcp windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
10:59gcp windowsTestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
10:58aws windowsTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
10:58aws linuxTestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
10:45aws windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
10:27gcp linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
9:43aws linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:38azure linuxTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster_apply_policy_default_values.yml.tmpl/READPLAN=1
9:29gcp linuxTestAccept/bundle/resources/clusters/deploy/data_security_mode/DATABRICKS_BUNDLE_ENGINE=direct
9:23gcp linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
9:11aws linuxTestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
9:07gcp linuxTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
9:05gcp linuxTestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
8:52gcp windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
8:51azure windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:46azure linuxTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:44azure windowsTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:39aws linuxTestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
8:35aws windowsTestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:34gcp linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
8:16azure linuxTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=
8:15gcp windowsTestAccept/bundle/resources/clusters/deploy/data_security_mode/DATABRICKS_BUNDLE_ENGINE=direct
8:13azure linuxTestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:04gcp linuxTestAccept/bundle/resources/clusters/resize-terminated-fallback/DATABRICKS_BUNDLE_ENGINE=direct
8:04aws linuxTestAccept/bundle/resources/clusters/deploy/data_security_mode/DATABRICKS_BUNDLE_ENGINE=direct
7:52gcp windowsTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
7:24gcp linuxTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
7:18azure linuxTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
7:11aws linuxTestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
7:08aws windowsTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=
6:40gcp linuxTestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
6:37aws windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
6:23gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
6:22aws windowsTestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=cluster.yml.tmpl/READPLAN=1
6:15gcp linuxTestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:54gcp linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:52azure windowsTestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:52azure linuxTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:45aws linuxTestAccept/bundle/resources/clusters/resize-terminated-fallback/DATABRICKS_BUNDLE_ENGINE=direct
5:39azure windowsTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
5:19aws windowsTestAccept/bundle/invariant/migrate/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=schema.yml.tmpl
5:14gcp windowsTestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:12gcp windowsTestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:04aws windowsTestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct

janniklasrose added a commit that referenced this pull request Aug 5, 2026
## Changes
Revert usage of RedactSensitiveFields
## Why
RedactSensitiveFields (added in #5896, merged 2026-07-22) deep-clones
the state struct field-by-field via reflection, then marshals the clone.
The clone skips unexported fields
(libs/structs/structwalk/redact.go:44-46), and duration.Duration stores
its whole value in an unexported field while marshalling through a
custom MarshalJSON
So on main, every duration written to the direct-engine state file is
silently corrupted to 0s.
## Tests
Golden fiels updated
<!-- If your PR needs to be included in the release notes for next
release,
add a changelog fragment: create .nextchanges/<section>/<name>.md with a
one-line description (e.g. .nextchanges/cli/quickstart.md). See
.nextchanges/README.md. -->
---------
Co-authored-by: Jan N Rose <janniklas.rose@gmail.com>
deco-sdk-taggingBot added a commit that referenced this pull request Aug 6, 2026
## Release v1.11.0
### CLI
* Fixed `databricks repos get/update/delete` failing with `object at path "..." is not a repo` for Git-CLI-enabled folders (currently in preview), which the workspace API reports as directories rather than repos ([#6181](#6181)).
* Support `dbfs:/Skills/...` paths in `databricks fs` commands, routed to the Files API. ([#6147](#6147))
### Bundles
* For jobs where `ai_runtime_task.code_source_path` is a relative path to a local directory, the directory is now packaged into a tarball (honoring `.gitignore` and `sync.include`/`sync.exclude`), uploaded during deployment, and `code_source_path` is rewritten to the uploaded workspace path. ([#6110](#6110))
* Added JSON output to `bundle init`. Running `databricks bundle init <template> -o json` now reports the files the template wrote, relative to the output directory. This lets callers that pass `--output-dir` learn where the template materialized instead of assuming the output is a single directory named after the project. The default text output is unchanged. ([#6161](#6161))
* The terraform deployment engine is deprecated and will stop working in a future version of the CLI. Setting `bundle.engine: terraform` now emits a deprecation warning. See https://docs.databricks.com/aws/en/dev-tools/bundles/direct for how to migrate to the direct deployment engine. ([#6099](#6099))
* Fixed the direct deployment engine planning a spurious `create` for an empty `grants: []` list. Terraform records no grants resource for such a list, so `bundle plan` after `bundle deployment migrate` no longer reports an action for it. Emptying a previously deployed list still revokes the grants, after which the node is dropped from the deployment state instead of being reported as unchanged forever. ([#6039](#6039))
* Fixed `bundle generate` downloading notebooks found inside a folder without their file extension. They are now exported like top-level notebooks, so a Python notebook lands as `notebook.py` instead of an extensionless file ([#6144](#6144)).
* direct: `webhook_notifications.on_*` destinations on jobs, tasks, and `for_each_task` are now compared as unordered sets. Previously the Jobs API returning these lists in a different order than submitted produced a phantom diff that `bundle plan` and `bundle deploy` could never converge past, reporting `1 to change` on every run ([#6060](#6060)).
* Fixed a pipeline with `allow_duplicate_names: true` never converging on the direct engine: the field is only accepted on create/update and is never returned by the pipelines GET API, so every subsequent `bundle plan` reported the pipeline as a perpetual update. ([#6076](#6076))
* direct: A local change to an input-only field (one the API accepts on write but never returns on read, e.g. pipelines' `run_as` or external locations' `skip_validation`) is no longer silently skipped when the new value coincidentally matches the field's fabricated remote value. Previously such a change could hit the `remote_already_set` shortcut and be dropped from the plan. ([#6112](#6112))
* Revert usage of RedactiveSenstiveFields (added in [#5896](#5896), released in 1.10.0) which lead to incorrect behaviour (permanent drift) for duration field in Postgres resources ([#6179](#6179)).
* Document postgres resource fields in the json schema ([#6164](#6164), [#6163](#6163)).
* direct: Recreating a `vector_search_indexes` resource no longer fails with "Index ... is currently pending deletion" when the backend has not yet released the index name. The create is now retried until the name becomes available. ([#6143](#6143))
### Dependency Updates
* Bump `github.com/databricks/databricks-sdk-go` from v0.165.0 to v0.166.0. ([#6175](#6175))
* Upgrade Terraform provider to 1.124.0. ([#6174](#6174))
andrewnester added a commit that referenced this pull request Aug 6, 2026
## Changes
Add support for Unity Catalog (UC) secrets as a first-class DAB resource
type under the direct deployment engine.
Notes:
- Secret values are never persisted to deployment state — the remote
plaintext is not returned by the API and is intentionally omitted from
state to avoid leaking secrets
- New validation mutator (ValidateSecretValueIsVariable) that rejects
plain-text value fields at bundle validation time — the value must be a
variable reference (e.g. ${var.my_secret}) so secrets are never
committed to databricks.yml
Fixes#3689
Also requires #5896
## Example
```
bundle:
name: my-bundle
variables:
my_secret_value:
description: "Secret value passed at deploy time"
resources:
secrets:
my_secret:
catalog_name: main
schema_name: default
name: my_api_key
value: ${var.my_secret_value}
comment: "API key for external service"
```
## Tests
Acceptance tests covering create/update/delete, update-value,
direct-only enforcement, and the plain-text validation error
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@andrewnester@eng-dev-ecosystem-bot@pietern@shreyas-goenka