Skip to content

Commit

Permalink
Added MLflow permissions (#1013)
Browse files Browse the repository at this point in the history
## MLflow Experiment usage

Valid [permission levels](https://docs.databricks.com/security/access-control/workspace-acl.html#mlflow-experiment-permissions-1) for [databricks_mlflow_experiment](mlflow_experiment.md) are: `CAN_READ`, `CAN_EDIT`, and `CAN_MANAGE`.

```hcl
data "databricks_current_user" "me" {}

resource "databricks_mlflow_experiment" "this" {
  name              = "${data.databricks_current_user.me.home}/Sample"
  artifact_location = "dbfs:/tmp/my-experiment"
  description       = "My MLflow experiment description"
}

resource "databricks_group" "auto" {
  display_name = "Automation"
}

resource "databricks_group" "eng" {
  display_name = "Engineering"
}

resource "databricks_permissions" "experiment_usage" {
  experiment_id = databricks_mlflow_experiment.this.id

  access_control {
    group_name       = "users"
    permission_level = "CAN_READ"
  }

  access_control {
    group_name       = databricks_group.auto.display_name
    permission_level = "CAN_MANAGE"
  }

  access_control {
    group_name       = databricks_group.eng.display_name
    permission_level = "CAN_EDIT"
  }
}
```

## MLflow Model usage

Valid [permission levels](https://docs.databricks.com/security/access-control/workspace-acl.html#mlflow-model-permissions-1) for [databricks_mlflow_model](mlflow_model.md) are: `CAN_READ`, `CAN_EDIT`, `CAN_MANAGE_STAGING_VERSIONS`, `CAN_MANAGE_PRODUCTION_VERSIONS`, and `CAN_MANAGE`.

```hcl
resource "databricks_mlflow_model" "this" {
  name = "SomePredictions"
}

resource "databricks_group" "auto" {
  display_name = "Automation"
}

resource "databricks_group" "eng" {
  display_name = "Engineering"
}

resource "databricks_permissions" "model_usage" {
  registered_model_id = databricks_mlflow_model.this.registered_model_id

  access_control {
    group_name       = "users"
    permission_level = "CAN_READ"
  }

  access_control {
    group_name       = databricks_group.auto.display_name
    permission_level = "CAN_MANAGE_PRODUCTION_VERSIONS"
  }

  access_control {
    group_name       = databricks_group.eng.display_name
    permission_level = "CAN_MANAGE_STAGING_VERSIONS"
  }
}
```

Fixes #1012
  • Loading branch information
nfx committed Dec 30, 2021
1 parent e86931a commit 6fbe7ff
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 0.4.3

* Added support for `databricks_permissions` for `databricks_mlflow_experiment` and `databricks_mlflow_model` ([#1013](https://github.com/databrickslabs/terraform-provider-databricks/pull/1013)).
* Added `Using XXX auth` explanation to HTTP 403 errors, which should help troubleshooting misconfigured authentication or provider aliasing. Example error message now looks like: *cannot create group: /2.0/preview/scim/v2/Groups is only accessible by admins. Using databricks-cli auth: host=https://XXX.cloud.databricks.com/, token=`***REDACTED***`, profile=demo.* All sensitive configuration parameters (`token`, `password`, and `azure_client_secret`) are redacted and replaced with `***REDACTED***` ([#821](https://github.com/databrickslabs/terraform-provider-databricks/issues/821)).
* Improved documentation with regards to public subnets in AWS quick start ([#1005](https://github.com/databrickslabs/terraform-provider-databricks/pull/1005)).
* Added `databricks_mount` code genration for [exporter](https://registry.terraform.io/providers/databrickslabs/databricks/latest/docs/guides/experimental-exporter) tooling ([#1006](https://github.com/databrickslabs/terraform-provider-databricks/pull/1006)).
Expand Down
10 changes: 8 additions & 2 deletions docs/resources/mlflow_experiment.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ This resource allows you to create MLflow experiments in Databricks.
## Example Usage

```hcl
resource "databricks_mlflow_experiment" "test" {
name = "/Users/myuserid/my-experiment"
data "databricks_current_user" "me" {}
resource "databricks_mlflow_experiment" "this" {
name = "${data.databricks_current_user.me.home}/Sample"
artifact_location = "dbfs:/tmp/my-experiment"
description = "My MLflow experiment description"
}
Expand All @@ -22,3 +24,7 @@ The following arguments are supported:
* `name` - (Required) Name of MLflow experiment. It must be an absolute path within the Databricks workspace, e.g. `/Users/<some-username>/my-experiment`. For more information about changes to experiment naming conventions, see [mlflow docs](https://docs.databricks.com/applications/mlflow/experiments.html#experiment-migration).
* `artifact_location` - Path to dbfs:/ or s3:// artifact location of the MLflow experiment.
* `description` - The description of the MLflow experiment.

## Access Control

* [databricks_permissions](permissions.md#MLflow-Experiment-usage) can control which groups or individual users can *Read*, *Edit*, or *Manage* individual experiments.
6 changes: 5 additions & 1 deletion docs/resources/mlflow_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ resource "databricks_mlflow_model" "test" {

The following arguments are supported:

* `name` - (Required) Name of MLflow model.
* `name` - (Required) Name of MLflow model. Change of name triggers new resource.
* `description` - The description of the MLflow model.
* `tags` - Tags for the MLflow model.

## Access Control

* [databricks_permissions](permissions.md#MLflow-Model-usage) can control which groups or individual users can *Read*, *Edit*, *Manage Staging Versions*, *Manage Production Versions*, and *Manage* individual models.
78 changes: 78 additions & 0 deletions docs/resources/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,84 @@ resource "databricks_permissions" "repo_usage" {
}
```

## MLflow Experiment usage

Valid [permission levels](https://docs.databricks.com/security/access-control/workspace-acl.html#mlflow-experiment-permissions-1) for [databricks_mlflow_experiment](mlflow_experiment.md) are: `CAN_READ`, `CAN_EDIT`, and `CAN_MANAGE`.

```hcl
data "databricks_current_user" "me" {}
resource "databricks_mlflow_experiment" "this" {
name = "${data.databricks_current_user.me.home}/Sample"
artifact_location = "dbfs:/tmp/my-experiment"
description = "My MLflow experiment description"
}
resource "databricks_group" "auto" {
display_name = "Automation"
}
resource "databricks_group" "eng" {
display_name = "Engineering"
}
resource "databricks_permissions" "experiment_usage" {
experiment_id = databricks_mlflow_experiment.this.id
access_control {
group_name = "users"
permission_level = "CAN_READ"
}
access_control {
group_name = databricks_group.auto.display_name
permission_level = "CAN_MANAGE"
}
access_control {
group_name = databricks_group.eng.display_name
permission_level = "CAN_EDIT"
}
}
```

## MLflow Model usage

Valid [permission levels](https://docs.databricks.com/security/access-control/workspace-acl.html#mlflow-model-permissions-1) for [databricks_mlflow_model](mlflow_model.md) are: `CAN_READ`, `CAN_EDIT`, `CAN_MANAGE_STAGING_VERSIONS`, `CAN_MANAGE_PRODUCTION_VERSIONS`, and `CAN_MANAGE`.

```hcl
resource "databricks_mlflow_model" "this" {
name = "SomePredictions"
}
resource "databricks_group" "auto" {
display_name = "Automation"
}
resource "databricks_group" "eng" {
display_name = "Engineering"
}
resource "databricks_permissions" "model_usage" {
registered_model_id = databricks_mlflow_model.this.registered_model_id
access_control {
group_name = "users"
permission_level = "CAN_READ"
}
access_control {
group_name = databricks_group.auto.display_name
permission_level = "CAN_MANAGE_PRODUCTION_VERSIONS"
}
access_control {
group_name = databricks_group.eng.display_name
permission_level = "CAN_MANAGE_STAGING_VERSIONS"
}
}
```

## Passwords usage

By default on AWS deployments, all admin users can sign in to Databricks using either SSO or their username and password, and all API users can authenticate to the Databricks REST APIs using their username and password. As an admin, you [can limit](https://docs.databricks.com/administration-guide/users-groups/single-sign-on/index.html#optional-configure-password-access-control) admin users’ and API users’ ability to authenticate with their username and password by configuring `CAN_USE` permissions using password access control.
Expand Down
32 changes: 14 additions & 18 deletions mlflow/resource_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

// Tag ...
type Tag struct {
Key string `json:"key" tf:"force_new"`
Value string `json:"value" tf:"force_new"`
Key string `json:"key"`
Value string `json:"value"`
}

// Model defines the response object from the API
Expand All @@ -21,50 +20,49 @@ type Model struct {
UserID string `json:"user_id,omitempty" tf:"computed"`
LatestVersions []string `json:"latest_versions,omitempty" tf:"computed"`
Description string `json:"description,omitempty"`
Tags []Tag `json:"tags,omitempty" tf:"force_new"`
Tags []Tag `json:"tags,omitempty"`
RegisteredModelID string `json:"id,omitempty" tf:"computed,alias:registered_model_id"`
}

// registeredModel defines response from GET API op
type registeredModel struct {
RegisteredModel Model `json:"registered_model"`
RegisteredModelDatabricks Model `json:"registered_model_databricks"`
}

// ModelsAPI ...
type ModelsAPI struct {
client *common.DatabricksClient
context context.Context
}

// NewModelsAPI ...
func NewModelsAPI(ctx context.Context, m interface{}) ModelsAPI {
return ModelsAPI{m.(*common.DatabricksClient), ctx}
}

// Create ...
func (a ModelsAPI) Create(m *Model) error {
return a.client.Post(a.context, "/mlflow/registered-models/create", m, m)
}

// Read ...
func (a ModelsAPI) Read(name string) (*Model, error) {
var m registeredModel
err := a.client.Get(a.context, "/mlflow/registered-models/get", map[string]string{
err := a.client.Get(a.context, "/mlflow/databricks/registered-models/get", map[string]string{
"name": name,
}, &m)
if err != nil {
return nil, err
}
return &m.RegisteredModel, nil
return &m.RegisteredModelDatabricks, nil
}

// Update ...
// Update the model entity
func (a ModelsAPI) Update(m *Model) error {
return a.client.Patch(a.context, "/mlflow/registered-models/update", m)
}

// Delete ...
func (a ModelsAPI) Delete(m *Model) error {
return a.client.Delete(a.context, "/mlflow/registered-models/delete", m)
// Delete removes the model by it's name
func (a ModelsAPI) Delete(name string) error {
return a.client.Delete(a.context, "/mlflow/registered-models/delete", map[string]string{
"name": name,
})
}

func ResourceMLFlowModel() *schema.Resource {
Expand Down Expand Up @@ -98,9 +96,7 @@ func ResourceMLFlowModel() *schema.Resource {
return NewModelsAPI(ctx, c).Update(&m)
},
Delete: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
var m Model
common.DataToStructPointer(d, s, &m)
return NewModelsAPI(ctx, c).Delete(&m)
return NewModelsAPI(ctx, c).Delete(d.Id())
},
Schema: s,
}.ToResource()
Expand Down
16 changes: 8 additions & 8 deletions mlflow/resource_model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ func TestModelCreate(t *testing.T) {
},
{
Method: "GET",
Resource: "/api/2.0/mlflow/registered-models/get?name=xyz",
Resource: "/api/2.0/mlflow/databricks/registered-models/get?name=xyz",
Response: registeredModel{
RegisteredModel: m(),
RegisteredModelDatabricks: m(),
},
},
},
Expand Down Expand Up @@ -89,9 +89,9 @@ func TestModelRead(t *testing.T) {
Fixtures: []qa.HTTPFixture{
{
Method: "GET",
Resource: "/api/2.0/mlflow/registered-models/get?name=xyz",
Resource: "/api/2.0/mlflow/databricks/registered-models/get?name=xyz",
Response: registeredModel{
RegisteredModel: m(),
RegisteredModelDatabricks: m(),
},
},
},
Expand All @@ -109,9 +109,9 @@ func TestModelReadGetError(t *testing.T) {
Fixtures: []qa.HTTPFixture{
{
Method: "GET",
Resource: "/api/2.0/mlflow/registered-models/get?name=xyz",
Resource: "/api/2.0/mlflow/databricks/registered-models/get?name=xyz",
Response: registeredModel{
RegisteredModel: m(),
RegisteredModelDatabricks: m(),
},
Status: 400,
},
Expand All @@ -138,9 +138,9 @@ func TestModelUpdate(t *testing.T) {
},
{
Method: "GET",
Resource: "/api/2.0/mlflow/registered-models/get?name=xyz",
Resource: "/api/2.0/mlflow/databricks/registered-models/get?name=xyz",
Response: registeredModel{
RegisteredModel: gm,
RegisteredModelDatabricks: gm,
},
},
},
Expand Down
3 changes: 3 additions & 0 deletions permissions/resource_permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ func permissionsResourceIDFields() []permissionsIDFieldMapping {
{"sql_dashboard_id", "dashboard", "sql/dashboards", []string{"CAN_EDIT", "CAN_RUN", "CAN_MANAGE"}, SIMPLE},
{"sql_alert_id", "alert", "sql/alerts", []string{"CAN_EDIT", "CAN_RUN", "CAN_MANAGE"}, SIMPLE},
{"sql_query_id", "query", "sql/queries", []string{"CAN_EDIT", "CAN_RUN", "CAN_MANAGE"}, SIMPLE},
{"experiment_id", "mlflowExperiment", "experiments", []string{"CAN_READ", "CAN_EDIT", "CAN_MANAGE"}, SIMPLE},
{"registered_model_id", "registered-model", "registered-models", []string{
"CAN_READ", "CAN_EDIT", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE"}, SIMPLE},
}
}

Expand Down

0 comments on commit 6fbe7ff

Please sign in to comment.