Migrate SCIM API calls to IAM v2
This page describes how to move scripts that manage users, groups, service principals, group memberships, entitlements, and workspace assignments from the System for Cross-domain Identity Management (SCIM) APIs to the identity and access management (IAM) v2 APIs. It covers REST calls, the Databricks SDK for Python, the Databricks SDK for Go, the Databricks SDK for Java, and the Databricks CLI.
For Terraform configurations, see Migrate Terraform identity resources to IAM v2.
Requirements
- Databricks SDK for Python 0.159.0 and above.
- Databricks SDK for Go 0.192.0 and above.
- Databricks SDK for Java 0.173.0 and above.
- Databricks CLI 1.24.0 and above.
Key differences
- One API per resource: Each SCIM resource maps to a set of create, get, list, update, and delete methods. Group memberships and workspace entitlements are separate resources, so you don't send SCIM
PATCHoperations. - Workspace-level and account-level methods: Call the workspace-level methods on a workspace URL and the account-level methods on the account console URL. Users, groups, and service principals belong to the account. The workspace-level methods manage the account that contains the workspace you call.
- Workspace access is separate: Creating a user, group, or service principal adds it to the account only. To give it access to a workspace, create a workspace assignment.
- Account groups only: The workspace-level SCIM API creates workspace-local groups. IAM v2 creates and manages account groups, including from the workspace-level methods. The caller also gets the group manager role on each group it creates, on a best-effort basis.
- Resource names: Each object has a
name, such asusers/123at the workspace level oraccounts/<account-id>/users/123at the account level. Get, update, and delete take thename. - Parents: Group member methods take the group's
nameasparent, such asgroups/123. Account-level list and create methods takeaccounts/<account-id>asparent. Workspace-level list and create methods take an optionalparentthat defaults to your account. - Same IDs:
user_id,group_id,service_principal_id, andprincipal_idare the same IDs that SCIM returns inid. - Field names: Fields use
snake_case, for exampleusernameinstead ofuserNameandgroup_nameinstead ofdisplayName. A user's email is itsusername, and a user'sdisplayNameisfull_name.given_namefollowed byfull_name.family_name. - Updates: Update methods take an
update_maskthat lists the fields to change, for examplefull_name.given_name,account_user_status. - Filters and paging: List filters use
snake_casefield names witheq, for exampleusername eq "jane.doe@example.com". Users supportusername eqandexternal_id eq, and groups supportgroup_name eqandexternal_id eq. Results are paged withpage_size(1000 at most) andpage_token, notstartIndexandcount.
Endpoint mapping
The following table maps each current call to the workspace-level IAM v2 call. In the table, /scim/v2 stands for /api/2.0/preview/scim/v2 in a workspace and /api/2.0/accounts/{account-id}/scim/v2 in the account. The last two rows are calls to the account permission assignment API.
To get the account-level IAM v2 call:
- Add
accounts/<account-id>/after/api/2.0/identity/. For example,GET /api/2.0/identity/usersbecomesGET /api/2.0/identity/accounts/<account-id>/users. - For workspace assignments, add
accounts/<account-id>/workspaces/<workspace-id>/after/api/2.0/identity/instead.
For the request and response fields of each call, see the IAM v2 API reference:
- Workspace level: Workspace User API, Workspace Group API, Workspace Service Principal API, Workspace Direct Group Member API, and Workspace Assignment API.
- Account level: Account User API, Account Group API, Account Service Principal API, Account Direct Group Member API, and Account Workspace Assignment API.
Current call | IAM v2 call |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Deleting a user, group, or service principal deletes it from the account and removes it from every workspace, even when you call the workspace-level method. To remove a principal from one workspace only, delete its workspace assignment.
The following table maps the SDK and Databricks CLI clients. Workspace-level IAM v2 methods end in _proxy or Proxy, such as create_user_proxy in Python and CreateUserProxy in Go.
Client | SCIM clients | IAM v2 client |
|---|---|---|
Databricks SDK for Python |
|
|
Databricks SDK for Go |
|
|
Databricks SDK for Java |
|
|
Databricks CLI |
|
|
Entitlement mapping
Entitlements move from the user, group, or service principal to its workspace assignment, which lists every entitlement granted directly to the principal in that workspace.
SCIM entitlement or permission | IAM v2 entitlement |
|---|---|
|
|
Permission | An assignment with the entitlements the principal needs, such as |
|
|
|
|
|
|
|
|
Permission |
|
Membership in the workspace | No change. Membership in the |
Examples
The examples use the workspace-level methods. Replace <workspace-url> with your workspace URL, and authenticate as described in Authorize access to Databricks resources.
Create a user
To create a user, run the following. IAM v2 requires username, full_name, and account_user_status on create, so a request that sends only username fails.
- curl
- Python
- Go
- Java
- CLI
curl -X POST "https://<workspace-url>/api/2.0/identity/users" \
-H "Authorization: Bearer $DATABRICKS_TOKEN" \
-d '{
"username": "jane.doe@example.com",
"full_name": {"given_name": "Jane", "family_name": "Doe"},
"account_user_status": "ACTIVE"
}'
from databricks.sdk import WorkspaceClient
from databricks.sdk.service import iamv2
w = WorkspaceClient()
user = w.workspace_iam_v2.create_user_proxy(
user=iamv2.User(
username="jane.doe@example.com",
full_name=iamv2.UserFullName(given_name="Jane", family_name="Doe"),
account_user_status=iamv2.State.ACTIVE,
)
)
print(user.name) # users/<user-id>
user, err := w.WorkspaceIamV2.CreateUserProxy(ctx, iamv2.CreateUserProxyRequest{
User: iamv2.User{
Username: "jane.doe@example.com",
FullName: iamv2.UserFullName{GivenName: "Jane", FamilyName: "Doe"},
AccountUserStatus: iamv2.StateActive,
},
})
User user = w.workspaceIamV2().createUserProxy(
new CreateUserProxyRequest().setUser(
new User()
.setUsername("jane.doe@example.com")
.setFullName(new UserFullName().setGivenName("Jane").setFamilyName("Doe"))
.setAccountUserStatus(State.ACTIVE)));
databricks workspace-iam-v2 create-user-proxy --json '{
"username": "jane.doe@example.com",
"full_name": {"given_name": "Jane", "family_name": "Doe"},
"account_user_status": "ACTIVE"
}'
Find a user by username
To look up a user by username, run the following:
- curl
- Python
- Go
- Java
- CLI
curl -G "https://<workspace-url>/api/2.0/identity/users" \
-H "Authorization: Bearer $DATABRICKS_TOKEN" \
--data-urlencode 'filter=username eq "jane.doe@example.com"'
for user in w.workspace_iam_v2.list_users_proxy(filter='username eq "jane.doe@example.com"'):
print(user.name, user.user_id)
users, err := w.WorkspaceIamV2.ListUsersProxyAll(ctx, iamv2.ListUsersProxyRequest{
Filter: `username eq "jane.doe@example.com"`,
})
for _, user := range users {
fmt.Println(user.Name, user.UserId)
}
for (User user : w.workspaceIamV2().listUsersProxy(
new ListUsersProxyRequest().setFilter("username eq \"jane.doe@example.com\""))) {
System.out.println(user.getName());
}
databricks workspace-iam-v2 list-users-proxy --filter 'username eq "jane.doe@example.com"'
The SDKs and the Databricks CLI fetch every page for you. In REST, repeat the call with page_token set to the previous next_page_token until it's empty.
Update a user
To change a user's name and deactivate the user in the account, run the following:
- curl
- Python
- Go
- Java
- CLI
curl -X PATCH "https://<workspace-url>/api/2.0/identity/users/<user-id>?update_mask=full_name.family_name,account_user_status" \
-H "Authorization: Bearer $DATABRICKS_TOKEN" \
-d '{"username": "jane.doe@example.com", "full_name": {"family_name": "Smith"}, "account_user_status": "INACTIVE"}'
w.workspace_iam_v2.update_user_proxy(
name="users/<user-id>",
user=iamv2.User(
username="jane.doe@example.com",
full_name=iamv2.UserFullName(family_name="Smith"),
account_user_status=iamv2.State.INACTIVE,
),
update_mask="full_name.family_name,account_user_status",
)
_, err := w.WorkspaceIamV2.UpdateUserProxy(ctx, iamv2.UpdateUserProxyRequest{
Name: "users/<user-id>",
User: iamv2.User{
Username: "jane.doe@example.com",
FullName: iamv2.UserFullName{FamilyName: "Smith"},
AccountUserStatus: iamv2.StateInactive,
},
UpdateMask: "full_name.family_name,account_user_status",
})
w.workspaceIamV2().updateUserProxy(
new UpdateUserProxyRequest()
.setName("users/<user-id>")
.setUser(
new User()
.setUsername("jane.doe@example.com")
.setFullName(new UserFullName().setFamilyName("Smith"))
.setAccountUserStatus(State.INACTIVE))
.setUpdateMask("full_name.family_name,account_user_status"));
databricks workspace-iam-v2 update-user-proxy users/<user-id> full_name.family_name,account_user_status --json '{
"username": "jane.doe@example.com",
"full_name": {"family_name": "Smith"},
"account_user_status": "INACTIVE"
}'
Only the fields in update_mask change. The following rules also apply:
usernameis immutable, so a SCIMuserNamechange has no IAM v2 equivalent. An update that includesusernamein the mask fails withINVALID_PARAMETER_VALUE.- The account owner and hidden users can't have their status changed.
- You can't deactivate the user you're signed in as.
- The SDKs require
usernamein the request body even when the mask doesn't include it, so the examples include it.
Create a group and add a member
To create a group and add a user to it, run the following. Replace <user-id> with the user's ID from the create-a-user example, and use the group ID returned by the create call.
- curl
- Python
- Go
- Java
- CLI
curl -X POST "https://<workspace-url>/api/2.0/identity/groups" \
-H "Authorization: Bearer $DATABRICKS_TOKEN" \
-d '{"group_name": "data-engineers"}'
curl -X POST "https://<workspace-url>/api/2.0/identity/groups/<group-id>/direct-members" \
-H "Authorization: Bearer $DATABRICKS_TOKEN" \
-d '{"principal_id": <user-id>}'
group = w.workspace_iam_v2.create_group_proxy(group=iamv2.Group(group_name="data-engineers"))
w.workspace_iam_v2.create_direct_group_member_proxy(
parent=group.name,
direct_group_member=iamv2.DirectGroupMember(principal_id=int(user.user_id)),
)
group, err := w.WorkspaceIamV2.CreateGroupProxy(ctx, iamv2.CreateGroupProxyRequest{
Group: iamv2.Group{GroupName: "data-engineers"},
})
_, err = w.WorkspaceIamV2.CreateDirectGroupMemberProxy(ctx, iamv2.CreateDirectGroupMemberProxyRequest{
Parent: group.Name,
DirectGroupMember: iamv2.DirectGroupMember{PrincipalId: userId},
})
Group group = w.workspaceIamV2().createGroupProxy(
new CreateGroupProxyRequest().setGroup(new Group().setGroupName("data-engineers")));
w.workspaceIamV2().createDirectGroupMemberProxy(
new CreateDirectGroupMemberProxyRequest()
.setParent(group.getName())
.setDirectGroupMember(new DirectGroupMember().setPrincipalId(userId)));
databricks workspace-iam-v2 create-group-proxy --group-name data-engineers
databricks workspace-iam-v2 create-direct-group-member-proxy groups/<group-id> <user-id>
To remove the member, delete groups/<group-id>/direct-members/<user-id>.
Grant entitlements in a workspace
To give a group workspace access and SQL access in this workspace, run the following:
- curl
- Python
- Go
- Java
- CLI
curl -X POST "https://<workspace-url>/api/2.0/identity/workspace-assignments" \
-H "Authorization: Bearer $DATABRICKS_TOKEN" \
-d '{"principal_id": <group-id>, "entitlements": ["WORKSPACE_ACCESS", "DATABRICKS_SQL_ACCESS"]}'
w.workspace_iam_v2.create_workspace_assignment_proxy(
workspace_assignment=iamv2.WorkspaceAssignment(
principal_id=int(group.group_id),
entitlements=[iamv2.Entitlement.WORKSPACE_ACCESS, iamv2.Entitlement.DATABRICKS_SQL_ACCESS],
)
)
_, err := w.WorkspaceIamV2.CreateWorkspaceAssignmentProxy(ctx, iamv2.CreateWorkspaceAssignmentProxyRequest{
WorkspaceAssignment: iamv2.WorkspaceAssignment{
PrincipalId: groupId,
Entitlements: []iamv2.Entitlement{iamv2.EntitlementWorkspaceAccess, iamv2.EntitlementDatabricksSqlAccess},
},
})
w.workspaceIamV2().createWorkspaceAssignmentProxy(
new CreateWorkspaceAssignmentProxyRequest()
.setWorkspaceAssignment(
new WorkspaceAssignment()
.setPrincipalId(groupId)
.setEntitlements(
List.of(Entitlement.WORKSPACE_ACCESS, Entitlement.DATABRICKS_SQL_ACCESS))));
databricks workspace-iam-v2 create-workspace-assignment-proxy --json '{
"principal_id": <group-id>,
"entitlements": ["WORKSPACE_ACCESS", "DATABRICKS_SQL_ACCESS"]
}'
An assignment needs at least one entitlement. Use these calls to manage an existing assignment:
- To change its entitlements, update
workspace-assignments/<principal-id>withupdate_mask=entitlementsand the full list of entitlements to keep. - To remove the principal from the workspace, delete the assignment.
- To read every entitlement the principal holds, including through groups, get the assignment and read
effective_entitlements.
Deactivate a principal in one workspace
To deactivate a user or service principal in one workspace without changing its account status, update principals/<principal-id>/workspace-identity-detail with update_mask=workspace_identity_status and set workspace_identity_status to INACTIVE. You can't activate or deactivate the user you're signed in as, and group principals aren't supported. See the Workspace Identity Detail API.
Identities from your identity provider
When automatic identity management is enabled, users, groups, and service principals from your identity provider sync to Databricks when they're first used. You can't create them with external_id set. Instead, get the external principal by its ID in your identity provider. The call syncs the principal's details and group memberships and returns its Databricks ID in internal_id:
Principal | Workspace-level call |
|---|---|
User |
|
Group |
|
Service principal |
|
Service principals sync only from Microsoft Entra ID.
To filter on the external ID of a principal that already exists, use external_id eq "<external-id>". See Automatic identity management.
For a principal that syncs from your identity provider, you can update only external_id. Its other fields come from your identity provider. You can't add or remove direct members of a group that syncs from your identity provider.
What IAM v2 doesn't cover
Keep using the SCIM APIs for the following:
- Workspace-local groups. Migrate them to account groups first: Migrate workspace-local groups to account groups.
- Roles and instance profiles on users, groups, and service principals.
- The account admin role.
Additional resources
-
- Migrate Terraform identity resources to IAM v2
- Move Terraform configurations from the SCIM-based identity resources to the IAM v2 resources.
-
- Manage Databricks users
- Add, update, and remove users in Databricks as an account or workspace admin.