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)