diff --git a/docs/resources/grant_ownership.md b/docs/resources/grant_ownership.md new file mode 100644 index 00000000000..7bd1b083c56 --- /dev/null +++ b/docs/resources/grant_ownership.md @@ -0,0 +1,64 @@ +--- +page_title: "snowflake_grant_ownership Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + +--- + +# snowflake_grant_ownership (Resource) + + + + + + +## Schema + +### Required + +- `on` (Block List, Min: 1, Max: 1) Configures which object(s) should transfer their ownership to the specified role. (see [below for nested schema](#nestedblock--on)) + +### Optional + +- `account_role_name` (String) The fully qualified name of the account role to which privileges will be granted. +- `database_role_name` (String) The fully qualified name of the database role to which privileges will be granted. +- `outbound_privileges` (String) Specifies whether to remove or transfer all existing outbound privileges on the object when ownership is transferred to a new role. Available options are: REVOKE for removing existing privileges and COPY to transfer them with ownership. For more information head over to [Snowflake documentation](https://docs.snowflake.com/en/sql-reference/sql/grant-ownership#optional-parameters). + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `on` + +Optional: + +- `all` (Block List, Max: 1) Configures the privilege to be granted on all objects in either a database or schema. (see [below for nested schema](#nestedblock--on--all)) +- `future` (Block List, Max: 1) Configures the privilege to be granted on all objects in either a database or schema. (see [below for nested schema](#nestedblock--on--future)) +- `object_name` (String) Specifies the identifier for the object on which you are transferring ownership. +- `object_type` (String) Specifies the type of object on which you are transferring ownership. Available values are: AGGREGATION POLICY | ALERT | AUTHENTICATION POLICY | COMPUTE POOL | DATABASE | DATABASE ROLE | DYNAMIC TABLE | EVENT TABLE | EXTERNAL TABLE | EXTERNAL VOLUME | FAILOVER GROUP | FILE FORMAT | FUNCTION | HYBRID TABLE | ICEBERG TABLE | IMAGE REPOSITORY | INTEGRATION | MATERIALIZED VIEW | NETWORK POLICY | NETWORK RULE | PACKAGES POLICY | PIPE | PROCEDURE | MASKING POLICY | PASSWORD POLICY | PROJECTION POLICY | REPLICATION GROUP | ROLE | ROW ACCESS POLICY | SCHEMA | SESSION POLICY | SECRET | SEQUENCE | STAGE | STREAM | TABLE | TAG | TASK | USER | VIEW | WAREHOUSE + + +### Nested Schema for `on.all` + +Required: + +- `object_type_plural` (String) Specifies the type of object in plural form on which you are transferring ownership. Available values are: AGGREGATION POLICIES | ALERTS | AUTHENTICATION POLICIES | COMPUTE POOLS | DATABASES | DATABASE ROLES | DYNAMIC TABLES | EVENT TABLES | EXTERNAL TABLES | EXTERNAL VOLUMES | FAILOVER GROUPS | FILE FORMATS | FUNCTIONS | HYBRID TABLES | ICEBERG TABLES | IMAGE REPOSITORIES | INTEGRATIONS | MATERIALIZED VIEWS | NETWORK POLICIES | NETWORK RULES | PACKAGES POLICIES | PIPES | PROCEDURES | MASKING POLICIES | PASSWORD POLICIES | PROJECTION POLICIES | REPLICATION GROUPS | ROLES | ROW ACCESS POLICIES | SCHEMAS | SESSION POLICIES | SECRETS | SEQUENCES | STAGES | STREAMS | TABLES | TAGS | TASKS | USERS | VIEWS | WAREHOUSES. For more information head over to [Snowflake documentation](https://docs.snowflake.com/en/sql-reference/sql/grant-ownership#required-parameters). + +Optional: + +- `in_database` (String) The fully qualified name of the database. +- `in_schema` (String) The fully qualified name of the schema. + + + +### Nested Schema for `on.future` + +Required: + +- `object_type_plural` (String) Specifies the type of object in plural form on which you are transferring ownership. Available values are: AGGREGATION POLICIES | ALERTS | AUTHENTICATION POLICIES | COMPUTE POOLS | DATABASES | DATABASE ROLES | DYNAMIC TABLES | EVENT TABLES | EXTERNAL TABLES | EXTERNAL VOLUMES | FAILOVER GROUPS | FILE FORMATS | FUNCTIONS | HYBRID TABLES | ICEBERG TABLES | IMAGE REPOSITORIES | INTEGRATIONS | MATERIALIZED VIEWS | NETWORK POLICIES | NETWORK RULES | PACKAGES POLICIES | PIPES | PROCEDURES | MASKING POLICIES | PASSWORD POLICIES | PROJECTION POLICIES | REPLICATION GROUPS | ROLES | ROW ACCESS POLICIES | SCHEMAS | SESSION POLICIES | SECRETS | SEQUENCES | STAGES | STREAMS | TABLES | TAGS | TASKS | USERS | VIEWS | WAREHOUSES. For more information head over to [Snowflake documentation](https://docs.snowflake.com/en/sql-reference/sql/grant-ownership#required-parameters). + +Optional: + +- `in_database` (String) The fully qualified name of the database. +- `in_schema` (String) The fully qualified name of the schema. diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index f11d1512c73..6a0f8042b89 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -468,6 +468,7 @@ func getResources() map[string]*schema.Resource { "snowflake_function": resources.Function(), "snowflake_grant_account_role": resources.GrantAccountRole(), "snowflake_grant_database_role": resources.GrantDatabaseRole(), + "snowflake_grant_ownership": resources.GrantOwnership(), "snowflake_grant_privileges_to_role": resources.GrantPrivilegesToRole(), "snowflake_grant_privileges_to_account_role": resources.GrantPrivilegesToAccountRole(), "snowflake_grant_privileges_to_database_role": resources.GrantPrivilegesToDatabaseRole(), diff --git a/pkg/resources/grant_ownership.go b/pkg/resources/grant_ownership.go index 99e0a771a8e..b5fc9734efc 100644 --- a/pkg/resources/grant_ownership.go +++ b/pkg/resources/grant_ownership.go @@ -16,6 +16,26 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) +// Done (doc-discuss thread): +// - Materialized views + +// 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) + +// TODO for later: +// - Tasks bulk operations + +// TODO: +// - Test edge cases (and not only) +// - RBAC hierarchy +// - Delete resource before grant ownership resource +// - Pipes on bulk and individually +// - Tasks on bulk +// - Materialized view +// - Validation (?): grant ownership of e.g. share (not possible) +// - Dynamic Tables +// - Database Roles + var grantOwnershipSchema = map[string]*schema.Schema{ "account_role_name": { Type: schema.TypeString, @@ -342,10 +362,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), }, } @@ -383,10 +404,11 @@ func ReadGrantOwnership(ctx context.Context, d *schema.ResourceData, meta any) d } if !ownershipFound { + d.SetId("") return diag.Diagnostics{ diag.Diagnostic{ - Severity: diag.Error, - Summary: "Couldn't find OWNERSHIP privilege on target object", + Severity: diag.Warning, + Summary: "Couldn't find OWNERSHIP privilege on 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 009433a67e2..e7e4b0a2017 100644 --- a/pkg/resources/grant_ownership_acceptance_test.go +++ b/pkg/resources/grant_ownership_acceptance_test.go @@ -8,6 +8,9 @@ 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" "github.com/hashicorp/terraform-plugin-testing/terraform" @@ -577,6 +580,253 @@ func TestAcc_GrantOwnership_InvalidConfiguration_MultipleTargets(t *testing.T) { }) } +func TestAcc_GrantOwnership_TargetObjectRemovedOutsideTerraform(t *testing.T) { + // TODO(uncomment): t.Skip("will be unskipped in the following grant ownership prs") + + 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() { + 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) { + // TODO(uncomment): t.Skip("will be unskipped in the following grant ownership prs") + + 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) { + // TODO(Uncomment): 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)) + 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_OnTasks(t *testing.T) { + t.Skip("will be unskipped in the following grant ownership prs") + // - Tasks on bulk +} + +func TestAcc_GrantOwnership_OnAllTasks(t *testing.T) { + t.Skip("will be unskipped in the following grant ownership prs") + // - Tasks on bulk +} + +func TestAcc_GrantOwnership_RoleBasedAccessControlUseCase(t *testing.T) { + // TODO(uncomment): t.Skip("will be unskipped in the following grant ownership prs") + + 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" { + provider = snowflake.secondary + database = snowflake_database.test.name + name = "%s" +} +`, schemaName) + + if withSecondaryProvider { + return fmt.Sprintf("%s\n%s", baseConfig, secondaryProviderConfig) + } + + return baseConfig +} + func checkResourceOwnershipIsGranted(opts *sdk.ShowGrantOptions, grantOn sdk.ObjectType, roleName string, objectNames ...string) func(s *terraform.State) error { return func(s *terraform.State) error { client := acc.TestAccProvider.Meta().(*provider.Context).Client @@ -604,3 +854,54 @@ 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() { + currentRole, err := client.ContextFunctions.CurrentRole(ctx) + assert.NoError(t, err) + err = client.Grants.GrantOwnership(ctx, sdk.OwnershipGrantOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectTypeDatabase, + Name: roleId, + }, + }, + sdk.OwnershipGrantTo{ + AccountRoleName: sdk.Pointer(sdk.NewAccountObjectIdentifier(currentRole)), + }, + new(sdk.GrantOwnershipOptions), + ) + assert.NoError(t, err) + assert.NoError(t, client.Databases.Drop(ctx, roleId, new(sdk.DropDatabaseOptions))) + } +} + +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 00000000000..c4aca147cb5 --- /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 00000000000..8af99038aec --- /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 00000000000..718c619d2db --- /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 00000000000..8af99038aec --- /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 00000000000..1fd903cb085 --- /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 00000000000..9c899c33788 --- /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 00000000000..4184d0f2099 --- /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 00000000000..e86f7da4000 --- /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 bf11482e14e..a1ee2520f96 100644 --- a/pkg/sdk/grants_impl.go +++ b/pkg/sdk/grants_impl.go @@ -203,6 +203,7 @@ func (v *grants) GrantOwnership(ctx context.Context, on OwnershipGrantOn, to Own if opts == nil { opts = &GrantOwnershipOptions{} } + opts.On = on opts.To = to return validateAndExec(v.client, ctx, opts)