-
Notifications
You must be signed in to change notification settings - Fork 384
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
Added databricks_mws_permission_assignment
resource
#1491
Changes from all commits
503fb85
7696fe7
68f3690
9298ffd
a62a4fd
0a28533
06cae22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package access | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strconv" | ||
|
||
"github.com/databricks/terraform-provider-databricks/common" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func NewPermissionAssignmentAPI(ctx context.Context, m any) PermissionAssignmentAPI { | ||
return PermissionAssignmentAPI{m.(*common.DatabricksClient), ctx} | ||
} | ||
|
||
type PermissionAssignmentAPI struct { | ||
client *common.DatabricksClient | ||
context context.Context | ||
} | ||
|
||
type Permissions struct { | ||
Permissions []string `json:"permissions"` | ||
} | ||
|
||
func (a PermissionAssignmentAPI) CreateOrUpdate(principalId int64, r Permissions) error { | ||
path := fmt.Sprintf("/preview/permissionassignments/principals/%d", principalId) | ||
return a.client.Put(a.context, path, r) | ||
} | ||
|
||
func (a PermissionAssignmentAPI) Remove(principalId string) error { | ||
path := fmt.Sprintf("/preview/permissionassignments/principals/%s", principalId) | ||
return a.client.Delete(a.context, path, nil) | ||
} | ||
|
||
type Principal struct { | ||
DisplayName string `json:"display_name"` | ||
PrincipalID int64 `json:"principal_id"` | ||
ServicePrincipalName string `json:"service_principal_name,omitempty"` | ||
UserName string `json:"user_name,omitempty"` | ||
GroupName string `json:"group_name,omitempty"` | ||
} | ||
|
||
type PermissionAssignment struct { | ||
Permissions []string `json:"permissions"` | ||
Principal Principal | ||
} | ||
|
||
type PermissionAssignmentList struct { | ||
PermissionAssignments []PermissionAssignment `json:"permission_assignments"` | ||
} | ||
|
||
func (l PermissionAssignmentList) ForPrincipal(principalId int64) (res Permissions, err error) { | ||
for _, v := range l.PermissionAssignments { | ||
if v.Principal.PrincipalID != principalId { | ||
continue | ||
} | ||
return Permissions{v.Permissions}, nil | ||
} | ||
return res, common.NotFound(fmt.Sprintf("%d not found", principalId)) | ||
} | ||
|
||
func (a PermissionAssignmentAPI) List() (list PermissionAssignmentList, err error) { | ||
err = a.client.Get(a.context, "/preview/permissionassignments", nil, &list) | ||
return | ||
} | ||
|
||
func mustInt64(s string) int64 { | ||
n, err := strconv.ParseInt(s, 10, 0) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return n | ||
} | ||
|
||
// ResourcePermissionAssignment performs of users to a workspace | ||
// from a workspace context, though it requires additional set | ||
// data resource for "workspace account scim", whicl will be added later. | ||
func ResourcePermissionAssignment() *schema.Resource { | ||
type entity struct { | ||
PrincipalId int64 `json:"principal_id"` | ||
Permissions []string `json:"permissions" tf:"slice_as_set"` | ||
} | ||
s := common.StructToSchema(entity{}, | ||
func(m map[string]*schema.Schema) map[string]*schema.Schema { | ||
return m | ||
}) | ||
return common.Resource{ | ||
Schema: s, | ||
Create: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error { | ||
var assignment entity | ||
common.DataToStructPointer(d, s, &assignment) | ||
api := NewPermissionAssignmentAPI(ctx, c) | ||
err := api.CreateOrUpdate(assignment.PrincipalId, Permissions{assignment.Permissions}) | ||
if err != nil { | ||
return err | ||
} | ||
d.SetId(fmt.Sprintf("%d", assignment.PrincipalId)) | ||
return nil | ||
}, | ||
Read: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error { | ||
list, err := NewPermissionAssignmentAPI(ctx, c).List() | ||
if err != nil { | ||
return err | ||
} | ||
permissions, err := list.ForPrincipal(mustInt64(d.Id())) | ||
if err != nil { | ||
return err | ||
} | ||
return common.StructToData(permissions, s, d) | ||
}, | ||
Delete: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error { | ||
return NewPermissionAssignmentAPI(ctx, c).Remove(d.Id()) | ||
}, | ||
}.ToResource() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package access | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/databricks/terraform-provider-databricks/qa" | ||
) | ||
|
||
func TestPermissionAssignmentCreate(t *testing.T) { | ||
qa.ResourceFixture{ | ||
Fixtures: []qa.HTTPFixture{ | ||
{ | ||
Method: "PUT", | ||
Resource: "/api/2.0/preview/permissionassignments/principals/345", | ||
ExpectedRequest: Permissions{ | ||
Permissions: []string{"USER"}, | ||
}, | ||
}, | ||
{ | ||
Method: "GET", | ||
Resource: "/api/2.0/preview/permissionassignments", | ||
Response: PermissionAssignmentList{ | ||
PermissionAssignments: []PermissionAssignment{ | ||
{ | ||
Permissions: []string{"USER"}, | ||
Principal: Principal{ | ||
PrincipalID: 345, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
Resource: ResourcePermissionAssignment(), | ||
Create: true, | ||
AccountID: "abc", | ||
HCL: ` | ||
principal_id = 345 | ||
permissions = ["USER"] | ||
`, | ||
}.ApplyNoError(t) | ||
} | ||
|
||
func TestPermissionAssignmentReadNotFound(t *testing.T) { | ||
qa.ResourceFixture{ | ||
Fixtures: []qa.HTTPFixture{ | ||
{ | ||
Method: "GET", | ||
Resource: "/api/2.0/preview/permissionassignments", | ||
Response: PermissionAssignmentList{ | ||
PermissionAssignments: []PermissionAssignment{ | ||
{ | ||
Permissions: []string{"USER"}, | ||
Principal: Principal{ | ||
PrincipalID: 345, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
Resource: ResourcePermissionAssignment(), | ||
Read: true, | ||
Removed: true, | ||
AccountID: "abc", | ||
ID: "123", | ||
}.ApplyNoError(t) | ||
} | ||
|
||
func TestPermissionAssignmentFuzz(t *testing.T) { | ||
qa.ResourceCornerCases(t, ResourcePermissionAssignment()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
--- | ||
subcategory: "Unity Catalog" | ||
--- | ||
# databricks_mws_permission_assignment Resource | ||
|
||
These resources are invoked in the account context. Provider must have `account_id` attribute configured. | ||
|
||
## Example Usage | ||
|
||
In account context, adding account-level group to a workspace: | ||
|
||
Comment on lines
+10
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see that we only have docs for updating role assignment in the account context. I think thats fine and sufficient but it will not work for workspace admin personas who are not account admins. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, we'll add that "workspace level account scim user listing" in one of the following releases. |
||
```hcl | ||
provider "databricks" { | ||
// <other properties> | ||
account_id = "<databricks account id>" | ||
} | ||
|
||
resource "databricks_group" "data_eng" { | ||
display_name = "Data Engineering" | ||
} | ||
|
||
resource "databricks_mws_permission_assignment" "add_admin_group" { | ||
workspace_id = databricks_mws_workspaces.this.workspace_id | ||
principal_id = databricks_group.data_eng.id | ||
permissions = ["ADMIN"] | ||
} | ||
``` | ||
|
||
In account context, adding account-level user to a workspace: | ||
|
||
```hcl | ||
provider "databricks" { | ||
// <other properties> | ||
account_id = "<databricks account id>" | ||
} | ||
|
||
resource "databricks_user" "me" { | ||
user_name = "me@example.com" | ||
} | ||
|
||
resource "databricks_mws_permission_assignment" "add_user" { | ||
workspace_id = databricks_mws_workspaces.this.workspace_id | ||
principal_id = databricks_user.me.id | ||
permissions = ["USER"] | ||
} | ||
``` | ||
|
||
In account context, adding account-level service principal to a workspace: | ||
|
||
```hcl | ||
provider "databricks" { | ||
// <other properties> | ||
account_id = "<databricks account id>" | ||
} | ||
|
||
resource "databricks_service_principal" "sp" { | ||
display_name = "Automation-only SP" | ||
} | ||
|
||
resource "databricks_mws_permission_assignment" "add_admin_spn" { | ||
workspace_id = databricks_mws_workspaces.this.workspace_id | ||
principal_id = databricks_service_principal.sp.id | ||
permissions = ["ADMIN"] | ||
} | ||
``` | ||
nfx marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package acceptance | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/databricks/terraform-provider-databricks/internal/acceptance" | ||
"github.com/databricks/terraform-provider-databricks/qa" | ||
) | ||
|
||
func TestAccAssignGroupToWorkspace(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, what does this test do? Does it test that the template can be accepted? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it does create, read, update, and delete test assignments of test principals |
||
qa.RequireCloudEnv(t, "unity-catalog-account") | ||
acceptance.Test(t, []acceptance.Step{ | ||
{ | ||
Template: ` | ||
resource "databricks_group" "this" { | ||
display_name = "TF {var.RANDOM}" | ||
} | ||
resource "databricks_mws_permission_assignment" "this" { | ||
workspace_id = {env.TEST_UC_WORKSPACE_ID} | ||
principal_id = databricks_group.this.id | ||
permissions = ["USER"] | ||
}`, | ||
}, | ||
{ | ||
Template: ` | ||
resource "databricks_group" "this" { | ||
display_name = "TF {var.RANDOM}" | ||
} | ||
resource "databricks_mws_permission_assignment" "this" { | ||
workspace_id = {env.TEST_UC_WORKSPACE_ID} | ||
principal_id = databricks_group.this.id | ||
permissions = ["ADMIN"] | ||
}`, | ||
}, | ||
{ | ||
Template: ` | ||
resource "databricks_group" "this" { | ||
display_name = "TF {var.RANDOM}" | ||
} | ||
resource "databricks_mws_permission_assignment" "this" { | ||
workspace_id = {env.TEST_UC_WORKSPACE_ID} | ||
principal_id = databricks_group.this.id | ||
permissions = ["USER"] | ||
}`, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccAssignSpnToWorkspace(t *testing.T) { | ||
qa.RequireCloudEnv(t, "unity-catalog-account") | ||
acceptance.Test(t, []acceptance.Step{ | ||
{ | ||
Template: ` | ||
resource "databricks_service_principal" "this" { | ||
display_name = "TF {var.RANDOM}" | ||
} | ||
resource "databricks_mws_permission_assignment" "this" { | ||
workspace_id = {env.TEST_UC_WORKSPACE_ID} | ||
principal_id = databricks_service_principal.this.id | ||
permissions = ["USER"] | ||
}`, | ||
}, | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 will there be a documentation for access/permission_assignment? For that one API doesn't need workspace id or account id, they can be derived from the workspace url.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't have a way to retrieve account-level IDs from workspace in the provider yet. please "publish" the api ;)