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

feat: Grant ownership follow up tasks [FOR LATER] #2670

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
248 changes: 248 additions & 0 deletions docs/resources/grant_ownership.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,204 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "snowflake_grant_ownership Resource - terraform-provider-snowflake"
subcategory: ""
description: |-

---


!> **Warning** We're in a process of implementing this resource, so it's not available yet.

~> **Note** This is a preview resource. It's ready for general use. In case of any errors, please file an issue in our GitHub repository.
~> **Note** For more details about granting ownership, please visit [`GRANT OWNERSHIP` Snowflake documentation page](https://docs.snowflake.com/en/sql-reference/sql/grant-ownership).




# snowflake_grant_ownership (Resource)



## Example Usage

```terraform
##################################
### on object to account role
##################################

resource "snowflake_role" "test" {
name = "test_role"
}

resource "snowflake_database" "test" {
name = "test_database"
}

resource "snowflake_schema" "test" {
name = "test_schema"
database = snowflake_database.test.name
}

resource "snowflake_grant_ownership" "test" {
account_role_name = snowflake_role.test.name
outbound_privileges = "COPY"
on {
object_type = "SCHEMA"
object_name = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\""
}
}

##################################
### on object to database role
##################################

resource "snowflake_database" "test" {
name = "test_database"
}

resource "snowflake_schema" "test" {
name = "test_schema"
database = snowflake_database.test.name
}

resource "snowflake_database_role" "test" {
name = "test_database_role"
database = snowflake_database.test.name
}

resource "snowflake_grant_ownership" "test" {
database_role_name = "\"${snowflake_database_role.test.database}\".\"${snowflake_database_role.test.name}\""
outbound_privileges = "REVOKE"
on {
object_type = "SCHEMA"
object_name = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\""
}
}

##################################
### on all tables in database to account role
##################################

resource "snowflake_role" "test" {
name = "test_role"
}

resource "snowflake_database" "test" {
name = "test_database"
}

resource "snowflake_grant_ownership" "test" {
account_role_name = snowflake_role.test.name
on {
all {
plural_object_type = "TABLES"
in_database = snowflake_database.test.name
}
}
}

##################################
### on all tables in schema to account role
##################################

resource "snowflake_role" "test" {
name = "test_role"
}

resource "snowflake_database" "test" {
name = "test_database"
}

resource "snowflake_schema" "test" {
name = "test_schema"
database = snowflake_database.test.name
}

resource "snowflake_grant_ownership" "test" {
account_role_name = snowflake_role.test.name
on {
all {
plural_object_type = "TABLES"
in_schema = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\""
}
}
}

##################################
### on future tables in database to account role
##################################

resource "snowflake_role" "test" {
name = "test_role"
}

resource "snowflake_database" "test" {
name = "test_database"
}

resource "snowflake_grant_ownership" "test" {
account_role_name = snowflake_role.test.name
on {
future {
plural_object_type = "TABLES"
in_database = snowflake_database.test.name
}
}
}

##################################
### on future tables in schema to account role
##################################

resource "snowflake_role" "test" {
name = "test_role"
}

resource "snowflake_database" "test" {
name = "test_database"
}

resource "snowflake_schema" "test" {
name = "test_schema"
database = snowflake_database.test.name
}

resource "snowflake_grant_ownership" "test" {
account_role_name = snowflake_role.test.name
on {
future {
plural_object_type = "TABLES"
in_schema = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\""
}
}
}
```

## Granting ownership on pipes
To transfer ownership of a pipe, there must be additional conditions met. Otherwise, additional manual work
will be needed afterward or in some cases, the ownership won't be transferred (resulting in error).

To transfer ownership of a pipe(s) **fully automatically**, one of the following conditions has to be met:
- OPERATE and MONITOR privileges are granted to the current role on the pipe(s) and `outbound_privileges` field is set to `COPY`.
- The pipe(s) running status is paused (additional privileges and fields set are needed to pause and resume the pipe before and after ownership transfer. If it's already paused, nothing additional is needed and the pipe will remain paused after the ownership transfer).

To transfer ownership of a pipe(s) **semi-automatically** you have to:
1. Pause the pipe(s) you want to transfer ownership of (using [ALTER PIPE](https://docs.snowflake.com/en/sql-reference/sql/alter-pipe#syntax); see PIPE_EXECUTION_PAUSED).
2. Create Terraform configuration with the `snowflake_grant_ownership` resource and perform ownership transfer with the `terraform apply`.
3. To resume the pipe(s) after ownership transfer use [PIPE_FORCE_RESUME system function](https://docs.snowflake.com/en/sql-reference/functions/system_pipe_force_resume).

## Granting ownership on task
Granting ownership on single task requires:
- Either OWNERSHIP or OPERATE privilege to suspend the task (and its root)
- Role that will be granted ownership has to have USAGE granted on the warehouse assigned to the task, as well as EXECUTE TASK granted globally
- The outbound privileges set to `outbound_privileges = "COPY"` if you want to move grants automatically to the owner (also enables the provider to resume the task automatically)
If originally the first owner won't be granted with OPERATE, USAGE (on the warehouse), EXECUTE TASK (on the account), and outbound privileges won't be set to `COPY`, then you have to resume suspended tasks manually.

## Granting ownership on all tasks in database/schema
Granting ownership on all tasks requires less privileges than granting ownership on one task, because it does a little bit less and requires additional work to be done after.
The only thing you have to take care of is to resume tasks after grant ownership transfer. If your tasks are managed by the Snowflake Terraform Plugin, this should
be as simple as running `terraform apply` second time (assuming the currently used role is privileged enough to be able to resume the tasks).
If your tasks are not managed by the Snowflake Terraform Plugin, you should resume them yourself manually.

<!-- schema generated by tfplugindocs -->
## Schema
Expand Down Expand Up @@ -62,3 +251,62 @@ Optional:

- `in_database` (String) The fully qualified name of the database.
- `in_schema` (String) The fully qualified name of the schema.

## Import

~> **Note** All the ..._name parts should be fully qualified names (where every part is quoted), e.g. for schema object it is `"<database_name>"."<schema_name>"."<object_name>"`

Import is supported using the following syntax:

`terraform import "<role_type>|<role_identifier>|<outbound_privileges_behavior>|<grant_type>|<grant_data>"`

where:
- role_type - string - type of granted role (either ToAccountRole or ToDatabaseRole)
- role_name - string - fully qualified identifier for either account role or database role (depending on the role_type)
- outbound_privileges_behavior - string - behavior specified for existing roles (can be either COPY or REVOKE)
- grant_type - enum
- grant_data - data dependent on grant_type

It has varying number of parts, depending on grant_type. All the possible types are:

### OnObject
`terraform import "<role_type>|<role_identifier>|<outbound_privileges_behavior>|OnObject|<object_type>|<object_name>"`

### OnAll (contains inner types: InDatabase | InSchema)

#### InDatabase
`terraform import "<role_type>|<role_identifier>|<outbound_privileges_behavior>|OnAll|<plural_object_type>|InDatabase|<database_name>"`

#### InSchema
`terraform import "<role_type>|<role_identifier>|<outbound_privileges_behavior>|OnAll|<plural_object_type>|InSchema|<schema_name>"`

### OnFuture (contains inner types: InDatabase | InSchema)

#### InDatabase
`terraform import "<role_type>|<role_identifier>|<outbound_privileges_behavior>|OnFuture|<plural_object_type>|InDatabase|<database_name>"`

#### InSchema
`terraform import "<role_type>|<role_identifier>|<outbound_privileges_behavior>|OnFuture|<plural_object_type>|InSchema|<schema_name>"`

### Import examples

#### OnObject on Schema ToAccountRole
`terraform import "ToAccountRole|\"account_role\"|COPY|OnObject|SCHEMA|\"database_name\".\"schema_name\""`

#### OnObject on Schema ToDatabaseRole
`terraform import "ToDatabaseRole|\"database_name\".\"database_role_name\"|COPY|OnObject|SCHEMA|\"database_name\".\"schema_name\""`

#### OnObject on Table
`terraform import "ToAccountRole|\"account_role\"|COPY|OnObject|TABLE|\"database_name\".\"schema_name\".\"table_name\""`

#### OnAll InDatabase
`terraform import "ToAccountRole|\"account_role\"|REVOKE|OnAll|TABLES|InDatabase|\"database_name\""`

#### OnAll InSchema
`terraform import "ToAccountRole|\"account_role\"||OnAll|TABLES|InSchema|\"database_name\".\"schema_name\""`

#### OnFuture InDatabase
`terraform import "ToAccountRole|\"account_role\"||OnFuture|TABLES|InDatabase|\"database_name\""`

#### OnFuture InSchema
`terraform import "ToAccountRole|\"account_role\"|COPY|OnFuture|TABLES|InSchema|\"database_name\".\"schema_name\""`
89 changes: 89 additions & 0 deletions pkg/resources/grant_ownership_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,95 @@ func TestAcc_GrantOwnership_OnAllPipes(t *testing.T) {
})
}

func TestAcc_GrantOwnership_OnTask(t *testing.T) {
accountRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
taskName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
accountRoleFullyQualifiedName := sdk.NewAccountObjectIdentifier(accountRoleName).FullyQualifiedName()
taskFullyQualifiedName := sdk.NewSchemaObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName, taskName).FullyQualifiedName()

configVariables := config.Variables{
"account_role_name": config.StringVariable(accountRoleName),
"database": config.StringVariable(acc.TestDatabaseName),
"schema": config.StringVariable(acc.TestSchemaName),
"task": config.StringVariable(taskName),
}
resourceName := "snowflake_grant_ownership.test"

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.RequireAbove(tfversion.Version1_5_0),
},
Steps: []resource.TestStep{
{
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantOwnership/OnTask"),
ConfigVariables: configVariables,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "account_role_name", accountRoleName),
resource.TestCheckResourceAttr(resourceName, "on.0.object_type", sdk.ObjectTypeTask.String()),
resource.TestCheckResourceAttr(resourceName, "on.0.object_name", taskFullyQualifiedName),
resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("ToAccountRole|%s||OnObject|TASK|%s", accountRoleFullyQualifiedName, taskFullyQualifiedName)),
checkResourceOwnershipIsGranted(&sdk.ShowGrantOptions{
On: &sdk.ShowGrantsOn{
Object: &sdk.Object{
ObjectType: sdk.ObjectTypeTask,
Name: sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(taskFullyQualifiedName),
},
},
// TODO(SNOW-999049): Fix this identifier
}, sdk.ObjectTypeTask, accountRoleName, fmt.Sprintf("%s\".\"%s\".%s", acc.TestDatabaseName, acc.TestSchemaName, taskName)),
),
},
},
})
}

func TestAcc_GrantOwnership_OnAllTasks(t *testing.T) {
accountRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
taskName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
secondTaskName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
accountRoleFullyQualifiedName := sdk.NewAccountObjectIdentifier(accountRoleName).FullyQualifiedName()
schemaFullyQualifiedName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName).FullyQualifiedName()

configVariables := config.Variables{
"account_role_name": config.StringVariable(accountRoleName),
"database": config.StringVariable(acc.TestDatabaseName),
"schema": config.StringVariable(acc.TestSchemaName),
"task": config.StringVariable(taskName),
"second_task": config.StringVariable(secondTaskName),
}
resourceName := "snowflake_grant_ownership.test"

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.RequireAbove(tfversion.Version1_5_0),
},
Steps: []resource.TestStep{
{
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantOwnership/OnAllTasks"),
ConfigVariables: configVariables,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "account_role_name", accountRoleName),
resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("ToAccountRole|%s|REVOKE|OnAll|TASKS|InSchema|%s", accountRoleFullyQualifiedName, schemaFullyQualifiedName)),
checkResourceOwnershipIsGranted(&sdk.ShowGrantOptions{
To: &sdk.ShowGrantsTo{
Role: sdk.NewAccountObjectIdentifier(accountRoleName),
},
},
sdk.ObjectTypeTask, accountRoleName,
// TODO(SNOW-999049): Fix this identifier
fmt.Sprintf("%s\".\"%s\".%s", acc.TestDatabaseName, acc.TestSchemaName, taskName),
fmt.Sprintf("%s\".\"%s\".%s", acc.TestDatabaseName, acc.TestSchemaName, secondTaskName),
),
),
},
},
})
}

func createDatabaseWithRoleAsOwner(t *testing.T, roleName string, databaseName string) func() {
t.Helper()
client, err := sdk.NewDefaultClient()
Expand Down
31 changes: 31 additions & 0 deletions pkg/resources/testdata/TestAcc_GrantOwnership/OnAllTasks/test.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
resource "snowflake_role" "test" {
name = var.account_role_name
}

resource "snowflake_task" "test" {
database = var.database
schema = var.schema
name = var.task
sql_statement = "SELECT CURRENT_TIMESTAMP"
}

resource "snowflake_task" "second_test" {
database = var.database
schema = var.schema
name = var.second_task
sql_statement = "SELECT CURRENT_TIMESTAMP"
}

resource "snowflake_grant_ownership" "test" {
depends_on = [snowflake_task.test, snowflake_task.second_test]
account_role_name = snowflake_role.test.name

on {
all {
object_type_plural = "TASKS"
in_schema = "\"${var.database}\".\"${var.schema}\""
}
}

outbound_privileges = "REVOKE"
}
Loading
Loading