Skip to content

Commit

Permalink
feat: grant ownership follow-up (#2658)
Browse files Browse the repository at this point in the history
A follow-up for
#2604.

Done in this pr:
- All of the edge cases handled and tested (except of tasks that are
done in the separate pr):
  - Materialized views (already handled by Snowflake no changes needed)
  - RBAC hierarchy (test case added)
- Delete dependent resource (role or granted object) and remove grant
resource from the state (test case added)

Won't do:
- External tables (cannot handle this edge case, because we have to know
the auto_refresh state of the external table; it's not retrievable by
SHOW or DESC commands. It will be still possible to grant ownership of
the external table, but there may be additional manual work to do
afterward. Everything is documented.)

## Test Plan
<!-- detail ways in which this PR has been tested or needs to be tested
-->
* [x] acceptance tests that show how the resource is handling certain
edge cases + RBAC use case

## References
[GRANT
OWNERSHIP](https://docs.snowflake.com/en/sql-reference/sql/grant-ownership)

## Mentioned in
A list of issues requesting this resource:
#2549
#2199
#2084
#1942
#1875
  • Loading branch information
sfc-gh-jcieslak authored Apr 8, 2024
1 parent 0b947a5 commit bfa2317
Show file tree
Hide file tree
Showing 16 changed files with 885 additions and 30 deletions.
282 changes: 282 additions & 0 deletions docs/resources/grant_ownership.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,238 @@
---
# 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}\""
}
}
}
##################################
### RoleBasedAccessControl (RBAC example)
##################################
resource "snowflake_role" "test" {
name = "role"
}
resource "snowflake_database" "test" {
name = "database"
}
resource "snowflake_grant_ownership" "test" {
account_role_name = snowflake_role.test.name
on {
object_type = "DATABASE"
object_name = snowflake_database.test.name
}
}
resource "snowflake_grant_account_role" "test" {
role_name = snowflake_role.test.name
user_name = "username"
}
provider "snowflake" {
profile = "default"
alias = "secondary"
role = snowflake_role.test.name
}
## With ownership on the database, the secondary provider is able to create schema on it without any additional privileges.
resource "snowflake_schema" "test" {
depends_on = [snowflake_grant_ownership.test, snowflake_grant_account_role.test]
provider = snowflake.secondary
database = snowflake_database.test.name
name = "schema"
}
```

## 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 external tables
Transferring ownership on an external table or its parent database blocks automatic refreshes of the table metadata by setting the `AUTO_REFRESH` property to `FALSE`.
Right now, there's no way to check the `AUTO_REFRESH` state of the external table and because of that, a manual step is required after ownership transfer.
To set the `AUTO_REFRESH` property back to `TRUE` (after you transfer ownership), use the [ALTER EXTERNAL TABLE](https://docs.snowflake.com/en/sql-reference/sql/alter-external-table) command.

<!-- schema generated by tfplugindocs -->
## Schema
Expand Down Expand Up @@ -62,3 +285,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\""`
Loading

0 comments on commit bfa2317

Please sign in to comment.