Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
7303c65
feat: incremental sync v1 - scoped implementation
JavierCarnelli-ConductorOne Aug 18, 2026
f435216
feat: extend incremental sync to role/permission-assignment changes
JavierCarnelli-ConductorOne Aug 19, 2026
ca5195e
Merge remote-tracking branch 'origin/main' into feat/incremental-sync…
JavierCarnelli-ConductorOne Aug 19, 2026
1d9c118
fix: add event_id tiebreaker to audit log pagination
JavierCarnelli-ConductorOne Aug 20, 2026
22c4e7f
fix: bound sql statement polling and cancel on give-up
JavierCarnelli-ConductorOne Aug 20, 2026
5d31b34
fix: nil-safe parent access in user/service-principal/role Get
JavierCarnelli-ConductorOne Aug 21, 2026
f6fbc49
fix: mirror token-auth branch in workspaceBuilder.Get
JavierCarnelli-ConductorOne Aug 21, 2026
590e616
fix: select audit event parent by Account API availability
JavierCarnelli-ConductorOne Aug 21, 2026
16f3b89
fix: don't require the Account API for incremental sync under token auth
JavierCarnelli-ConductorOne Aug 21, 2026
4a14d25
refactor: simplify quotedInClause and ExecuteStatement's status check
JavierCarnelli-ConductorOne Aug 21, 2026
62b6858
feat: add sql-warehouse-workspace to pin the audit-log query workspace
JavierCarnelli-ConductorOne Aug 21, 2026
1484ab6
fix: address remaining audit-log review findings
JavierCarnelli-ConductorOne Aug 26, 2026
a1835ef
fix: log corrupt event cursors instead of resetting silently
JavierCarnelli-ConductorOne Aug 26, 2026
ce795ff
chore: promote Debug logs to Warn for visibility
JavierCarnelli-ConductorOne Aug 26, 2026
a81b45f
chore: retrigger GitHub jobs
JavierCarnelli-ConductorOne Aug 27, 2026
f8ebc73
fix: drop incremental-sync fields from the workspace-token config group
JavierCarnelli-ConductorOne Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,40 @@ To instead exclude specific workspaces from the sync, pass them to the
list. Each entry can be a workspace name, deployment name, or numeric workspace
ID. Excluded workspaces and their roles are skipped entirely.

## Incremental sync

By default, `baton-databricks` does a full resync of every resource on every run.
You can opt into an additional, cheap pathway that polls a Databricks audit log
between full syncs to pick up access changes early, by setting
`--enable-incremental-sync` (or `BATON_ENABLE_INCREMENTAL_SYNC`). Full syncs
still run as the correctness backstop; incremental sync does not detect
deletions, which are only caught by the next full sync.

Incremental sync requires OAuth2 (service principal) authentication — it's not
available with workspace tokens, since the Account API needed to resolve audit
events back to synced resources is unreachable that way.

Incremental sync also requires:

- `--sql-warehouse-id` (or `BATON_SQL_WAREHOUSE_ID`), the ID of a Databricks SQL
warehouse the connector can use to query the `system.access.audit` table. A
small serverless warehouse is recommended to minimize cold-start latency.
- `--sql-warehouse-workspace` (or `BATON_SQL_WAREHOUSE_WORKSPACE`), the
deployment name of the workspace that hosts that SQL warehouse. SQL
warehouses only exist in one workspace, so this is required whenever more
than one workspace is available; with only one workspace it's inferred
automatically.
- A one-time setup performed by a Databricks admin, which the connector cannot
do on its own:
- An account admin must [enable the `access` system
schema](https://docs.databricks.com/en/admin/system-tables/index.html) for
the account's Unity Catalog metastore.
- A metastore admin must grant `SELECT` on `system.access` to the service
principal or user the connector authenticates as.

Once enabled, ongoing polling only needs that `SELECT` grant plus warehouse
access; no further elevated privilege is required.

## Group povisioning limitations
provisioning of account groups from a workspace token is not supported, if you need to provision groups you can only do it using the client-id and client-secret flow,
this is due to the fact that the Databricks API does not allow provisioning of groups from a workspace token.
Expand Down Expand Up @@ -144,7 +178,8 @@ Flags:
--client-secret string The client secret used to authenticate with ConductorOne ($BATON_CLIENT_SECRET)
--databricks-client-id string required: The Databricks service principal's client ID used to connect to the Databricks Account and Workspace API ($BATON_DATABRICKS_CLIENT_ID)
--databricks-client-secret string required: The Databricks service principal's client secret used to connect to the Databricks Account and Workspace API ($BATON_DATABRICKS_CLIENT_SECRET)
--databricks-exclude-workspaces strings Workspaces to exclude from sync, identified by workspace name, deployment name, or numeric workspace ID ($BATON_DATABRICKS_EXCLUDE_WORKSPACES)
--databricks-exclude-workspaces strings Workspaces to exclude from sync, identified by workspace name, deployment name, or numeric workspace ID. Mutually exclusive with workspaces. ($BATON_DATABRICKS_EXCLUDE_WORKSPACES)
--enable-incremental-sync Poll a Databricks audit-log event feed between full syncs to pick up access changes early. Deletions are still only caught by the next full sync. ($BATON_ENABLE_INCREMENTAL_SYNC)
--external-resource-c1z string The path to the c1z file to sync external baton resources with ($BATON_EXTERNAL_RESOURCE_C1Z)
--external-resource-entitlement-id-filter string The entitlement that external users, groups must have access to sync external baton resources ($BATON_EXTERNAL_RESOURCE_ENTITLEMENT_ID_FILTER)
--external-resource-traits strings Resource type traits (e.g. "user", "group", "app") to sync and match from the external resource c1z. When unset the matcher falls back to user and group; passing this flag replaces the full set rather than adding to it. ($BATON_EXTERNAL_RESOURCE_TRAITS)
Expand All @@ -164,6 +199,8 @@ Flags:
-p, --provisioning This must be set in order for provisioning actions to be enabled ($BATON_PROVISIONING)
--skip-entitlements-and-grants This must be set to skip syncing of entitlements and grants ($BATON_SKIP_ENTITLEMENTS_AND_GRANTS)
--skip-full-sync This must be set to skip a full sync ($BATON_SKIP_FULL_SYNC)
--sql-warehouse-id string ID of the Databricks SQL warehouse used to query system.access.audit. Required when incremental sync is enabled. ($BATON_SQL_WAREHOUSE_ID)
--sql-warehouse-workspace string Deployment name of the workspace that hosts the SQL warehouse (sql-warehouse-id), since SQL warehouses only exist in one workspace. Required when incremental sync is enabled and more than one workspace is available; if omitted with only one workspace available, that workspace is used automatically. ($BATON_SQL_WAREHOUSE_WORKSPACE)
--storage-engine string The storage engine to use when opening the sync c1z file: sqlite or pebble. Leave unset to use the baton-sdk default. ($BATON_STORAGE_ENGINE)
--sync-resource-types strings The resource type IDs to sync ($BATON_SYNC_RESOURCE_TYPES)
--sync-resources strings The resource IDs to sync ($BATON_SYNC_RESOURCES)
Expand All @@ -172,7 +209,7 @@ Flags:
-v, --version version for baton-databricks
--workers int The number of sync workers to use. -1 for auto-detect, 0 for sequential, >0 for parallel ($BATON_WORKERS)
--workspace-tokens strings required: The Databricks personal access tokens scoped to specific workspaces used to connect to the Databricks Workspace API ($BATON_WORKSPACE_TOKENS)
--workspaces strings Limit syncing to the specified workspaces, by deployment name, not workspace ID. Required when using workspace tokens, in the same order as workspace-tokens. ($BATON_WORKSPACES)
--workspaces strings Limit syncing to the specified workspaces, by deployment name, not workspace ID. Required when using workspace tokens, in the same order as workspace-tokens. Mutually exclusive with databricks-exclude-workspaces. ($BATON_WORKSPACES)

Use "baton-databricks [command] --help" for more information about a command.
```
11 changes: 10 additions & 1 deletion baton_capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"capabilities": [
"CAPABILITY_SYNC",
"CAPABILITY_TARGETED_SYNC",
"CAPABILITY_PROVISION"
],
"permissions": {}
Expand All @@ -22,6 +23,7 @@
},
"capabilities": [
"CAPABILITY_SYNC",
"CAPABILITY_TARGETED_SYNC",
"CAPABILITY_PROVISION"
],
"permissions": {}
Expand All @@ -36,6 +38,7 @@
},
"capabilities": [
"CAPABILITY_SYNC",
"CAPABILITY_TARGETED_SYNC",
"CAPABILITY_PROVISION"
],
"permissions": {}
Expand All @@ -50,6 +53,7 @@
},
"capabilities": [
"CAPABILITY_SYNC",
"CAPABILITY_TARGETED_SYNC",
"CAPABILITY_PROVISION"
],
"permissions": {}
Expand All @@ -69,6 +73,7 @@
},
"capabilities": [
"CAPABILITY_SYNC",
"CAPABILITY_TARGETED_SYNC",
"CAPABILITY_ACCOUNT_PROVISIONING",
"CAPABILITY_RESOURCE_DELETE"
],
Expand All @@ -84,6 +89,7 @@
},
"capabilities": [
"CAPABILITY_SYNC",
"CAPABILITY_TARGETED_SYNC",
"CAPABILITY_PROVISION"
],
"permissions": {}
Expand All @@ -93,7 +99,10 @@
"CAPABILITY_PROVISION",
"CAPABILITY_SYNC",
"CAPABILITY_ACCOUNT_PROVISIONING",
"CAPABILITY_RESOURCE_DELETE"
"CAPABILITY_RESOURCE_DELETE",
"CAPABILITY_TARGETED_SYNC",
"CAPABILITY_EVENT_FEED_V2",
"CAPABILITY_SERVICE_MODE_TARGETED_SYNC"
],
"credentialDetails": {
"capabilityAccountProvisioning": {
Expand Down
23 changes: 22 additions & 1 deletion config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,24 @@
"displayName": "Exclude Workspaces",
"description": "Workspaces to exclude from sync, identified by workspace name, deployment name, or numeric workspace ID. Mutually exclusive with workspaces.",
"stringSliceField": {}
},
{
"name": "enable-incremental-sync",
"displayName": "Enable Incremental Sync",
"description": "Poll a Databricks audit-log event feed between full syncs to pick up access changes early. Deletions are still only caught by the next full sync.",
"boolField": {}
},
{
"name": "sql-warehouse-id",
"displayName": "SQL Warehouse ID",
"description": "ID of the Databricks SQL warehouse used to query system.access.audit. Required when incremental sync is enabled.",
"stringField": {}
},
{
"name": "sql-warehouse-workspace",
"displayName": "SQL Warehouse Workspace",
"description": "Deployment name of the workspace that hosts the SQL warehouse (sql-warehouse-id), since SQL warehouses only exist in one workspace. Required when incremental sync is enabled and more than one workspace is available; if omitted with only one workspace available, that workspace is used automatically.",
"stringField": {}
}
],
"constraints": [
Expand Down Expand Up @@ -199,7 +217,10 @@
"account-hostname",
"workspaces",
"base-url",
"databricks-exclude-workspaces"
"databricks-exclude-workspaces",
"enable-incremental-sync",
"sql-warehouse-id",
"sql-warehouse-workspace"
],
"default": true
},
Expand Down
6 changes: 6 additions & 0 deletions docs/connector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ sidebarTitle: "Databricks"

The Databricks connector supports [automatic account provisioning and deprovisioning](/product/admin/account-provisioning).

### Optional: faster updates between syncs

By default, the Databricks connector picks up access changes on its regular sync schedule. You can optionally turn on incremental sync, which checks Databricks' activity log between full syncs so that changes like new group members show up in C1 sooner. Full syncs still run as usual and remain the source of truth; removed access is only reflected after the next full sync.

Turning this on requires a small Databricks SQL warehouse and a one-time setup step performed by a Databricks admin (granting the connector read access to Databricks' `system.access` activity log). Ask your connector operator or C1 support contact to enable it for you.

## Gather Databricks credentials

Configuring the connector requires you to pass in credentials generated in Databricks. Gather these credentials before you move on.
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/conf.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 30 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,26 @@ var (
field.WithDescription("Workspaces to exclude from sync, identified by workspace name, deployment name, or numeric workspace ID. Mutually exclusive with workspaces."),
field.WithDisplayName("Exclude Workspaces"),
)
EnableIncrementalSyncField = field.BoolField(
"enable-incremental-sync",
field.WithDescription("Poll a Databricks audit-log event feed between full syncs to pick up access changes early. Deletions are still only caught by the next full sync."),
field.WithDisplayName("Enable Incremental Sync"),
field.WithDefaultValue(false),
)
SQLWarehouseIDField = field.StringField(
"sql-warehouse-id",
field.WithDescription("ID of the Databricks SQL warehouse used to query system.access.audit. Required when incremental sync is enabled."),
field.WithDisplayName("SQL Warehouse ID"),
)
SQLWarehouseWorkspaceField = field.StringField(
"sql-warehouse-workspace",
field.WithDescription(
"Deployment name of the workspace that hosts the SQL warehouse (sql-warehouse-id), since SQL warehouses "+
"only exist in one workspace. Required when incremental sync is enabled and more than one workspace "+
"is available; if omitted with only one workspace available, that workspace is used automatically.",
),
field.WithDisplayName("SQL Warehouse Workspace"),
)
configFields = []field.SchemaField{
AccountHostnameField,
AccountIdField,
Expand All @@ -80,6 +100,9 @@ var (
WorkspaceTokensField,
BaseURLField,
ExcludeWorkspacesField,
EnableIncrementalSyncField,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: also add EnableIncrementalSyncField and SQLWarehouseIDField to every auth field group that supports incremental sync. They are in configFields (here) but missing from both group Fields lists below, so the grouped schema does not associate them with a selectable auth mode and SDK validation skips them for that mode.

Keep common feature fields in each applicable auth group. Pattern: https://github.com/ConductorOne/baton-azure-devops/blob/47b239de197e4c4c35da801e08c59ba6009f78e8/pkg/config/config.go#L215-L267

SQLWarehouseIDField,
SQLWarehouseWorkspaceField,

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.

🟡 Suggestion: enable-incremental-sync, sql-warehouse-id, and sql-warehouse-workspace were added to configFields but to neither entry in WithFieldGroups below, unlike every other connector-specific field (workspaces, base-url, databricks-exclude-workspaces, …) which appear in both groups. config_schema.json's fieldGroups confirms the omission. If the UI renders fields per selected auth group, these three won't be settable there — add them to the oauth2 group (and to workspace-token if that mode should support incremental sync).

}
)

Expand All @@ -101,15 +124,20 @@ var Config = field.NewConfiguration(
Fields: []field.SchemaField{
AccountIdField, DatabricksClientIdField, DatabricksClientSecretField,
HostnameField, AccountHostnameField, WorkspacesField, BaseURLField, ExcludeWorkspacesField,
EnableIncrementalSyncField, SQLWarehouseIDField, SQLWarehouseWorkspaceField,
},
Default: true,
},
{
Name: DatabricksWorkspaceTokenGroup,
DisplayName: "Workspace token",
HelpText: "Authenticate with a personal access token scoped to each workspace.",
Fields: []field.SchemaField{AccountIdField, WorkspacesField, WorkspaceTokensField, HostnameField, AccountHostnameField, BaseURLField, ExcludeWorkspacesField},
Default: false,
// Incremental sync requires the Account API, which workspace tokens can't reach
// (see Validate) — omitted here so the UI doesn't offer an option that can never work.
Fields: []field.SchemaField{
AccountIdField, WorkspacesField, WorkspaceTokensField, HostnameField, AccountHostnameField, BaseURLField, ExcludeWorkspacesField,
},
Default: false,
},
}),
)
Expand Down
10 changes: 10 additions & 0 deletions pkg/connector/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ func (a *accountBuilder) Grant(ctx context.Context, principal *v2.Resource, enti
return nil, nil
}

// Get returns the singleton account resource, used to re-sync it after a RESOURCE_CHANGE event.
func (a *accountBuilder) Get(ctx context.Context, resourceId *v2.ResourceId, parentResourceId *v2.ResourceId) (*v2.Resource, annotations.Annotations, error) {
resource, err := a.accountResource(ctx)
if err != nil {
return nil, nil, err
}

return resource, nil, nil
}

func (a *accountBuilder) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) {
l := ctxzap.Extract(ctx)

Expand Down
Loading
Loading