Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add datasource databricks_users #4028

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
05f70c1
#3468: added initial data_users.go resource
dgomez04 Sep 13, 2024
f2dd1c2
#3468: made changes to data_users.go to explicitly define schema, and…
dgomez04 Sep 16, 2024
2d594a9
#3468: added 'user_name_contains' attribute to data_users.go to allow…
dgomez04 Sep 17, 2024
0d6f280
#3468: added acceptance tests for data_users
dgomez04 Sep 17, 2024
b6380f8
#3468: added documentation for databricks_users data source.
dgomez04 Sep 17, 2024
1c703b4
renamed resource func to DataSourceUsers, removed various acceptance …
dgomez04 Sep 20, 2024
dbd79d8
added correct reference to data resource 'databricks_users' on the ac…
dgomez04 Sep 20, 2024
4ed08d4
name format changes to acceptance tests
dgomez04 Sep 20, 2024
072123a
modified acceptance test
dgomez04 Oct 2, 2024
bffcf54
fixed integration test to point to correct import
dgomez04 Oct 2, 2024
7fa5823
#3468: started migrating to the plugin framework, adding support for …
dgomez04 Oct 23, 2024
1c58eaa
migrated data_users to plugin framework and added support for both pr…
dgomez04 Oct 24, 2024
c2d70b9
deleted sdkv2 'data_users' and removed it from sdkv2.go
dgomez04 Oct 24, 2024
4ab1e27
migrated test
dgomez04 Oct 24, 2024
8db0188
added acceptance tests and added the data source to pluginfw.go
dgomez04 Oct 24, 2024
2e46921
modified docs to reflect changes
dgomez04 Oct 24, 2024
ed587ee
Merge branch 'main' into feature/3468-databricks_users
dgomez04 Oct 24, 2024
6e4b8ed
added correct attributes to the docs
dgomez04 Oct 24, 2024
5604c68
added correct attributes to the docs
dgomez04 Oct 24, 2024
f285db5
Merge remote-tracking branch 'origin/feature/3468-databricks_users' i…
dgomez04 Oct 24, 2024
a628819
Merge branch 'main' into feature/3468-databricks_users
dgomez04 Oct 31, 2024
d5ac8b4
Fix `fmt`on pluginfw.go
dgomez04 Oct 31, 2024
32e79f6
merged upstream
dgomez04 Oct 31, 2024
6f6d19e
merged with upstream
dgomez04 Nov 1, 2024
88608c1
removed unnecesary tests, switched to use a filter field to leverage …
dgomez04 Nov 1, 2024
26c69fb
added resource to rollout utils
dgomez04 Nov 1, 2024
9e54137
updated docs to reflect changes
dgomez04 Nov 1, 2024
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
80 changes: 80 additions & 0 deletions docs/data-sources/users.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
subcategory: "Security"
---

# databricks_users Data Source

-> If you have a fully automated setup with workspaces created by [databricks_mws_workspaces](../resources/mws_workspaces.md) or [azurerm_databricks_workspace](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/databricks_workspace), please make sure to add [depends_on attribute](../guides/troubleshooting.md#data-resources-and-authentication-is-not-configured-errors) in order to prevent _default auth: cannot configure default credentials_ errors.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a note that it works with both workspace & account-level providers?


Retrieves information about multiple [databricks_user](../resources/user.md) resources.

## Example Usage

Adding a subset of users to a group

```hcl
data "databricks_users" "company_users" {
filter = "userName co \"@domain.org\""
}
resource "databricks_group" "data_users_group" {
display_name = "Data Users"
}
resource "databricks_group_member" "add_users_to_group" {
for_each = { for user in data.databricks_users.company_users.users : user.id => user }
group_id = databricks_group.data_users_group.id
member_id = each.value.id
}
```

## Argument Reference

This data source allows you to filter the list of users using the following optional arguments:

- `filter` - (Optional) Query by which the results have to be filtered. If not specified, all users will be returned. Supported operators are equals (`eq`), contains (`co`), starts with (`sw`), and not equals (`ne`). Additionally, simple expressions can be formed using logical operators `and` and `or`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should add a note (or pointer to the doc) that will clarify if the search is case-sensitive or not?


**Examples:**
- User whose `displayName` equals "john":
```hcl
filter = "displayName eq \"john\""
```
- User whose `displayName` contains "john" or `userName` contains "@domain.org":
```hcl
filter = "displayName co \"john\" or userName co \"@domain.org\""
```

## Attribute Reference

This data source exposes the following attributes:

- `users` - A list of users matching the specified criteria. Each user has the following attributes:
- `id` - The ID of the user.
- `userName` - The username of the user.
- `emails` - All the emails associated with the Databricks user.
- `name`
- `givenName` - Given name of the Databricks user.
- `familyName` - Family name of the Databricks user.
- `displayName` - The display name of the user.
- `roles` - Indicates if the user has the admin role.
Copy link
Contributor

Choose a reason for hiding this comment

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

it's not only for admin roles

- `$ref`
- `value`
- `display`
- `primary`
- `type`
- `externalId` - reserved for future use.
- `active` - Boolean that represents if this user is active.
Copy link
Contributor

Choose a reason for hiding this comment

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

We have more information in the iam.User struct - groups, entitlements.


## Related Resources

The following resources are used in the same context:

- [**databricks_user**](../resources/user.md): Resource to manage individual users in Databricks.

- [**databricks_group**](../resources/group.md): Resource to manage groups in Databricks.

- [**databricks_group_member**](../resources/group_member.md): Resource to manage group memberships by adding users to groups.

- [**databricks_permissions**](../resources/permissions.md): Resource to manage access control in the Databricks workspace.

- [**databricks_current_user**](current_user.md): Data source to retrieve information about the user or service principal that is calling the Databricks REST API.
2 changes: 2 additions & 0 deletions internal/providers/pluginfw/pluginfw_rollout_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/qualitymonitor"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/registered_model"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/sharing"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/user"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/volume"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/resource"
Expand All @@ -45,6 +46,7 @@ var pluginFwOnlyResources = []func() resource.Resource{
var pluginFwOnlyDataSources = []func() datasource.DataSource{
registered_model.DataSourceRegisteredModel,
notificationdestinations.DataSourceNotificationDestinations,
user.DataSourceUsers,
catalog.DataSourceFunctions,
// TODO: Add DataSourceCluster into migratedDataSources after fixing unit tests.
cluster.DataSourceCluster, // Using the staging name (with pluginframework suffix)
Expand Down
97 changes: 97 additions & 0 deletions internal/providers/pluginfw/resources/user/data_users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package user

import (
"context"

"github.com/databricks/databricks-sdk-go/service/iam"
"github.com/databricks/terraform-provider-databricks/common"
pluginfwcommon "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/common"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/converters"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/tfschema"
"github.com/databricks/terraform-provider-databricks/internal/service/iam_tf"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

const dataSourceName = "users"

func DataSourceUsers() datasource.DataSource {
return &UsersDataSource{}
}

var _ datasource.DataSourceWithConfigure = &UsersDataSource{}

type UsersDataSource struct {
Client *common.DatabricksClient
}

type UsersInfo struct {
Filter types.String `json:"filter,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

What I would recommend as well - by default fetch only limited number of attributes, i.e., don't include roles - this could be done to decrease the load on the SCIM backend as fetching roles is quite heavyweight operation.

We can add an additional parameter, like, extra_attributes that will receive the list of additional attributes (comma-separated).

Users []iam_tf.User `json:"users,omitempty" tf:"computed"`
}

func (d *UsersDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = pluginfwcommon.GetDatabricksProductionName(dataSourceName)
}

func (d *UsersDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
attrs, blocks := tfschema.DataSourceStructToSchemaMap(UsersInfo{}, nil)
resp.Schema = schema.Schema{
Attributes: attrs,
Blocks: blocks,
}
}

func (d *UsersDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if d.Client == nil {
d.Client = pluginfwcommon.ConfigureDataSource(req, resp)
}
}

func (d *UsersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var usersInfo UsersInfo

resp.Diagnostics.Append(req.Config.Get(ctx, &usersInfo)...)
if resp.Diagnostics.HasError() {
return
}

var users []iam.User
var err error

if d.Client.Config.IsAccountClient() {
a, diags := d.Client.GetAccountClient()
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

users, err = a.Users.ListAll(ctx, iam.ListAccountUsersRequest{Filter: usersInfo.Filter.ValueString()})
Copy link
Contributor

Choose a reason for hiding this comment

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

explicitly configure attributes, like this: https://github.com/databricks/terraform-provider-databricks/blob/main/exporter/util_scim.go#L289, but without roles and groups (and maybe entitlements) by default.

if err != nil {
resp.Diagnostics.AddError("Error listing account users", err.Error())
}
} else {
w, diags := d.Client.GetWorkspaceClient()
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

users, err = w.Users.ListAll(ctx, iam.ListUsersRequest{Filter: usersInfo.Filter.ValueString()})
if err != nil {
resp.Diagnostics.AddError("Error listing workspace users", err.Error())
}
}

for _, user := range users {
var tfUser iam_tf.User
resp.Diagnostics.Append(converters.GoSdkToTfSdkStruct(ctx, user, &tfUser)...)
if resp.Diagnostics.HasError() {
return
}
usersInfo.Users = append(usersInfo.Users, tfUser)
}

resp.Diagnostics.Append(resp.State.Set(ctx, usersInfo)...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package user_test

import (
"testing"

"github.com/databricks/terraform-provider-databricks/internal/acceptance"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const dataSourceTemplate = `
resource "databricks_user" "user1" {
user_name = "tf-{var.STICKY_RANDOM}[email protected]"
}

resource "databricks_user" "user2" {
user_name = "tf-{var.STICKY_RANDOM}[email protected]"
}

data "databricks_users" "this" {
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
filter = "userName co \"testuser\""
depends_on = [databricks_user.user1, databricks_user.user2]
}
`

func checkUsersDataSourcePopulated(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
ds, ok := s.Modules[0].Resources["data.databricks_users.this"]
require.True(t, ok, "data.databricks_users.this has to be there")

usersCount := ds.Primary.Attributes["users.#"]
require.Equal(t, "2", usersCount, "expected two users")

userIds := []string{
ds.Primary.Attributes["users.0.id"],
ds.Primary.Attributes["users.1.id"],
}

expectedUserIDs := []string{
s.Modules[0].Resources["databricks_user.user1"].Primary.ID,
s.Modules[0].Resources["databricks_user.user2"].Primary.ID,
}

assert.ElementsMatch(t, expectedUserIDs, userIds, "expected user ids to match")

return nil
}
}

func TestAccDataSourceDataUsers(t *testing.T) {
acceptance.AccountLevel(t, acceptance.Step{
Template: dataSourceTemplate,
Check: checkUsersDataSourcePopulated(t),
})
}

func TestWorkspaceDataSourceDataUsers(t *testing.T) {
acceptance.WorkspaceLevel(t, acceptance.Step{
Template: dataSourceTemplate,
Check: checkUsersDataSourcePopulated(t),
})
}
Loading