diff --git a/docs/resources/grant_ownership.md b/docs/resources/grant_ownership.md index 7bd1b083c5..4636598646 100644 --- a/docs/resources/grant_ownership.md +++ b/docs/resources/grant_ownership.md @@ -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 @@ -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 `""."".""` + +Import is supported using the following syntax: + +`terraform import "||||"` + +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 "|||OnObject||"` + +### OnAll (contains inner types: InDatabase | InSchema) + +#### InDatabase +`terraform import "|||OnAll||InDatabase|"` + +#### InSchema +`terraform import "|||OnAll||InSchema|"` + +### OnFuture (contains inner types: InDatabase | InSchema) + +#### InDatabase +`terraform import "|||OnFuture||InDatabase|"` + +#### InSchema +`terraform import "|||OnFuture||InSchema|"` + +### 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\""` diff --git a/docs/technical-documentation/grants_redesign_desgin_decisions.md b/docs/technical-documentation/grants_redesign_desgin_decisions.md new file mode 100644 index 0000000000..b70af89be8 --- /dev/null +++ b/docs/technical-documentation/grants_redesign_desgin_decisions.md @@ -0,0 +1,120 @@ +## Why did we decide to do a grant redesign? +Multiple factors led us to refactor grant resources, the most notable being: +- Grant problems were the majority in our GitHub issues page. We wanted to resolve all of them by providing a better solution. +- Old grant resources were made by target object and not by grant type. That led to a large number of grants to maintain. In contrast, after the refactor, we ended up with 8 resources which is a significantly lower amount in comparison to around 23 old grants (additionally, they were incomplete, and a lot more should be added to achieve full compatibility with Snowflake capabilities). +- It aligned with our goal of 100% grant feature coverage. When it comes to managing infrastructure in any way, access management is one of the most important things to consider. Thus, the user should be able to perform any granting operation possible to do manually in the worksheet. +- It’s common to use tools that perform data manipulation (like dbt) on infrastructure created by Terraform. Because of that, we should be able to perform granting commands that some of the tools may require to work properly (GRANT OWNERSHIP could be one of them). + +## What are the new grant resources? +Here’s a list of resources and data sources we introduced during the grant redesign. Those resources are made to deprecate and eventually fully replace all of the previously existing grant resources. + +**Resources** +- [snowflake_grant_privileges_to_database_role](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_database_role) +- [snowflake_grant_privileges_to_account_role](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_account_role) +- [snowflake_grant_account_role](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_account_role) +- [snowflake_grant_database_role](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_database_role) +- snowflake_grant_application_role (coming soon) +- [snowflake_grant_privileges_to_share](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_privileges_to_share) +- [snowflake_grant_ownership](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_ownership) + +**Data sources** +- [snowflake_grants](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/data-sources/grants) + +## Design decisions + +### Implicit enable_multiple_grants enabled by default +With the grant resources replacement, we wanted to change some of the default behaviors. +One of those defaults would be to make grant resources only care about privileges granted by them, mimicking the old **enable_multiple_grants** field enabled. +The motivation behind this was consistency with other resources. Other resources work in a way that they only care about themselves and manage the objects they are configured for. +Additionally, having such a destructive default could lead to some unexpected grants being revoked if someone forgets to set the flag. +Right now, there’s no alternative to the behavior **enable_multiple_grants** set to false, but we’re considering a flag for that case ([see future topics section](#future-topics)). + +### Workaround for on_all and all_privileges (the always_apply parameter) +As with the **on_future** field, granting **all_privileges** or granting **on_all** also raised a few questions about tracking granted objects. +Mostly, it boiled down to running the GRANT statement whenever something changes in the Snowflake infrastructure in the case of **on_all** or whenever a privilege was added to/removed from Snowflake in the case of **all_privileges**. +We still have to discuss how and if we would like to have internal tracking of the objects affected by those commands. + +For now, we decided to add the **always_apply** parameter that always produces a Terraform plan which re-grants specified privileges per terraform apply command execution. +It’s worth noting that the workaround doesn’t meet with the Terraform idea of providers having an eventually convergent state (after running the “terraform apply” the provider should eventually produce no plan). +Any user relying on this principle in their CI/CD pipelines should have this in mind when using **always_apply**. + +### How should we treat the on_future parameter? +In privilege-granting resources, there’s an option to grant specific privileges on objects created in the future. +This raised a question of what we should do to the granted privileges when the resource with the specified **on_future** field is being removed. +Should we track granted privileges and revoke them or don’t track them at all? We ended up with a decision to treat the **on_future** option as a “[trigger](https://en.wikipedia.org/wiki/Database_trigger)”. +There were a lot of benefits that came with that assumption, and also it was already implemented in such a way, so it wasn’t a big surprise to the users already using that feature. +We are removing the **on_future** “trigger” on the Terraform delete operation, leaving affected grants as they were. + +[Documentation Reference](https://docs.snowflake.com/en/sql-reference/sql/grant-privilege#optional-parameters) + +### Common identifier misuses +A big portion of the issues regarding grants was identifier-related (missing quotes, identifiers with special characters, etc.). +To partially resolve this issue, we introduced better validation on the schema level for most problematic identifier fields. +Some of the fields couldn’t be validated, because they are multipurpose. +Sometimes they expect account-level objects (e.g. database) and sometimes schema-level objects (e.g. table). +Even though not all of the fields are using this validation method, we don’t see those kinds of issues anymore, +indicating the better validation method and clearer error messages helped you with defining some of the grant resources. + +### snowflake_grant_privileges_to_role to snowflake_grant_privileges_to_account_role +There are two main reasons why we wanted to create a successor of **snowflake_grant_privileges_to_role** called **snowflake_grant_privileges_to_account_role**: +- We wanted to make a clear distinction between account and database role resources (in the name of resources and fields). + We are aware that there may be resources/fields which should be adjusted to follow this convention. We will be gradually changing those notifying you through [MIGRATION_GUIDE.md](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md). +- We wanted to change an internal identifier used in the **snowflake_grant_privileges_to_role** to a more import-friendly alternative. + To follow some level of deprecation and give users time to migrate, It was easier to create another resource with a different name and internals for the same purpose, than change the existing resource and force everyone to migrate their state. + We are aware that grants are special resources and in some cases, they may be created in the count of hundreds or thousands. + To migrate this number of resources is not easy and should be performed gradually, and we didn’t want to block users from the latest features of the provider just because of the grant refactoring we were doing at the time. + +To name a few lower-priority reasons, we also had in mind that: +- When adding a new resource we could carry out a large code structure refactor without fear of breaking anything already used by the users. + This increased the maintainability of the resource and made it easier to grasp, so providing new functionality or fixing a bug can be done faster. +- We wanted to address all known edge cases during the refactor mentioned above, making the resource more complete. + +### Why we decide to stick with one data source for grants +Grants have one show page in the documentation and were already represented as one data source. The motivation behind following this path was that: +- It's already there, so users wouldn't have to change much. +- It's easier from the usability point of view because it reflects the Snowflake documentation page, so it’s easier to have it opened on the side and create a configuration based on it. +- Performance shouldn't be a concern, because the bottlenecks that would be visible in the single data source, would also apply to the separate data sources approach. +- Even though the data source seems pretty big, the code behind it is relatively small and easy to maintain. Dividing it into smaller parts could negatively affect the maintainability. + +[Documentation Reference](https://docs.snowflake.com/en/sql-reference/sql/show-grants) + +### A snowflake_grant_privileges_to_application resource won’t be added +We didn’t implement the snowflake_grant_privileges_to_application resource, because granting/revoking privileges to/from application roles is only possible to perform from within the application context. + +[Documentation Reference](https://docs.snowflake.com/en/sql-reference/sql/grant-privilege-application-role#usage-notes) + +### An application_role_name parameter won’t be added to the grant_application_role resource +Granting an application role to another application role can only be performed within the context of an installed application, e.g. in the application’s setup script. + +[Documentation Reference](https://docs.snowflake.com/en/sql-reference/sql/grant-application-role) + +### Instance roles won’t be added to the snowflake_grants data source (for now) +We didn’t add the **instance_role** field to the **snowflake_grants** data source, because they would require implementing a new type of identifier, which wouldn’t be that easy. +We decided to tackle **instance_role** identifier later because the topic of identifiers is something we would like to look into soon [after the grant redesign](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md#redesigning-grants). +After the identifiers redesign, we will be in a much better position to add new identifier types or functionalities around them. + +[Identifier redesign](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md#identifiers-rework) + +[Documentation Reference](https://docs.snowflake.com/en/sql-reference/snowflake-db-classes) + +## Grant ownership +Granting ownership was something that we discussed a long time ago. +Initially, we decided not to add it and create a document backed by an analysis that would contain reasons why it wouldn’t be possible to cover some cases. +We wanted to be careful with such decisions, and that’s why we asked for your and internal feedback ([grant ownership discussion](https://github.com/Snowflake-Labs/terraform-provider-snowflake/discussions/2235)). + +After receiving feedback and doing a deeper analysis of certain cases, we concluded that we can and should have this resource available. +Soon after, we created a design document representing a proposal of the grant ownership resource schema, behavior, and edge cases to cover. +Currently, we are working on the implementation of the [grant ownership resource](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_ownership). After it is ready, we will announce it in our GitHub repository as well as threads mentioning grant ownership. + +## Future topics +Even though the grants redesign initiative is lasting longer than we initially expected, there are still things that need to be discussed or discovered. +There may be some unusual cases where the current version of grant resources may struggle with. +With a more stable project, we will be able to do more work dedicated to discovering such edge cases. +For now, we’re relying on you to report such cases that we later analyze, prioritize, fix, and release as soon as possible. +As for the list of things yet to be discussed, we have: +- Right now, there's no way to "have **enable_multiple_grants** turned off" in the new grant resources, but we are considering adding an **authoritative** flag (the name is not chosen yet) that would work oppositely to the **enable_multiple_grants**. + By enabling the **authoritative** flag, any other privileges granted to the target object will be revoked, making the granting resource the only source of privileges on this object. +- Discussion on granting **all_privileges** and **on_all** where we’ll decide how and if we would like to track changes of: + - Added or removed privileges by Snowflake in the case of all_privileges + - Added or removed objects by the user in Snowflake in the case of on_all +- Think about adding a flag to privilege-granting resources to prevent other sources from granting privileges on the same object. It would behave similarly to the enable_multiple_grants field in the old grant resources. diff --git a/tmp/snowflake_grant_ownership/import.sh b/examples/resources/snowflake_grant_ownership/import.sh similarity index 100% rename from tmp/snowflake_grant_ownership/import.sh rename to examples/resources/snowflake_grant_ownership/import.sh diff --git a/tmp/snowflake_grant_ownership/resource.tf b/examples/resources/snowflake_grant_ownership/resource.tf similarity index 77% rename from tmp/snowflake_grant_ownership/resource.tf rename to examples/resources/snowflake_grant_ownership/resource.tf index e6c4f1cf51..38dc8a0dd2 100644 --- a/tmp/snowflake_grant_ownership/resource.tf +++ b/examples/resources/snowflake_grant_ownership/resource.tf @@ -149,3 +149,41 @@ resource "snowflake_grant_ownership" "test" { } } +################################## +### 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" +} diff --git a/pkg/resources/grant_ownership.go b/pkg/resources/grant_ownership.go index 787b0a532d..dc287b24f8 100644 --- a/pkg/resources/grant_ownership.go +++ b/pkg/resources/grant_ownership.go @@ -342,10 +342,11 @@ func ReadGrantOwnership(ctx context.Context, d *schema.ResourceData, meta any) d grants, err := client.Grants.Show(ctx, opts) if err != nil { + d.SetId("") return diag.Diagnostics{ diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to retrieve grants", + Severity: diag.Warning, + Summary: "Failed to retrieve grants. Marking the resource as removed.", Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err), }, } @@ -384,12 +385,11 @@ func ReadGrantOwnership(ctx context.Context, d *schema.ResourceData, meta any) d if !ownershipFound { d.SetId("") - return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Warning, - Summary: "Couldn't find OWNERSHIP privilege on the target object. Marking resource as removed.", - Detail: fmt.Sprintf("Id: %s", id.String()), + Summary: "Couldn't find OWNERSHIP privilege on the target object. Marking the resource as removed.", + Detail: fmt.Sprintf("Id: %s", d.Id()), }, } } diff --git a/pkg/resources/grant_ownership_acceptance_test.go b/pkg/resources/grant_ownership_acceptance_test.go index e2153e59fa..a3ba8231a3 100644 --- a/pkg/resources/grant_ownership_acceptance_test.go +++ b/pkg/resources/grant_ownership_acceptance_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/stretchr/testify/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" @@ -22,8 +23,6 @@ import ( ) func TestAcc_GrantOwnership_OnObject_Database_ToAccountRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) databaseFullyQualifiedName := sdk.NewAccountObjectIdentifier(databaseName).FullyQualifiedName() @@ -70,8 +69,6 @@ func TestAcc_GrantOwnership_OnObject_Database_ToAccountRole(t *testing.T) { } func TestAcc_GrantOwnership_OnObject_Database_IdentifiersWithDots(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(5, acctest.CharSetAlpha) + "." + acctest.RandStringFromCharSet(5, acctest.CharSetAlpha)) databaseFullyQualifiedName := sdk.NewAccountObjectIdentifier(databaseName).FullyQualifiedName() @@ -118,8 +115,6 @@ func TestAcc_GrantOwnership_OnObject_Database_IdentifiersWithDots(t *testing.T) } func TestAcc_GrantOwnership_OnObject_Schema_ToAccountRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaFullyQualifiedName := sdk.NewDatabaseObjectIdentifier(databaseName, schemaName).FullyQualifiedName() @@ -168,8 +163,6 @@ func TestAcc_GrantOwnership_OnObject_Schema_ToAccountRole(t *testing.T) { } func TestAcc_GrantOwnership_OnObject_Schema_ToDatabaseRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaFullyQualifiedName := sdk.NewDatabaseObjectIdentifier(databaseName, schemaName).FullyQualifiedName() @@ -218,8 +211,6 @@ func TestAcc_GrantOwnership_OnObject_Schema_ToDatabaseRole(t *testing.T) { } func TestAcc_GrantOwnership_OnObject_Table_ToAccountRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) tableName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) @@ -270,8 +261,6 @@ func TestAcc_GrantOwnership_OnObject_Table_ToAccountRole(t *testing.T) { } func TestAcc_GrantOwnership_OnObject_Table_ToDatabaseRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) tableName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) @@ -322,8 +311,6 @@ func TestAcc_GrantOwnership_OnObject_Table_ToDatabaseRole(t *testing.T) { } func TestAcc_GrantOwnership_OnAll_InDatabase_ToAccountRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) databaseFullyQualifiedName := sdk.NewAccountObjectIdentifier(databaseName).FullyQualifiedName() @@ -377,8 +364,6 @@ func TestAcc_GrantOwnership_OnAll_InDatabase_ToAccountRole(t *testing.T) { } func TestAcc_GrantOwnership_OnAll_InSchema_ToAccountRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaFullyQualifiedName := sdk.NewDatabaseObjectIdentifier(databaseName, schemaName).FullyQualifiedName() @@ -432,8 +417,6 @@ func TestAcc_GrantOwnership_OnAll_InSchema_ToAccountRole(t *testing.T) { } func TestAcc_GrantOwnership_OnFuture_InDatabase_ToAccountRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) databaseFullyQualifiedName := sdk.NewAccountObjectIdentifier(databaseName).FullyQualifiedName() @@ -481,8 +464,6 @@ func TestAcc_GrantOwnership_OnFuture_InDatabase_ToAccountRole(t *testing.T) { } func TestAcc_GrantOwnership_OnFuture_InSchema_ToAccountRole(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaFullyQualifiedName := sdk.NewDatabaseObjectIdentifier(databaseName, schemaName).FullyQualifiedName() @@ -532,8 +513,6 @@ func TestAcc_GrantOwnership_OnFuture_InSchema_ToAccountRole(t *testing.T) { } func TestAcc_GrantOwnership_InvalidConfiguration_EmptyObjectType(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - configVariables := config.Variables{ "account_role_name": config.StringVariable(strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))), "database_name": config.StringVariable(strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))), @@ -556,8 +535,6 @@ func TestAcc_GrantOwnership_InvalidConfiguration_EmptyObjectType(t *testing.T) { } func TestAcc_GrantOwnership_InvalidConfiguration_MultipleTargets(t *testing.T) { - t.Skip("will be unskipped in the following grant ownership prs") - configVariables := config.Variables{ "account_role_name": config.StringVariable(strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))), "database_name": config.StringVariable(strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))), @@ -579,6 +556,244 @@ func TestAcc_GrantOwnership_InvalidConfiguration_MultipleTargets(t *testing.T) { }) } +func TestAcc_GrantOwnership_TargetObjectRemovedOutsideTerraform(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseFullyQualifiedName := sdk.NewAccountObjectIdentifier(databaseName).FullyQualifiedName() + + accountRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + accountRoleFullyQualifiedName := sdk.NewAccountObjectIdentifier(accountRoleName).FullyQualifiedName() + + cleanupDatabase := createDatabase(t, databaseName) + + configVariables := config.Variables{ + "account_role_name": config.StringVariable(accountRoleName), + "database_name": config.StringVariable(databaseName), + } + 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/OnObject_Database_ToAccountRole_NoDatabaseResource"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "account_role_name", accountRoleName), + resource.TestCheckResourceAttr(resourceName, "on.0.object_type", "DATABASE"), + resource.TestCheckResourceAttr(resourceName, "on.0.object_name", databaseName), + resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("ToAccountRole|%s||OnObject|DATABASE|%s", accountRoleFullyQualifiedName, databaseFullyQualifiedName)), + checkResourceOwnershipIsGranted(&sdk.ShowGrantOptions{ + To: &sdk.ShowGrantsTo{ + Role: sdk.NewAccountObjectIdentifier(accountRoleName), + }, + }, sdk.ObjectTypeDatabase, accountRoleName, databaseName), + ), + }, + { + PreConfig: func() { + grantOwnershipToTheCurrentRole(t, sdk.OwnershipGrantOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectTypeDatabase, + Name: sdk.NewAccountObjectIdentifier(databaseName), + }, + }) + cleanupDatabase() + }, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoDatabaseResource"), + ConfigVariables: configVariables, + // The error occurs in Create operation indicating the Read operation couldn't find the grant and set the resource as removed. + ExpectError: regexp.MustCompile("An error occurred during grant ownership"), + }, + }, + }) +} + +func TestAcc_GrantOwnership_AccountRoleRemovedOutsideTerraform(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseFullyQualifiedName := sdk.NewAccountObjectIdentifier(databaseName).FullyQualifiedName() + + accountRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + accountRoleFullyQualifiedName := sdk.NewAccountObjectIdentifier(accountRoleName).FullyQualifiedName() + + cleanupAccountRole := createAccountRole(t, accountRoleName) + + configVariables := config.Variables{ + "account_role_name": config.StringVariable(accountRoleName), + "database_name": config.StringVariable(databaseName), + } + 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/OnObject_Database_ToAccountRole_NoRoleResource"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "account_role_name", accountRoleName), + resource.TestCheckResourceAttr(resourceName, "on.0.object_type", "DATABASE"), + resource.TestCheckResourceAttr(resourceName, "on.0.object_name", databaseName), + resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("ToAccountRole|%s||OnObject|DATABASE|%s", accountRoleFullyQualifiedName, databaseFullyQualifiedName)), + checkResourceOwnershipIsGranted(&sdk.ShowGrantOptions{ + To: &sdk.ShowGrantsTo{ + Role: sdk.NewAccountObjectIdentifier(accountRoleName), + }, + }, sdk.ObjectTypeDatabase, accountRoleName, databaseName), + ), + }, + { + PreConfig: func() { + cleanupAccountRole() + }, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoRoleResource"), + ConfigVariables: configVariables, + // The error occurs in Create operation indicating the Read operation couldn't find the grant and set the resource as removed. + ExpectError: regexp.MustCompile("An error occurred during grant ownership"), + }, + }, + }) +} + +func TestAcc_GrantOwnership_OnMaterializedView(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + tableName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + materializedViewName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + materializedViewFullyQualifiedName := sdk.NewSchemaObjectIdentifier(databaseName, schemaName, materializedViewName).FullyQualifiedName() + + accountRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + accountRoleFullyQualifiedName := sdk.NewAccountObjectIdentifier(accountRoleName).FullyQualifiedName() + + configVariables := config.Variables{ + "account_role_name": config.StringVariable(accountRoleName), + "database_name": config.StringVariable(databaseName), + "schema_name": config.StringVariable(schemaName), + "table_name": config.StringVariable(tableName), + "materialized_view_name": config.StringVariable(materializedViewName), + "warehouse_name": config.StringVariable(acc.TestWarehouseName), + } + 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/OnObject_MaterializedView_ToAccountRole"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "account_role_name", accountRoleName), + resource.TestCheckResourceAttr(resourceName, "on.0.object_type", "MATERIALIZED VIEW"), + resource.TestCheckResourceAttr(resourceName, "on.0.object_name", materializedViewFullyQualifiedName), + resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("ToAccountRole|%s||OnObject|MATERIALIZED VIEW|%s", accountRoleFullyQualifiedName, materializedViewFullyQualifiedName)), + checkResourceOwnershipIsGranted(&sdk.ShowGrantOptions{ + To: &sdk.ShowGrantsTo{ + Role: sdk.NewAccountObjectIdentifier(accountRoleName), + }, + }, sdk.ObjectTypeMaterializedView, accountRoleName, fmt.Sprintf("%s.%s.%s", databaseName, schemaName, materializedViewName)), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantOwnership/OnObject_MaterializedView_ToAccountRole"), + ConfigVariables: configVariables, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAcc_GrantOwnership_RoleBasedAccessControlUseCase(t *testing.T) { + t.Skip("Will be un-skipped in SNOW-1313849") + + accountRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + userName := getCurrentUser(t) + + 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{ + // We have to make it in two steps, because provider blocks cannot contain depends_on meta-argument + // that are needed to grant the role to the current user before it can be used. + // Additionally, only the Config field can specify a configuration with custom provider blocks. + { + Config: roleBasedAccessControlUseCaseConfig(accountRoleName, databaseName, userName, schemaName, false), + }, + { + Config: roleBasedAccessControlUseCaseConfig(accountRoleName, databaseName, userName, schemaName, true), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) +} + +func roleBasedAccessControlUseCaseConfig(accountRoleName string, databaseName string, userName string, schemaName string, withSecondaryProvider bool) string { + baseConfig := fmt.Sprintf(` +resource "snowflake_role" "test" { + name = "%[1]s" +} + +resource "snowflake_database" "test" { + name = "%[2]s" +} + +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 = "%[3]s" +} +`, accountRoleName, databaseName, userName) + + secondaryProviderConfig := fmt.Sprintf(` +provider "snowflake" { + profile = "default" + alias = "secondary" + role = snowflake_role.test.name +} + +resource "snowflake_schema" "test" { + depends_on = [snowflake_grant_ownership.test, snowflake_grant_account_role.test] + provider = snowflake.secondary + database = snowflake_database.test.name + name = "%s" +} +`, schemaName) + + if withSecondaryProvider { + return fmt.Sprintf("%s\n%s", baseConfig, secondaryProviderConfig) + } + + return baseConfig +} + func TestAcc_GrantOwnership_MoveOwnershipOutsideTerraform(t *testing.T) { databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) databaseFullyQualifiedName := sdk.NewAccountObjectIdentifier(databaseName).FullyQualifiedName() @@ -850,3 +1065,60 @@ func checkResourceOwnershipIsGranted(opts *sdk.ShowGrantOptions, grantOn sdk.Obj return nil } } + +func createAccountRole(t *testing.T, name string) func() { + t.Helper() + client, err := sdk.NewDefaultClient() + assert.NoError(t, err) + + ctx := context.Background() + roleId := sdk.NewAccountObjectIdentifier(name) + assert.NoError(t, client.Roles.Create(ctx, sdk.NewCreateRoleRequest(roleId))) + + return func() { + assert.NoError(t, client.Roles.Drop(ctx, sdk.NewDropRoleRequest(roleId))) + } +} + +func createDatabase(t *testing.T, name string) func() { + t.Helper() + client, err := sdk.NewDefaultClient() + assert.NoError(t, err) + + ctx := context.Background() + roleId := sdk.NewAccountObjectIdentifier(name) + assert.NoError(t, client.Databases.Create(ctx, roleId, new(sdk.CreateDatabaseOptions))) + + return func() { + assert.NoError(t, client.Databases.Drop(ctx, roleId, new(sdk.DropDatabaseOptions))) + } +} + +func grantOwnershipToTheCurrentRole(t *testing.T, on sdk.OwnershipGrantOn) { + t.Helper() + client, err := sdk.NewDefaultClient() + assert.NoError(t, err) + + ctx := context.Background() + currentRole, err := client.ContextFunctions.CurrentRole(ctx) + assert.NoError(t, err) + + err = client.Grants.GrantOwnership( + ctx, + on, + sdk.OwnershipGrantTo{ + AccountRoleName: sdk.Pointer(sdk.NewAccountObjectIdentifier(currentRole)), + }, + new(sdk.GrantOwnershipOptions), + ) + assert.NoError(t, err) +} + +func getCurrentUser(t *testing.T) string { + t.Helper() + client, err := sdk.NewDefaultClient() + assert.NoError(t, err) + currentUser, err := client.ContextFunctions.CurrentUser(context.Background()) + assert.NoError(t, err) + return currentUser +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoDatabaseResource/test.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoDatabaseResource/test.tf new file mode 100644 index 0000000000..c4aca147cb --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoDatabaseResource/test.tf @@ -0,0 +1,11 @@ +resource "snowflake_role" "test" { + name = var.account_role_name +} + +resource "snowflake_grant_ownership" "test" { + account_role_name = snowflake_role.test.name + on { + object_type = "DATABASE" + object_name = var.database_name + } +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoDatabaseResource/variables.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoDatabaseResource/variables.tf new file mode 100644 index 0000000000..8af99038ae --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoDatabaseResource/variables.tf @@ -0,0 +1,7 @@ +variable "account_role_name" { + type = string +} + +variable "database_name" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoRoleResource/test.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoRoleResource/test.tf new file mode 100644 index 0000000000..718c619d2d --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoRoleResource/test.tf @@ -0,0 +1,11 @@ +resource "snowflake_database" "test" { + name = var.database_name +} + +resource "snowflake_grant_ownership" "test" { + account_role_name = var.account_role_name + on { + object_type = "DATABASE" + object_name = snowflake_database.test.name + } +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoRoleResource/variables.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoRoleResource/variables.tf new file mode 100644 index 0000000000..8af99038ae --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_Database_ToAccountRole_NoRoleResource/variables.tf @@ -0,0 +1,7 @@ +variable "account_role_name" { + type = string +} + +variable "database_name" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_MaterializedView_ToAccountRole/test.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_MaterializedView_ToAccountRole/test.tf new file mode 100644 index 0000000000..1fd903cb08 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_MaterializedView_ToAccountRole/test.tf @@ -0,0 +1,39 @@ +resource "snowflake_role" "test" { + name = var.account_role_name +} + +resource "snowflake_database" "test" { + name = var.database_name +} + +resource "snowflake_schema" "test" { + name = var.schema_name + database = snowflake_database.test.name +} + +resource "snowflake_table" "test" { + database = snowflake_database.test.name + name = var.table_name + schema = snowflake_schema.test.name + + column { + name = "id" + type = "NUMBER(38,0)" + } +} + +resource "snowflake_materialized_view" "test" { + database = snowflake_database.test.name + name = var.materialized_view_name + schema = snowflake_schema.test.name + statement = "select * from \"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\".\"${snowflake_table.test.name}\"" + warehouse = var.warehouse_name +} + +resource "snowflake_grant_ownership" "test" { + account_role_name = snowflake_role.test.name + on { + object_type = "MATERIALIZED VIEW" + object_name = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\".\"${snowflake_materialized_view.test.name}\"" + } +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_MaterializedView_ToAccountRole/variables.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_MaterializedView_ToAccountRole/variables.tf new file mode 100644 index 0000000000..9c899c3378 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/OnObject_MaterializedView_ToAccountRole/variables.tf @@ -0,0 +1,23 @@ +variable "account_role_name" { + type = string +} + +variable "database_name" { + type = string +} + +variable "schema_name" { + type = string +} + +variable "table_name" { + type = string +} + +variable "materialized_view_name" { + type = string +} + +variable "warehouse_name" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/RoleBasedAccessControlUseCase/test.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/RoleBasedAccessControlUseCase/test.tf new file mode 100644 index 0000000000..4184d0f209 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/RoleBasedAccessControlUseCase/test.tf @@ -0,0 +1,28 @@ +resource "snowflake_role" "test" { + name = var.account_role_name +} + +resource "snowflake_database" "test" { + name = var.database_name +} + +resource "snowflake_grant_ownership" "test" { + account_role_name = snowflake_role.test.name + on { + object_type = "DATABASE" + object_name = snowflake_database.test.name + } +} + +provider "snowflake" { + profile = "default" + alias = "secondary" + role = snowflake_role.test.name +} + +resource "snowflake_schema" "test" { + provider = snowflake.secondary + depends_on = [snowflake_grant_ownership.test] + database = snowflake_database.test.name + name = var.schema_name +} diff --git a/pkg/resources/testdata/TestAcc_GrantOwnership/RoleBasedAccessControlUseCase/variables.tf b/pkg/resources/testdata/TestAcc_GrantOwnership/RoleBasedAccessControlUseCase/variables.tf new file mode 100644 index 0000000000..e86f7da400 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantOwnership/RoleBasedAccessControlUseCase/variables.tf @@ -0,0 +1,11 @@ +variable "account_role_name" { + type = string +} + +variable "database_name" { + type = string +} + +variable "schema_name" { + type = string +} diff --git a/pkg/sdk/grants_impl.go b/pkg/sdk/grants_impl.go index f1611828bf..5b6d07979c 100644 --- a/pkg/sdk/grants_impl.go +++ b/pkg/sdk/grants_impl.go @@ -206,6 +206,7 @@ func (v *grants) GrantOwnership(ctx context.Context, on OwnershipGrantOn, to Own if opts == nil { opts = &GrantOwnershipOptions{} } + opts.On = on opts.To = to diff --git a/tmp/grant_ownership.md.tmpl b/templates/resources/grant_ownership.md.tmpl similarity index 85% rename from tmp/grant_ownership.md.tmpl rename to templates/resources/grant_ownership.md.tmpl index aa24bbbe5c..5739c74288 100644 --- a/tmp/grant_ownership.md.tmpl +++ b/templates/resources/grant_ownership.md.tmpl @@ -42,9 +42,14 @@ To transfer ownership of a pipe(s) **semi-automatically** you have to: 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 tasks +{{/*## Granting ownership on tasks*/}} {{/* TODO: In next pr */}} +## 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. + {{ .SchemaMarkdown | trimspace }} ## Import