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 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
34 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
43b66ec
added requested changes
dgomez04 Nov 20, 2024
27f6d32
merged upstream and resolved conflicts
dgomez04 Nov 20, 2024
7a47c2a
Merge remote-tracking branch 'upstream/main' into feature/3468-databr…
dgomez04 Nov 25, 2024
7949b7d
merged latest changes from main branch and fixed a test definition
dgomez04 Nov 25, 2024
03fad81
made requested changes
dgomez04 Nov 27, 2024
a642e74
Merge remote-tracking branch 'upstream/main' into feature/3468-databr…
dgomez04 Nov 27, 2024
99d7d2c
added files into the products folder and fixed tests
dgomez04 Nov 27, 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
73 changes: 73 additions & 0 deletions docs/data-sources/users.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
subcategory: "Security"
---

# databricks_users Data Source

-> **Note** 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.

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" {
user_name_contains = "@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:

- `display_name_contains` - (Optional) A substring to filter users by their display name. Only users whose display names contain this substring will be included in the results.
- `user_name_contains` - (Optional) A substring to filter users by their username. Only users whose usernames contain this substring will be included in the results.

->**Note** You can specify **exactly one** of `display_name_contains` or `user_name_contains`. If neither is specified, all users will be returned.
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved

## 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.
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
- `$ref`
- `value`
- `display`
- `primary`
- `type`
- `externalId` - reserved for future use.
- `active` - Boolean that represents if this user is active.
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved

## 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.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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"
Expand Down Expand Up @@ -62,6 +63,7 @@ func (p *DatabricksProviderPluginFramework) DataSources(ctx context.Context) []f
sharing.DataSourceShare,
sharing.DataSourceShares,
catalog.DataSourceFunctions,
user.DataSourceUsers,
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
113 changes: 113 additions & 0 deletions internal/providers/pluginfw/resources/user/data_users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package user

import (
"context"
"fmt"

"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/tfschema"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
)

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

var _ datasource.DataSourceWithConfigure = &UsersDataSource{}

type UsersDataSource struct {
Client *common.DatabricksClient
}

type UsersInfo struct {
DisplayNameContains string `json:"display_name_contains,omitempty" tf:"computed"`
UserNameContains string `json:"user_name_contains,omitempty" tf:"computed"`
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
Users []iam.User `json:"users,omitempty" tf:"computed"`
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
}

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

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)
}
}

// AppendDiagAndCheckErrors is a helper function that simplifies error handling by combining the appending of diagnostics and the checking of errors in a single step.
// It is particularly useful in data source and resource read operations where you want to append diagnostics and immediately determine if an error has occurred.
func AppendDiagAndCheckErrors(resp *datasource.ReadResponse, diags diag.Diagnostics) bool {
resp.Diagnostics.Append(diags...)
return resp.Diagnostics.HasError()
}

// DiagsFromError helps us create an error diagnostic from an error.
func DiagsFromError(summary string, err error) diag.Diagnostics {
return diag.Diagnostics{
diag.NewErrorDiagnostic(summary, err.Error()),
}
}

func validateFilters(data *UsersInfo) diag.Diagnostics {
if data.DisplayNameContains != "" && data.UserNameContains != "" {
return diag.Diagnostics{diag.NewErrorDiagnostic("Invalid configuration", "Exactly one of display_name_contains or user_name_contains should be specified, not both.")}
}
return nil
}

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

diags := req.Config.Get(ctx, &userInfo)
diags = append(diags, validateFilters(&userInfo)...)
if AppendDiagAndCheckErrors(resp, diags) {
return
}

var filter string

if userInfo.DisplayNameContains != "" {
filter = fmt.Sprintf("displayName co \"%s\"", userInfo.DisplayNameContains)
} else if userInfo.UserNameContains != "" {
filter = fmt.Sprintf("userName co \"%s\"", userInfo.UserNameContains)
}

var users []iam.User
var err error

if d.Client.Config.IsAccountClient() {
a, diags := d.Client.GetAccountClient()
if AppendDiagAndCheckErrors(resp, diags) {
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
return
}
users, err = a.Users.ListAll(ctx, iam.ListAccountUsersRequest{Filter: filter})
if err != nil && AppendDiagAndCheckErrors(resp, DiagsFromError("Error listing account users", err)) {
return
}
} else {
w, diags := d.Client.GetWorkspaceClient()
if AppendDiagAndCheckErrors(resp, diags) {
return
}
users, err = w.Users.ListAll(ctx, iam.ListUsersRequest{Filter: filter})
if err != nil && AppendDiagAndCheckErrors(resp, DiagsFromError("Error listing workspace users", err)) {
return
}
}

userInfo.Users = users
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
resp.Diagnostics.Append(resp.State.Set(ctx, userInfo)...)
}
62 changes: 62 additions & 0 deletions internal/providers/pluginfw/resources/user/data_users_acc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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 = "[email protected]"
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
}

resource "databricks_user" "user2" {
user_name = "[email protected]"
}

data "databricks_users" "this" {
dgomez04 marked this conversation as resolved.
Show resolved Hide resolved
user_name_contains = "testuser"
}
`

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),
})
}
19 changes: 19 additions & 0 deletions internal/providers/pluginfw/resources/user/data_users_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package user

import (
"testing"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/stretchr/testify/assert"
)

func TestValidateFilters(t *testing.T) {
userInfo := UsersInfo{
DisplayNameContains: "filter",
UserNameContains: "another_filter",
}
actualDiagnostics := validateFilters(&userInfo)
expectedDiagnostics := diag.Diagnostics{diag.NewErrorDiagnostic("Invalid configuration", "Exactly one of display_name_contains or user_name_contains should be specified, not both.")}
assert.True(t, actualDiagnostics.HasError())
assert.Equal(t, expectedDiagnostics, actualDiagnostics)
}
Loading