diff --git a/docs/resources/grant_privileges_to_database_role.md b/docs/resources/grant_privileges_to_database_role.md
new file mode 100644
index 0000000000..067ee9caea
--- /dev/null
+++ b/docs/resources/grant_privileges_to_database_role.md
@@ -0,0 +1,309 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "snowflake_grant_privileges_to_database_role Resource - terraform-provider-snowflake"
+subcategory: ""
+description: |-
+
+---
+
+
+!> **Warning** Be careful when using `always_apply` field. It will always produce a plan (even when no changes were made) and can be harmful in some setups. For more details why we decided to introduce it to go our document explaining those design decisions (coming soon).
+
+# snowflake_grant_privileges_to_database_role (Resource)
+
+
+
+## Example Usage
+
+```terraform
+resource "snowflake_database_role" "db_role" {
+ database = "database"
+ name = "db_role_name"
+}
+
+##################################
+### on database privileges
+##################################
+
+# list of privileges
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["CREATE", "MONITOR"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_database = snowflake_database_role.db_role.database
+}
+
+# all privileges + grant option
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_database = snowflake_database_role.db_role.database
+ all_privileges = true
+ with_grant_option = true
+}
+
+# all privileges + grant option + always apply
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_database = snowflake_database_role.db_role.database
+ always_apply = true
+ all_privileges = true
+ with_grant_option = true
+}
+
+##################################
+### schema privileges
+##################################
+
+# list of privileges
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["MODIFY", "CREATE TABLE"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ schema_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+}
+
+# all privileges + grant option
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ schema_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+ all_privileges = true
+ with_grant_option = true
+}
+
+# all schemas in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["MODIFY", "CREATE TABLE"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ all_schemas_in_database = snowflake_database_role.db_role.database
+ }
+}
+
+# future schemas in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["MODIFY", "CREATE TABLE"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ future_schemas_in_database = snowflake_database_role.db_role.database
+ }
+}
+
+##################################
+### schema object privileges
+##################################
+
+# list of privileges
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "REFERENCES"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ object_type = "VIEW"
+ object_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\".\"my_view\"" # note this is a fully qualified name!
+ }
+}
+
+# all privileges + grant option
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ object_type = "VIEW"
+ object_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\".\"my_view\"" # note this is a fully qualified name!
+ }
+ all_privileges = true
+ with_grant_option = true
+}
+
+# all in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ all {
+ object_type_plural = "TABLES"
+ in_database = snowflake_database_role.db_role.database
+ }
+ }
+}
+
+# all in schema
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ all {
+ object_type_plural = "TABLES"
+ in_schema = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+ }
+}
+
+# future in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ future {
+ object_type_plural = "TABLES"
+ in_database = snowflake_database_role.db_role.database
+ }
+ }
+}
+
+# future in schema
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ future {
+ object_type_plural = "TABLES"
+ in_schema = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+ }
+}
+```
+
+
+## Schema
+
+### Required
+
+- `database_role_name` (String) The fully qualified name of the database role to which privileges will be granted.
+
+### Optional
+
+- `all_privileges` (Boolean) Grant all privileges on the database role.
+- `always_apply` (Boolean) If true, the resource will always produce a “plan” and on “apply” it will re-grant defined privileges. It is supposed to be used only in “grant privileges on all X’s in database / schema Y” or “grant all privileges to X” scenarios to make sure that every new object in a given database / schema is granted by the account role and every new privilege is granted to the database role. Important note: this flag is not compliant with the Terraform assumptions of the config being eventually convergent (producing an empty plan).
+- `always_apply_trigger` (String) This field should not be set and its main purpose is to achieve the functionality described by always_apply field. This is value will be flipped to the opposite value on every terraform apply, thus creating a new plan that will re-apply grants.
+- `on_database` (String) The fully qualified name of the database on which privileges will be granted.
+- `on_schema` (Block List, Max: 1) Specifies the schema on which privileges will be granted. (see [below for nested schema](#nestedblock--on_schema))
+- `on_schema_object` (Block List, Max: 1) Specifies the schema object on which privileges will be granted. (see [below for nested schema](#nestedblock--on_schema_object))
+- `privileges` (Set of String) The privileges to grant on the database role.
+- `with_grant_option` (Boolean) If specified, allows the recipient role to grant the privileges to other roles.
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `on_schema`
+
+Optional:
+
+- `all_schemas_in_database` (String) The fully qualified name of the database.
+- `future_schemas_in_database` (String) The fully qualified name of the database.
+- `schema_name` (String) The fully qualified name of the schema.
+
+
+
+### Nested Schema for `on_schema_object`
+
+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_schema_object--all))
+- `future` (Block List, Max: 1) Configures the privilege to be granted on future objects in either a database or schema. (see [below for nested schema](#nestedblock--on_schema_object--future))
+- `object_name` (String) The fully qualified name of the object on which privileges will be granted.
+- `object_type` (String) The object type of the schema object on which privileges will be granted. Valid values are: ALERT | DYNAMIC TABLE | EVENT TABLE | FILE FORMAT | FUNCTION | PROCEDURE | SECRET | SEQUENCE | PIPE | MASKING POLICY | PASSWORD POLICY | ROW ACCESS POLICY | SESSION POLICY | TAG | STAGE | STREAM | TABLE | EXTERNAL TABLE | TASK | VIEW | MATERIALIZED VIEW | NETWORK RULE | PACKAGES POLICY | ICEBERG TABLE
+
+
+### Nested Schema for `on_schema_object.all`
+
+Required:
+
+- `object_type_plural` (String) The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | DYNAMIC TABLES | EVENT TABLES | FILE FORMATS | FUNCTIONS | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS | NETWORK RULES | PACKAGES POLICIES | ICEBERG TABLES
+
+Optional:
+
+- `in_database` (String)
+- `in_schema` (String)
+
+
+
+### Nested Schema for `on_schema_object.future`
+
+Required:
+
+- `object_type_plural` (String) The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | DYNAMIC TABLES | EVENT TABLES | FILE FORMATS | FUNCTIONS | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS | NETWORK RULES | PACKAGES POLICIES | ICEBERG TABLES
+
+Optional:
+
+- `in_database` (String)
+- `in_schema` (String)
+
+## Import
+
+~> **Note** All the ..._name parts should be fully qualified names, e.g. for database object it is `"".""`
+~> **Note** To import all_privileges write ALL or ALL PRIVILEGES in place of ``
+
+Import is supported using the following syntax:
+
+`terraform import "|||||"`
+
+where:
+- database_role_name - fully qualified identifier
+- with_grant_option - boolean
+- always_apply - boolean
+- privileges - list of privileges, comma separated; to import all_privileges write "ALL" or "ALL PRIVILEGES"
+- grant_type - enum
+- grant_data - enum data
+
+It has varying number of parts, depending on grant_type. All the possible types are:
+
+### OnDatabase
+`terraform import "||||OnDatabase|"`
+
+### OnSchema
+
+On schema contains inner types for all options.
+
+#### OnSchema
+`terraform import "||||OnSchema|OnSchema|"`
+
+#### OnAllSchemasInDatabase
+`terraform import "||||OnSchema|OnAllSchemasInDatabase|"`
+
+#### OnFutureSchemasInDatabase
+`terraform import "||||OnSchema|OnFutureSchemasInDatabase|"`
+
+### OnSchemaObject
+
+On schema object contains inner types for all options.
+
+#### OnObject
+`terraform import "||||OnSchemaObject|OnObject||"`
+
+#### OnAll
+
+On all contains inner types for all options.
+
+##### InDatabase
+`terraform import "||||OnSchemaObject|OnAll||InDatabase|"`
+
+##### InSchema
+`terraform import "||||OnSchemaObject|OnAll||InSchema|"`
+
+#### OnFuture
+
+On future contains inner types for all options.
+
+##### InDatabase
+`terraform import "||||OnSchemaObject|OnFuture||InDatabase|"`
+
+##### InSchema
+`terraform import "||||OnSchemaObject|OnFuture||InSchema|"`
+
+### Import examples
+
+#### Grant all privileges OnDatabase
+`terraform import "\"test_db\".\"test_db_role\"|false|false|ALL|OnDatabase|\"test_db\""`
+
+#### Grant list of privileges OnAllSchemasInDatabase
+`terraform import "\"test_db\".\"test_db_role\"|false|false|CREATE TAG,CREATE TABLE|OnSchema|OnAllSchemasInDatabase|\"test_db\""`
+
+#### Grant list of privileges on table
+`terraform import "\"test_db\".\"test_db_role\"|false|false|SELECT,DELETE,INSERT|OnSchemaObject|OnObject|TABLE|\"test_db\".\"test_schema\".\"test_table\""`
+
+#### Grant list of privileges OnAll tables in schema
+`terraform import "\"test_db\".\"test_db_role\"|false|false|SELECT,DELETE,INSERT|OnSchemaObject|OnAll|TABLES|InSchema|\"test_db\".\"test_schema\""`
+
diff --git a/examples/resources/snowflake_grant_privileges_to_database_role/resource.tf b/examples/resources/snowflake_grant_privileges_to_database_role/resource.tf
new file mode 100644
index 0000000000..a5de908db6
--- /dev/null
+++ b/examples/resources/snowflake_grant_privileges_to_database_role/resource.tf
@@ -0,0 +1,146 @@
+resource "snowflake_database_role" "db_role" {
+ database = "database"
+ name = "db_role_name"
+}
+
+##################################
+### on database privileges
+##################################
+
+# list of privileges
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["CREATE", "MONITOR"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_database = snowflake_database_role.db_role.database
+}
+
+# all privileges + grant option
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_database = snowflake_database_role.db_role.database
+ all_privileges = true
+ with_grant_option = true
+}
+
+# all privileges + grant option + always apply
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_database = snowflake_database_role.db_role.database
+ always_apply = true
+ all_privileges = true
+ with_grant_option = true
+}
+
+##################################
+### schema privileges
+##################################
+
+# list of privileges
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["MODIFY", "CREATE TABLE"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ schema_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+}
+
+# all privileges + grant option
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ schema_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+ all_privileges = true
+ with_grant_option = true
+}
+
+# all schemas in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["MODIFY", "CREATE TABLE"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ all_schemas_in_database = snowflake_database_role.db_role.database
+ }
+}
+
+# future schemas in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["MODIFY", "CREATE TABLE"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema {
+ future_schemas_in_database = snowflake_database_role.db_role.database
+ }
+}
+
+##################################
+### schema object privileges
+##################################
+
+# list of privileges
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "REFERENCES"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ object_type = "VIEW"
+ object_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\".\"my_view\"" # note this is a fully qualified name!
+ }
+}
+
+# all privileges + grant option
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ object_type = "VIEW"
+ object_name = "\"${snowflake_database_role.db_role.database}\".\"my_schema\".\"my_view\"" # note this is a fully qualified name!
+ }
+ all_privileges = true
+ with_grant_option = true
+}
+
+# all in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ all {
+ object_type_plural = "TABLES"
+ in_database = snowflake_database_role.db_role.database
+ }
+ }
+}
+
+# all in schema
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ all {
+ object_type_plural = "TABLES"
+ in_schema = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+ }
+}
+
+# future in database
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ future {
+ object_type_plural = "TABLES"
+ in_database = snowflake_database_role.db_role.database
+ }
+ }
+}
+
+# future in schema
+resource "snowflake_grant_privileges_to_database_role" "example" {
+ privileges = ["SELECT", "INSERT"]
+ database_role_name = "\"${snowflake_database_role.db_role.database}\".\"${snowflake_database_role.db_role.name}\""
+ on_schema_object {
+ future {
+ object_type_plural = "TABLES"
+ in_schema = "\"${snowflake_database_role.db_role.database}\".\"my_schema\"" # note this is a fully qualified name!
+ }
+ }
+}
diff --git a/pkg/acceptance/testing.go b/pkg/acceptance/testing.go
index 0cd29e41f4..d6725ca491 100644
--- a/pkg/acceptance/testing.go
+++ b/pkg/acceptance/testing.go
@@ -91,3 +91,11 @@ func ConfigurationDirectory(directory string) func(config.TestStepConfigRequest)
return filepath.Join("testdata", directory)
}
}
+
+// ConfigurationInnerDirectory is similar to ConfigurationSameAsStepN, but instead of index-based directories,
+// you can choose a particular one by name.
+func ConfigurationInnerDirectory(innerDirectory string) func(config.TestStepConfigRequest) string {
+ return func(req config.TestStepConfigRequest) string {
+ return filepath.Join("testdata", req.TestName, innerDirectory)
+ }
+}
diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go
index f51cb5e706..6091561a00 100644
--- a/pkg/provider/provider.go
+++ b/pkg/provider/provider.go
@@ -444,6 +444,7 @@ func getResources() map[string]*schema.Resource {
"snowflake_file_format": resources.FileFormat(),
"snowflake_function": resources.Function(),
"snowflake_grant_privileges_to_role": resources.GrantPrivilegesToRole(),
+ "snowflake_grant_privileges_to_database_role": resources.GrantPrivilegesToDatabaseRole(),
"snowflake_managed_account": resources.ManagedAccount(),
"snowflake_masking_policy": resources.MaskingPolicy(),
"snowflake_materialized_view": resources.MaterializedView(),
diff --git a/pkg/resources/grant_privileges_to_database_role.go b/pkg/resources/grant_privileges_to_database_role.go
new file mode 100644
index 0000000000..8f4b9f0cb3
--- /dev/null
+++ b/pkg/resources/grant_privileges_to_database_role.go
@@ -0,0 +1,980 @@
+package resources
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "slices"
+ "strings"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/go-cty/cty"
+ "github.com/hashicorp/go-uuid"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+// TODO: Handle IMPORTED PRIVILEGES privilege (after second account will be added - SNOW-976501)
+
+var grantPrivilegesToDatabaseRoleSchema = map[string]*schema.Schema{
+ "database_role_name": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ Description: "The fully qualified name of the database role to which privileges will be granted.",
+ ValidateDiagFunc: IsValidIdentifier[sdk.DatabaseObjectIdentifier](),
+ },
+ "privileges": {
+ Type: schema.TypeSet,
+ Optional: true,
+ Description: "The privileges to grant on the database role.",
+ ExactlyOneOf: []string{
+ "privileges",
+ "all_privileges",
+ },
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ ValidateDiagFunc: isNotOwnershipGrant(),
+ },
+ },
+ "all_privileges": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ Description: "Grant all privileges on the database role.",
+ ExactlyOneOf: []string{
+ "privileges",
+ "all_privileges",
+ },
+ },
+ "with_grant_option": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ ForceNew: true,
+ Description: "If specified, allows the recipient role to grant the privileges to other roles.",
+ },
+ "always_apply": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ Description: "If true, the resource will always produce a “plan” and on “apply” it will re-grant defined privileges. It is supposed to be used only in “grant privileges on all X’s in database / schema Y” or “grant all privileges to X” scenarios to make sure that every new object in a given database / schema is granted by the account role and every new privilege is granted to the database role. Important note: this flag is not compliant with the Terraform assumptions of the config being eventually convergent (producing an empty plan).",
+ },
+ "always_apply_trigger": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "",
+ Description: "This field should not be set and its main purpose is to achieve the functionality described by always_apply field. This is value will be flipped to the opposite value on every terraform apply, thus creating a new plan that will re-apply grants.",
+ },
+ "on_database": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Description: "The fully qualified name of the database on which privileges will be granted.",
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
+ ExactlyOneOf: []string{
+ "on_database",
+ "on_schema",
+ "on_schema_object",
+ },
+ },
+ "on_schema": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Description: "Specifies the schema on which privileges will be granted.",
+ MaxItems: 1,
+ ExactlyOneOf: []string{
+ "on_database",
+ "on_schema",
+ "on_schema_object",
+ },
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "schema_name": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Description: "The fully qualified name of the schema.",
+ ValidateDiagFunc: IsValidIdentifier[sdk.DatabaseObjectIdentifier](),
+ ExactlyOneOf: []string{
+ "on_schema.0.schema_name",
+ "on_schema.0.all_schemas_in_database",
+ "on_schema.0.future_schemas_in_database",
+ },
+ },
+ "all_schemas_in_database": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Description: "The fully qualified name of the database.",
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
+ ExactlyOneOf: []string{
+ "on_schema.0.schema_name",
+ "on_schema.0.all_schemas_in_database",
+ "on_schema.0.future_schemas_in_database",
+ },
+ },
+ "future_schemas_in_database": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Description: "The fully qualified name of the database.",
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
+ ExactlyOneOf: []string{
+ "on_schema.0.schema_name",
+ "on_schema.0.all_schemas_in_database",
+ "on_schema.0.future_schemas_in_database",
+ },
+ },
+ },
+ },
+ },
+ "on_schema_object": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Description: "Specifies the schema object on which privileges will be granted.",
+ MaxItems: 1,
+ ExactlyOneOf: []string{
+ "on_database",
+ "on_schema",
+ "on_schema_object",
+ },
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "object_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Description: "The object type of the schema object on which privileges will be granted. Valid values are: ALERT | DYNAMIC TABLE | EVENT TABLE | FILE FORMAT | FUNCTION | PROCEDURE | SECRET | SEQUENCE | PIPE | MASKING POLICY | PASSWORD POLICY | ROW ACCESS POLICY | SESSION POLICY | TAG | STAGE | STREAM | TABLE | EXTERNAL TABLE | TASK | VIEW | MATERIALIZED VIEW | NETWORK RULE | PACKAGES POLICY | ICEBERG TABLE",
+ RequiredWith: []string{
+ "on_schema_object.0.object_name",
+ },
+ ConflictsWith: []string{
+ "on_schema_object.0.all",
+ "on_schema_object.0.future",
+ },
+ ValidateDiagFunc: ValidObjectType(),
+ },
+ "object_name": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Description: "The fully qualified name of the object on which privileges will be granted.",
+ RequiredWith: []string{
+ "on_schema_object.0.object_type",
+ },
+ ExactlyOneOf: []string{
+ "on_schema_object.0.object_name",
+ "on_schema_object.0.all",
+ "on_schema_object.0.future",
+ },
+ ValidateDiagFunc: IsValidIdentifier[sdk.SchemaObjectIdentifier](),
+ },
+ "all": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Description: "Configures the privilege to be granted on all objects in either a database or schema.",
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: grantPrivilegesOnDatabaseRoleBulkOperationSchema,
+ },
+ ConflictsWith: []string{
+ "on_schema_object.0.object_type",
+ },
+ ExactlyOneOf: []string{
+ "on_schema_object.0.object_name",
+ "on_schema_object.0.all",
+ "on_schema_object.0.future",
+ },
+ },
+ "future": {
+ Type: schema.TypeList,
+ Optional: true,
+ ForceNew: true,
+ Description: "Configures the privilege to be granted on future objects in either a database or schema.",
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: grantPrivilegesOnDatabaseRoleBulkOperationSchema,
+ },
+ ConflictsWith: []string{
+ "on_schema_object.0.object_type",
+ },
+ ExactlyOneOf: []string{
+ "on_schema_object.0.object_name",
+ "on_schema_object.0.all",
+ "on_schema_object.0.future",
+ },
+ },
+ },
+ },
+ },
+}
+
+var grantPrivilegesOnDatabaseRoleBulkOperationSchema = map[string]*schema.Schema{
+ "object_type_plural": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | DYNAMIC TABLES | EVENT TABLES | FILE FORMATS | FUNCTIONS | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS | NETWORK RULES | PACKAGES POLICIES | ICEBERG TABLES",
+ ValidateDiagFunc: ValidPluralObjectType(),
+ },
+ "in_database": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
+ },
+ "in_schema": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ ValidateDiagFunc: IsValidIdentifier[sdk.DatabaseObjectIdentifier](),
+ },
+}
+
+func isNotOwnershipGrant() func(value any, path cty.Path) diag.Diagnostics {
+ return func(value any, path cty.Path) diag.Diagnostics {
+ var diags diag.Diagnostics
+ if privilege, ok := value.(string); ok && strings.ToUpper(privilege) == "OWNERSHIP" {
+ diags = append(diags, diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Unsupported privilege 'OWNERSHIP'",
+ // TODO: Change when a new resource for granting ownership will be available (SNOW-991423)
+ Detail: "Granting ownership is only allowed in dedicated resources (snowflake_user_ownership_grant, snowflake_role_ownership_grant)",
+ AttributePath: nil,
+ })
+ }
+ return diags
+ }
+}
+
+func GrantPrivilegesToDatabaseRole() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: CreateGrantPrivilegesToDatabaseRole,
+ ReadContext: ReadGrantPrivilegesToDatabaseRole,
+ DeleteContext: DeleteGrantPrivilegesToDatabaseRole,
+ UpdateContext: UpdateGrantPrivilegesToDatabaseRole,
+
+ Schema: grantPrivilegesToDatabaseRoleSchema,
+ Importer: &schema.ResourceImporter{
+ StateContext: ImportGrantPrivilegesToDatabaseRole,
+ },
+ }
+}
+
+func ImportGrantPrivilegesToDatabaseRole(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
+ id, err := ParseGrantPrivilegesToDatabaseRoleId(d.Id())
+ if err != nil {
+ return nil, err
+ }
+ if err := d.Set("database_role_name", id.DatabaseRoleName.FullyQualifiedName()); err != nil {
+ return nil, err
+ }
+ if err := d.Set("with_grant_option", id.WithGrantOption); err != nil {
+ return nil, err
+ }
+ if err := d.Set("always_apply", id.AlwaysApply); err != nil {
+ return nil, err
+ }
+ if err := d.Set("all_privileges", id.AllPrivileges); err != nil {
+ return nil, err
+ }
+ if err := d.Set("privileges", id.Privileges); err != nil {
+ return nil, err
+ }
+
+ switch id.Kind {
+ case OnDatabaseDatabaseRoleGrantKind:
+ if err := d.Set("on_database", id.Data.(*OnDatabaseGrantData).DatabaseName.FullyQualifiedName()); err != nil {
+ return nil, err
+ }
+ case OnSchemaDatabaseRoleGrantKind:
+ data := id.Data.(*OnSchemaGrantData)
+ onSchema := make(map[string]any)
+
+ switch data.Kind {
+ case OnSchemaSchemaGrantKind:
+ onSchema["schema_name"] = data.SchemaName.FullyQualifiedName()
+ case OnAllSchemasInDatabaseSchemaGrantKind:
+ onSchema["all_schemas_in_database"] = data.DatabaseName.FullyQualifiedName()
+ case OnFutureSchemasInDatabaseSchemaGrantKind:
+ onSchema["future_schemas_in_database"] = data.DatabaseName.FullyQualifiedName()
+ }
+
+ if err := d.Set("on_schema", []any{onSchema}); err != nil {
+ return nil, err
+ }
+ case OnSchemaObjectDatabaseRoleGrantKind:
+ data := id.Data.(*OnSchemaObjectGrantData)
+ onSchemaObject := make(map[string]any)
+
+ switch data.Kind {
+ case OnObjectSchemaObjectGrantKind:
+ onSchemaObject["object_type"] = data.Object.ObjectType.String()
+ onSchemaObject["object_name"] = data.Object.Name.FullyQualifiedName()
+ case OnAllSchemaObjectGrantKind:
+ onAll := make(map[string]any)
+
+ onAll["object_type_plural"] = data.OnAllOrFuture.ObjectNamePlural.String()
+ switch data.OnAllOrFuture.Kind {
+ case InDatabaseBulkOperationGrantKind:
+ onAll["in_database"] = data.OnAllOrFuture.Database.FullyQualifiedName()
+ case InSchemaBulkOperationGrantKind:
+ onAll["in_schema"] = data.OnAllOrFuture.Schema.FullyQualifiedName()
+ }
+
+ onSchemaObject["all"] = []any{onAll}
+ case OnFutureSchemaObjectGrantKind:
+ onFuture := make(map[string]any)
+
+ onFuture["object_type_plural"] = data.OnAllOrFuture.ObjectNamePlural.String()
+ switch data.OnAllOrFuture.Kind {
+ case InDatabaseBulkOperationGrantKind:
+ onFuture["in_database"] = data.OnAllOrFuture.Database.FullyQualifiedName()
+ case InSchemaBulkOperationGrantKind:
+ onFuture["in_schema"] = data.OnAllOrFuture.Schema.FullyQualifiedName()
+ }
+
+ onSchemaObject["future"] = []any{onFuture}
+ }
+
+ if err := d.Set("on_schema_object", []any{onSchemaObject}); err != nil {
+ return nil, err
+ }
+ }
+
+ return []*schema.ResourceData{d}, nil
+}
+
+func CreateGrantPrivilegesToDatabaseRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+
+ id := createGrantPrivilegesToDatabaseRoleIdFromSchema(d)
+ err := client.Grants.GrantPrivilegesToDatabaseRole(
+ ctx,
+ getDatabaseRolePrivilegesFromSchema(d),
+ getDatabaseRoleGrantOn(d),
+ sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(d.Get("database_role_name").(string)),
+ &sdk.GrantPrivilegesToDatabaseRoleOptions{
+ WithGrantOption: sdk.Bool(d.Get("with_grant_option").(bool)),
+ },
+ )
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "An error occurred when granting privileges to database role",
+ Detail: fmt.Sprintf("Id: %s\nDatabase role name: %s\nError: %s", id.String(), id.DatabaseRoleName, err.Error()),
+ },
+ }
+ }
+
+ d.SetId(id.String())
+
+ return ReadGrantPrivilegesToDatabaseRole(ctx, d, meta)
+}
+
+func UpdateGrantPrivilegesToDatabaseRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ id, err := ParseGrantPrivilegesToDatabaseRoleId(d.Id())
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to parse internal identifier",
+ Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err.Error()),
+ },
+ }
+ }
+
+ if d.HasChange("all_privileges") {
+ _, allPrivileges := d.GetChange("all_privileges")
+
+ if !allPrivileges.(bool) {
+ err = client.Grants.RevokePrivilegesFromDatabaseRole(ctx, &sdk.DatabaseRoleGrantPrivileges{
+ AllPrivileges: sdk.Bool(true),
+ },
+ getDatabaseRoleGrantOn(d),
+ id.DatabaseRoleName,
+ new(sdk.RevokePrivilegesFromDatabaseRoleOptions),
+ )
+
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to revoke all privileges",
+ Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err.Error()),
+ },
+ }
+ }
+ }
+
+ id.AllPrivileges = allPrivileges.(bool)
+ }
+
+ if d.HasChange("privileges") {
+ shouldGrantAndRevoke := true
+
+ // Skip if all_privileges was set to true
+ if d.HasChange("all_privileges") {
+ if _, allPrivileges := d.GetChange("all_privileges"); allPrivileges.(bool) {
+ shouldGrantAndRevoke = false
+ id.Privileges = []string{}
+ }
+ }
+
+ if shouldGrantAndRevoke {
+ before, after := d.GetChange("privileges")
+ privilegesBeforeChange := expandStringList(before.(*schema.Set).List())
+ privilegesAfterChange := expandStringList(after.(*schema.Set).List())
+
+ var privilegesToAdd, privilegesToRemove []string
+
+ for _, privilegeBeforeChange := range privilegesBeforeChange {
+ if !slices.Contains(privilegesAfterChange, privilegeBeforeChange) {
+ privilegesToRemove = append(privilegesToRemove, privilegeBeforeChange)
+ }
+ }
+
+ for _, privilegeAfterChange := range privilegesAfterChange {
+ if !slices.Contains(privilegesBeforeChange, privilegeAfterChange) {
+ privilegesToAdd = append(privilegesToAdd, privilegeAfterChange)
+ }
+ }
+
+ grantOn := getDatabaseRoleGrantOn(d)
+
+ if len(privilegesToAdd) > 0 {
+ err = client.Grants.GrantPrivilegesToDatabaseRole(
+ ctx,
+ getDatabaseRolePrivileges(
+ false,
+ privilegesToAdd,
+ id.Kind == OnDatabaseDatabaseRoleGrantKind,
+ id.Kind == OnSchemaDatabaseRoleGrantKind,
+ id.Kind == OnSchemaObjectDatabaseRoleGrantKind,
+ ),
+ grantOn,
+ id.DatabaseRoleName,
+ new(sdk.GrantPrivilegesToDatabaseRoleOptions),
+ )
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to grant added privileges",
+ Detail: fmt.Sprintf("Id: %s\nPrivileges to add: %v\nError: %s", d.Id(), privilegesToAdd, err.Error()),
+ },
+ }
+ }
+ }
+
+ if len(privilegesToRemove) > 0 {
+ err = client.Grants.RevokePrivilegesFromDatabaseRole(
+ ctx,
+ getDatabaseRolePrivileges(
+ false,
+ privilegesToRemove,
+ id.Kind == OnDatabaseDatabaseRoleGrantKind,
+ id.Kind == OnSchemaDatabaseRoleGrantKind,
+ id.Kind == OnSchemaObjectDatabaseRoleGrantKind,
+ ),
+ grantOn,
+ id.DatabaseRoleName,
+ new(sdk.RevokePrivilegesFromDatabaseRoleOptions),
+ )
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to revoke removed privileges",
+ Detail: fmt.Sprintf("Id: %s\nPrivileges to remove: %v\nError: %s", d.Id(), privilegesToRemove, err.Error()),
+ },
+ }
+ }
+ }
+
+ id.Privileges = privilegesAfterChange
+ }
+ }
+
+ if d.HasChange("all_privileges") {
+ _, allPrivileges := d.GetChange("all_privileges")
+
+ if allPrivileges.(bool) {
+ err = client.Grants.GrantPrivilegesToDatabaseRole(ctx, &sdk.DatabaseRoleGrantPrivileges{
+ AllPrivileges: sdk.Bool(true),
+ },
+ getDatabaseRoleGrantOn(d),
+ id.DatabaseRoleName,
+ new(sdk.GrantPrivilegesToDatabaseRoleOptions),
+ )
+
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to grant all privileges",
+ Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err.Error()),
+ },
+ }
+ }
+ }
+
+ id.AllPrivileges = allPrivileges.(bool)
+ }
+
+ if d.HasChange("always_apply") {
+ id.AlwaysApply = d.Get("always_apply").(bool)
+ }
+
+ if id.AlwaysApply {
+ err := client.Grants.GrantPrivilegesToDatabaseRole(
+ ctx,
+ getDatabaseRolePrivilegesFromSchema(d),
+ getDatabaseRoleGrantOn(d),
+ id.DatabaseRoleName,
+ &sdk.GrantPrivilegesToDatabaseRoleOptions{
+ WithGrantOption: &id.WithGrantOption,
+ },
+ )
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Always apply. An error occurred when granting privileges to database role",
+ Detail: fmt.Sprintf("Id: %s\nDatabase role name: %s\nError: %s", d.Id(), id.DatabaseRoleName, err.Error()),
+ },
+ }
+ }
+ }
+
+ d.SetId(id.String())
+
+ return ReadGrantPrivilegesToDatabaseRole(ctx, d, meta)
+}
+
+func DeleteGrantPrivilegesToDatabaseRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ id, err := ParseGrantPrivilegesToDatabaseRoleId(d.Id())
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to parse internal identifier",
+ Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err.Error()),
+ },
+ }
+ }
+
+ err = client.Grants.RevokePrivilegesFromDatabaseRole(
+ ctx,
+ getDatabaseRolePrivilegesFromSchema(d),
+ getDatabaseRoleGrantOn(d),
+ id.DatabaseRoleName,
+ &sdk.RevokePrivilegesFromDatabaseRoleOptions{},
+ )
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "An error occurred when revoking privileges from database role",
+ Detail: fmt.Sprintf("Id: %s\nDatabase role name: %s\nError: %s", d.Id(), id.DatabaseRoleName, err.Error()),
+ },
+ }
+ }
+
+ d.SetId("")
+
+ return nil
+}
+
+func ReadGrantPrivilegesToDatabaseRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ id, err := ParseGrantPrivilegesToDatabaseRoleId(d.Id())
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to parse internal identifier",
+ Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err.Error()),
+ },
+ }
+ }
+
+ if id.AlwaysApply {
+ triggerId, err := uuid.GenerateUUID()
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to generate UUID",
+ Detail: fmt.Sprintf("Original error: %s", err.Error()),
+ },
+ }
+ }
+
+ // Change the value of always_apply_trigger to produce a plan
+ if err := d.Set("always_apply_trigger", triggerId); err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Error setting always_apply_trigger for database role",
+ Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err.Error()),
+ },
+ }
+ }
+ }
+
+ if id.AllPrivileges {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Warning,
+ Summary: "Show with all_privileges option is skipped.",
+ // TODO: link to the design decisions doc (SNOW-990811)
+ Detail: "See our document on design decisions for grants: ",
+ },
+ }
+ }
+
+ opts, grantedOn, diags := prepareShowGrantsRequest(id)
+ if len(diags) != 0 {
+ return diags
+ }
+
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ grants, err := client.Grants.Show(ctx, opts)
+ if err != nil {
+ return append(diags, diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to retrieve grants",
+ Detail: fmt.Sprintf("Id: %s\nError: %s", d.Id(), err.Error()),
+ })
+ }
+
+ var privileges []string
+
+ for _, grant := range grants {
+ // Accept only DATABASE ROLEs
+ if grant.GrantTo != sdk.ObjectTypeDatabaseRole && grant.GrantedTo != sdk.ObjectTypeDatabaseRole {
+ continue
+ }
+ // Only consider privileges that are already present in the ID, so we
+ // don't delete privileges managed by other resources.
+ if !slices.Contains(id.Privileges, grant.Privilege) {
+ continue
+ }
+ if id.WithGrantOption == grant.GrantOption && id.DatabaseRoleName.Name() == grant.GranteeName.Name() {
+ // Future grants do not have grantedBy, only current grants do.
+ // If grantedby is an empty string, it means terraform could not have created the grant
+ if (opts.Future == nil || !*opts.Future) && grant.GrantedBy.Name() == "" {
+ continue
+ }
+ // grant_on is for future grants, granted_on is for current grants.
+ // They function the same way though in a test for matching the object type
+ if grantedOn == grant.GrantedOn || grantedOn == grant.GrantOn {
+ privileges = append(privileges, grant.Privilege)
+ }
+ }
+ }
+
+ if err := d.Set("privileges", privileges); err != nil {
+ return append(diags, diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Error setting privileges for database role",
+ Detail: fmt.Sprintf("Id: %s\nPrivileges: %v\nError: %s", d.Id(), privileges, err.Error()),
+ })
+ }
+
+ return diags
+}
+
+func prepareShowGrantsRequest(id GrantPrivilegesToDatabaseRoleId) (*sdk.ShowGrantOptions, sdk.ObjectType, diag.Diagnostics) {
+ opts := new(sdk.ShowGrantOptions)
+ var grantedOn sdk.ObjectType
+
+ switch id.Kind {
+ case OnDatabaseDatabaseRoleGrantKind:
+ grantedOn = sdk.ObjectTypeDatabase
+ data := id.Data.(*OnDatabaseGrantData)
+ opts.On = &sdk.ShowGrantsOn{
+ Object: &sdk.Object{
+ ObjectType: sdk.ObjectTypeDatabase,
+ Name: data.DatabaseName,
+ },
+ }
+ case OnSchemaDatabaseRoleGrantKind:
+ grantedOn = sdk.ObjectTypeSchema
+ data := id.Data.(*OnSchemaGrantData)
+
+ switch data.Kind {
+ case OnSchemaSchemaGrantKind:
+ opts.On = &sdk.ShowGrantsOn{
+ Object: &sdk.Object{
+ ObjectType: sdk.ObjectTypeSchema,
+ Name: data.SchemaName,
+ },
+ }
+ case OnAllSchemasInDatabaseSchemaGrantKind:
+ return nil, "", diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Warning,
+ Summary: "Show with OnAllSchemasInDatabase option is skipped.",
+ // TODO: link to the design decisions doc (SNOW-990811)
+ Detail: "See our document on design decisions for grants: ",
+ },
+ }
+ case OnFutureSchemasInDatabaseSchemaGrantKind:
+ opts.Future = sdk.Bool(true)
+ opts.In = &sdk.ShowGrantsIn{
+ Database: data.DatabaseName,
+ }
+ }
+ case OnSchemaObjectDatabaseRoleGrantKind:
+ data := id.Data.(*OnSchemaObjectGrantData)
+
+ switch data.Kind {
+ case OnObjectSchemaObjectGrantKind:
+ grantedOn = data.Object.ObjectType
+ opts.On = &sdk.ShowGrantsOn{
+ Object: data.Object,
+ }
+ case OnAllSchemaObjectGrantKind:
+ return nil, "", diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Warning,
+ Summary: "Show with OnAll option is skipped.",
+ // TODO: link to the design decisions doc (SNOW-990811)
+ Detail: "See our document on design decisions for grants: ",
+ },
+ }
+ case OnFutureSchemaObjectGrantKind:
+ grantedOn = data.OnAllOrFuture.ObjectNamePlural.Singular()
+ opts.Future = sdk.Bool(true)
+
+ switch data.OnAllOrFuture.Kind {
+ case InDatabaseBulkOperationGrantKind:
+ opts.In = &sdk.ShowGrantsIn{
+ Database: data.OnAllOrFuture.Database,
+ }
+ case InSchemaBulkOperationGrantKind:
+ opts.In = &sdk.ShowGrantsIn{
+ Schema: data.OnAllOrFuture.Schema,
+ }
+ }
+ }
+ }
+
+ return opts, grantedOn, nil
+}
+
+func getDatabaseRolePrivilegesFromSchema(d *schema.ResourceData) *sdk.DatabaseRoleGrantPrivileges {
+ _, onDatabaseOk := d.GetOk("on_database")
+ _, onSchemaOk := d.GetOk("on_schema")
+ _, onSchemaObjectOk := d.GetOk("on_schema_object")
+
+ return getDatabaseRolePrivileges(
+ d.Get("all_privileges").(bool),
+ expandStringList(d.Get("privileges").(*schema.Set).List()),
+ onDatabaseOk,
+ onSchemaOk,
+ onSchemaObjectOk,
+ )
+}
+
+func getDatabaseRolePrivileges(allPrivileges bool, privileges []string, onDatabase bool, onSchema bool, onSchemaObject bool) *sdk.DatabaseRoleGrantPrivileges {
+ databaseRoleGrantPrivileges := new(sdk.DatabaseRoleGrantPrivileges)
+
+ if allPrivileges {
+ databaseRoleGrantPrivileges.AllPrivileges = sdk.Bool(true)
+ return databaseRoleGrantPrivileges
+ }
+
+ switch {
+ case onDatabase:
+ databasePrivileges := make([]sdk.AccountObjectPrivilege, len(privileges))
+ for i, privilege := range privileges {
+ databasePrivileges[i] = sdk.AccountObjectPrivilege(privilege)
+ }
+ databaseRoleGrantPrivileges.DatabasePrivileges = databasePrivileges
+ case onSchema:
+ schemaPrivileges := make([]sdk.SchemaPrivilege, len(privileges))
+ for i, privilege := range privileges {
+ schemaPrivileges[i] = sdk.SchemaPrivilege(privilege)
+ }
+ databaseRoleGrantPrivileges.SchemaPrivileges = schemaPrivileges
+ case onSchemaObject:
+ schemaObjectPrivileges := make([]sdk.SchemaObjectPrivilege, len(privileges))
+ for i, privilege := range privileges {
+ schemaObjectPrivileges[i] = sdk.SchemaObjectPrivilege(privilege)
+ }
+ databaseRoleGrantPrivileges.SchemaObjectPrivileges = schemaObjectPrivileges
+ }
+
+ return databaseRoleGrantPrivileges
+}
+
+func getDatabaseRoleGrantOn(d *schema.ResourceData) *sdk.DatabaseRoleGrantOn {
+ onDatabase, onDatabaseOk := d.GetOk("on_database")
+ onSchemaBlock, onSchemaOk := d.GetOk("on_schema")
+ onSchemaObjectBlock, onSchemaObjectOk := d.GetOk("on_schema_object")
+ on := new(sdk.DatabaseRoleGrantOn)
+
+ switch {
+ case onDatabaseOk:
+ on.Database = sdk.Pointer(sdk.NewAccountObjectIdentifierFromFullyQualifiedName(onDatabase.(string)))
+ case onSchemaOk:
+ onSchema := onSchemaBlock.([]any)[0].(map[string]any)
+
+ grantOnSchema := new(sdk.GrantOnSchema)
+
+ schemaName := onSchema["schema_name"].(string)
+ schemaNameOk := len(schemaName) > 0
+
+ allSchemasInDatabase := onSchema["all_schemas_in_database"].(string)
+ allSchemasInDatabaseOk := len(allSchemasInDatabase) > 0
+
+ futureSchemasInDatabase := onSchema["future_schemas_in_database"].(string)
+ futureSchemasInDatabaseOk := len(futureSchemasInDatabase) > 0
+
+ switch {
+ case schemaNameOk:
+ grantOnSchema.Schema = sdk.Pointer(sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(schemaName))
+ case allSchemasInDatabaseOk:
+ grantOnSchema.AllSchemasInDatabase = sdk.Pointer(sdk.NewAccountObjectIdentifierFromFullyQualifiedName(allSchemasInDatabase))
+ case futureSchemasInDatabaseOk:
+ grantOnSchema.FutureSchemasInDatabase = sdk.Pointer(sdk.NewAccountObjectIdentifierFromFullyQualifiedName(futureSchemasInDatabase))
+ }
+
+ on.Schema = grantOnSchema
+ case onSchemaObjectOk:
+ onSchemaObject := onSchemaObjectBlock.([]any)[0].(map[string]any)
+
+ grantOnSchemaObject := new(sdk.GrantOnSchemaObject)
+
+ objectType := onSchemaObject["object_type"].(string)
+ objectTypeOk := len(objectType) > 0
+
+ objectName := onSchemaObject["object_name"].(string)
+ objectNameOk := len(objectName) > 0
+
+ all := onSchemaObject["all"].([]any)
+ allOk := len(all) > 0
+
+ future := onSchemaObject["future"].([]any)
+ futureOk := len(future) > 0
+
+ switch {
+ case objectTypeOk && objectNameOk:
+ grantOnSchemaObject.SchemaObject = &sdk.Object{
+ ObjectType: sdk.ObjectType(objectType),
+ Name: sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(objectName),
+ }
+ case allOk:
+ grantOnSchemaObject.All = getGrantOnSchemaObjectIn(all[0].(map[string]any))
+ case futureOk:
+ grantOnSchemaObject.Future = getGrantOnSchemaObjectIn(future[0].(map[string]any))
+ }
+
+ on.SchemaObject = grantOnSchemaObject
+ }
+
+ return on
+}
+
+func getGrantOnSchemaObjectIn(allOrFuture map[string]any) *sdk.GrantOnSchemaObjectIn {
+ pluralObjectType := sdk.PluralObjectType(allOrFuture["object_type_plural"].(string))
+ grantOnSchemaObjectIn := &sdk.GrantOnSchemaObjectIn{
+ PluralObjectType: pluralObjectType,
+ }
+
+ if inDatabase, ok := allOrFuture["in_database"].(string); ok && len(inDatabase) > 0 {
+ grantOnSchemaObjectIn.InDatabase = sdk.Pointer(sdk.NewAccountObjectIdentifierFromFullyQualifiedName(inDatabase))
+ }
+
+ if inSchema, ok := allOrFuture["in_schema"].(string); ok && len(inSchema) > 0 {
+ grantOnSchemaObjectIn.InSchema = sdk.Pointer(sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(inSchema))
+ }
+
+ return grantOnSchemaObjectIn
+}
+
+func createGrantPrivilegesToDatabaseRoleIdFromSchema(d *schema.ResourceData) *GrantPrivilegesToDatabaseRoleId {
+ id := new(GrantPrivilegesToDatabaseRoleId)
+ id.DatabaseRoleName = sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(d.Get("database_role_name").(string))
+ id.AllPrivileges = d.Get("all_privileges").(bool)
+ if p, ok := d.GetOk("privileges"); ok {
+ id.Privileges = expandStringList(p.(*schema.Set).List())
+ }
+ id.WithGrantOption = d.Get("with_grant_option").(bool)
+
+ on := getDatabaseRoleGrantOn(d)
+ switch {
+ case on.Database != nil:
+ id.Kind = OnDatabaseDatabaseRoleGrantKind
+ id.Data = &OnDatabaseGrantData{
+ DatabaseName: *on.Database,
+ }
+ case on.Schema != nil:
+ onSchemaGrantData := new(OnSchemaGrantData)
+
+ switch {
+ case on.Schema.Schema != nil:
+ onSchemaGrantData.Kind = OnSchemaSchemaGrantKind
+ onSchemaGrantData.SchemaName = on.Schema.Schema
+ case on.Schema.AllSchemasInDatabase != nil:
+ onSchemaGrantData.Kind = OnAllSchemasInDatabaseSchemaGrantKind
+ onSchemaGrantData.DatabaseName = on.Schema.AllSchemasInDatabase
+ case on.Schema.FutureSchemasInDatabase != nil:
+ onSchemaGrantData.Kind = OnFutureSchemasInDatabaseSchemaGrantKind
+ onSchemaGrantData.DatabaseName = on.Schema.FutureSchemasInDatabase
+ }
+
+ id.Kind = OnSchemaDatabaseRoleGrantKind
+ id.Data = onSchemaGrantData
+ case on.SchemaObject != nil:
+ onSchemaObjectGrantData := new(OnSchemaObjectGrantData)
+
+ switch {
+ case on.SchemaObject.SchemaObject != nil:
+ onSchemaObjectGrantData.Kind = OnObjectSchemaObjectGrantKind
+ onSchemaObjectGrantData.Object = on.SchemaObject.SchemaObject
+ case on.SchemaObject.All != nil:
+ onSchemaObjectGrantData.Kind = OnAllSchemaObjectGrantKind
+ onSchemaObjectGrantData.OnAllOrFuture = getBulkOperationGrantData(on.SchemaObject.All)
+ case on.SchemaObject.Future != nil:
+ onSchemaObjectGrantData.Kind = OnFutureSchemaObjectGrantKind
+ onSchemaObjectGrantData.OnAllOrFuture = getBulkOperationGrantData(on.SchemaObject.Future)
+ }
+
+ id.Kind = OnSchemaObjectDatabaseRoleGrantKind
+ id.Data = onSchemaObjectGrantData
+ }
+
+ return id
+}
+
+func getBulkOperationGrantData(in *sdk.GrantOnSchemaObjectIn) *BulkOperationGrantData {
+ bulkOperationGrantData := &BulkOperationGrantData{
+ ObjectNamePlural: in.PluralObjectType,
+ }
+
+ if in.InDatabase != nil {
+ bulkOperationGrantData.Kind = InDatabaseBulkOperationGrantKind
+ bulkOperationGrantData.Database = in.InDatabase
+ }
+
+ if in.InSchema != nil {
+ bulkOperationGrantData.Kind = InSchemaBulkOperationGrantKind
+ bulkOperationGrantData.Schema = in.InSchema
+ }
+
+ return bulkOperationGrantData
+}
diff --git a/pkg/resources/grant_privileges_to_database_role_acceptance_test.go b/pkg/resources/grant_privileges_to_database_role_acceptance_test.go
new file mode 100644
index 0000000000..451c66f048
--- /dev/null
+++ b/pkg/resources/grant_privileges_to_database_role_acceptance_test.go
@@ -0,0 +1,852 @@
+package resources_test
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "log"
+ "regexp"
+ "slices"
+ "testing"
+
+ acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-testing/config"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/plancheck"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+ "github.com/hashicorp/terraform-plugin-testing/tfversion"
+)
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnDatabase(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.AccountObjectPrivilegeCreateSchema)),
+ config.StringVariable(string(sdk.AccountObjectPrivilegeModify)),
+ config.StringVariable(string(sdk.AccountObjectPrivilegeUsage)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "with_grant_option": config.BoolVariable(true),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "3"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.AccountObjectPrivilegeCreateSchema)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.AccountObjectPrivilegeModify)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.2", string(sdk.AccountObjectPrivilegeUsage)),
+ resource.TestCheckResourceAttr(resourceName, "on_database", databaseName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "true"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|true|false|CREATE SCHEMA,MODIFY,USAGE|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnDatabase_PrivilegesReversed(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.AccountObjectPrivilegeUsage)),
+ config.StringVariable(string(sdk.AccountObjectPrivilegeModify)),
+ config.StringVariable(string(sdk.AccountObjectPrivilegeCreateSchema)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "with_grant_option": config.BoolVariable(true),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "3"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.AccountObjectPrivilegeCreateSchema)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.AccountObjectPrivilegeModify)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.2", string(sdk.AccountObjectPrivilegeUsage)),
+ resource.TestCheckResourceAttr(resourceName, "on_database", databaseName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "true"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|true|false|CREATE SCHEMA,MODIFY,USAGE|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnSchema(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.SchemaPrivilegeCreateTable)),
+ config.StringVariable(string(sdk.SchemaPrivilegeModify)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "schema": config.StringVariable(acc.TestSchemaName),
+ "with_grant_option": config.BoolVariable(false),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ schemaName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchema"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaPrivilegeCreateTable)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.SchemaPrivilegeModify)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema.0.schema_name", schemaName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|CREATE TABLE,MODIFY|OnSchema|OnSchema|%s", databaseRoleName, schemaName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchema"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnSchema_ExactlyOneOf(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchema_ExactlyOneOf"),
+ PlanOnly: true,
+ ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"),
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnAllSchemasInDatabase(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.SchemaPrivilegeCreateTable)),
+ config.StringVariable(string(sdk.SchemaPrivilegeModify)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "with_grant_option": config.BoolVariable(false),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaPrivilegeCreateTable)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.SchemaPrivilegeModify)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema.0.all_schemas_in_database", databaseName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|CREATE TABLE,MODIFY|OnSchema|OnAllSchemasInDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnFutureSchemasInDatabase(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.SchemaPrivilegeCreateTable)),
+ config.StringVariable(string(sdk.SchemaPrivilegeModify)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "with_grant_option": config.BoolVariable(false),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaPrivilegeCreateTable)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.SchemaPrivilegeModify)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema.0.future_schemas_in_database", databaseName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|CREATE TABLE,MODIFY|OnSchema|OnFutureSchemasInDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnSchemaObject_OnObject(t *testing.T) {
+ name := "test_database_role_name"
+ tblName := "test_database_role_table_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "table_name": config.StringVariable(tblName),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.SchemaObjectPrivilegeInsert)),
+ config.StringVariable(string(sdk.SchemaObjectPrivilegeUpdate)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "schema": config.StringVariable(acc.TestSchemaName),
+ "with_grant_option": config.BoolVariable(false),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ tableName := sdk.NewSchemaObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName, tblName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaObjectPrivilegeInsert)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.SchemaObjectPrivilegeUpdate)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.object_type", string(sdk.ObjectTypeTable)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.object_name", tableName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|INSERT,UPDATE|OnSchemaObject|OnObject|TABLE|%s", databaseRoleName, tableName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnSchemaObject_OnObject_OwnershipPrivilege(t *testing.T) {
+ name := "test_database_role_name"
+ tableName := "test_database_role_table_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "table_name": config.StringVariable(tableName),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.SchemaObjectOwnership)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "schema": config.StringVariable(acc.TestSchemaName),
+ "with_grant_option": config.BoolVariable(false),
+ }
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject"),
+ ConfigVariables: configVariables,
+ ExpectError: regexp.MustCompile("Unsupported privilege 'OWNERSHIP'"),
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnSchemaObject_OnAll_InDatabase(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.SchemaObjectPrivilegeInsert)),
+ config.StringVariable(string(sdk.SchemaObjectPrivilegeUpdate)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "with_grant_option": config.BoolVariable(false),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaObjectPrivilegeInsert)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.SchemaObjectPrivilegeUpdate)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.object_type_plural", string(sdk.PluralObjectTypeTables)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.in_database", databaseName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|INSERT,UPDATE|OnSchemaObject|OnAll|TABLES|InDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_OnSchemaObject_OnFuture_InDatabase(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "privileges": config.ListVariable(
+ config.StringVariable(string(sdk.SchemaObjectPrivilegeInsert)),
+ config.StringVariable(string(sdk.SchemaObjectPrivilegeUpdate)),
+ ),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "with_grant_option": config.BoolVariable(false),
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase"),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaObjectPrivilegeInsert)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.SchemaObjectPrivilegeUpdate)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.future.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.future.0.object_type_plural", string(sdk.PluralObjectTypeTables)),
+ resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.future.0.in_database", databaseName),
+ resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|INSERT,UPDATE|OnSchemaObject|OnFuture|TABLES|InDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase"),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_UpdatePrivileges(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := func(allPrivileges bool, privileges []sdk.AccountObjectPrivilege) config.Variables {
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ }
+ if allPrivileges {
+ configVariables["all_privileges"] = config.BoolVariable(allPrivileges)
+ }
+ if len(privileges) > 0 {
+ configPrivileges := make([]config.Variable, len(privileges))
+ for i, privilege := range privileges {
+ configPrivileges[i] = config.StringVariable(string(privilege))
+ }
+ configVariables["privileges"] = config.ListVariable(configPrivileges...)
+ }
+ return configVariables
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges"),
+ ConfigVariables: configVariables(false, []sdk.AccountObjectPrivilege{
+ sdk.AccountObjectPrivilegeCreateSchema,
+ sdk.AccountObjectPrivilegeModify,
+ }),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "all_privileges", "false"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.AccountObjectPrivilegeCreateSchema)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.AccountObjectPrivilegeModify)),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|CREATE SCHEMA,MODIFY|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges"),
+ ConfigVariables: configVariables(false, []sdk.AccountObjectPrivilege{
+ sdk.AccountObjectPrivilegeCreateSchema,
+ sdk.AccountObjectPrivilegeMonitor,
+ sdk.AccountObjectPrivilegeUsage,
+ }),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "all_privileges", "false"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "3"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.AccountObjectPrivilegeCreateSchema)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.AccountObjectPrivilegeMonitor)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.2", string(sdk.AccountObjectPrivilegeUsage)),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|CREATE SCHEMA,USAGE,MONITOR|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/all_privileges"),
+ ConfigVariables: configVariables(true, []sdk.AccountObjectPrivilege{}),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "all_privileges", "true"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "0"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|ALL|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges"),
+ ConfigVariables: configVariables(false, []sdk.AccountObjectPrivilege{
+ sdk.AccountObjectPrivilegeModify,
+ sdk.AccountObjectPrivilegeMonitor,
+ }),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "all_privileges", "false"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.AccountObjectPrivilegeModify)),
+ resource.TestCheckResourceAttr(resourceName, "privileges.1", string(sdk.AccountObjectPrivilegeMonitor)),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|MODIFY,MONITOR|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_UpdatePrivileges_SnowflakeChecked(t *testing.T) {
+ name := "test_database_role_name"
+ schemaName := "test_database_role_schema_name"
+ configVariables := func(allPrivileges bool, privileges []string, schemaName string) config.Variables {
+ configVariables := config.Variables{
+ "name": config.StringVariable(name),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ }
+ if allPrivileges {
+ configVariables["all_privileges"] = config.BoolVariable(allPrivileges)
+ }
+ if len(privileges) > 0 {
+ configPrivileges := make([]config.Variable, len(privileges))
+ for i, privilege := range privileges {
+ configPrivileges[i] = config.StringVariable(privilege)
+ }
+ configVariables["privileges"] = config.ListVariable(configPrivileges...)
+ }
+ if len(schemaName) > 0 {
+ configVariables["schema_name"] = config.StringVariable(schemaName)
+ }
+ return configVariables
+ }
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges"),
+ ConfigVariables: configVariables(false, []string{
+ sdk.AccountObjectPrivilegeCreateSchema.String(),
+ sdk.AccountObjectPrivilegeModify.String(),
+ }, ""),
+ Check: queriedPrivilegesEqualTo(
+ databaseRoleName,
+ sdk.AccountObjectPrivilegeCreateSchema.String(),
+ sdk.AccountObjectPrivilegeModify.String(),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/all_privileges"),
+ ConfigVariables: configVariables(true, []string{}, ""),
+ Check: queriedPrivilegesContainAtLeast(
+ databaseRoleName,
+ sdk.AccountObjectPrivilegeCreateDatabaseRole.String(),
+ sdk.AccountObjectPrivilegeCreateSchema.String(),
+ sdk.AccountObjectPrivilegeModify.String(),
+ sdk.AccountObjectPrivilegeMonitor.String(),
+ sdk.AccountObjectPrivilegeUsage.String(),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges"),
+ ConfigVariables: configVariables(false, []string{
+ sdk.AccountObjectPrivilegeModify.String(),
+ sdk.AccountObjectPrivilegeMonitor.String(),
+ }, ""),
+ Check: queriedPrivilegesEqualTo(
+ databaseRoleName,
+ sdk.AccountObjectPrivilegeModify.String(),
+ sdk.AccountObjectPrivilegeMonitor.String(),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/on_schema"),
+ ConfigVariables: configVariables(false, []string{
+ sdk.SchemaPrivilegeCreateTask.String(),
+ sdk.SchemaPrivilegeCreateExternalTable.String(),
+ }, schemaName),
+ Check: queriedPrivilegesEqualTo(
+ databaseRoleName,
+ sdk.SchemaPrivilegeCreateTask.String(),
+ sdk.SchemaPrivilegeCreateExternalTable.String(),
+ ),
+ },
+ },
+ })
+}
+
+func TestAcc_GrantPrivilegesToDatabaseRole_AlwaysApply(t *testing.T) {
+ name := "test_database_role_name"
+ configVariables := func(alwaysApply bool) config.Variables {
+ return config.Variables{
+ "name": config.StringVariable(name),
+ "all_privileges": config.BoolVariable(true),
+ "database": config.StringVariable(acc.TestDatabaseName),
+ "always_apply": config.BoolVariable(alwaysApply),
+ }
+ }
+ resourceName := "snowflake_grant_privileges_to_database_role.test"
+
+ databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName()
+ databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked,
+ Steps: []resource.TestStep{
+ {
+ PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply"),
+ ConfigVariables: configVariables(false),
+ ConfigPlanChecks: resource.ConfigPlanChecks{
+ PostApplyPostRefresh: []plancheck.PlanCheck{
+ plancheck.ExpectEmptyPlan(),
+ },
+ },
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "always_apply", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|ALL|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply"),
+ ConfigVariables: configVariables(true),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "always_apply", "true"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|true|ALL|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply"),
+ ConfigVariables: configVariables(true),
+ ConfigPlanChecks: resource.ConfigPlanChecks{
+ PreApply: []plancheck.PlanCheck{
+ plancheck.ExpectNonEmptyPlan(),
+ },
+ },
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "always_apply", "true"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|true|ALL|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply"),
+ ConfigVariables: configVariables(true),
+ ConfigPlanChecks: resource.ConfigPlanChecks{
+ PreApply: []plancheck.PlanCheck{
+ plancheck.ExpectNonEmptyPlan(),
+ },
+ },
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "always_apply", "true"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|true|ALL|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ ExpectNonEmptyPlan: true,
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply"),
+ ConfigVariables: configVariables(false),
+ ConfigPlanChecks: resource.ConfigPlanChecks{
+ PostApplyPostRefresh: []plancheck.PlanCheck{
+ plancheck.ExpectEmptyPlan(),
+ },
+ },
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "always_apply", "false"),
+ resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|ALL|OnDatabase|%s", databaseRoleName, databaseName)),
+ ),
+ },
+ },
+ })
+}
+
+func createDatabaseRoleOutsideTerraform(t *testing.T, name string) {
+ t.Helper()
+ client, err := sdk.NewDefaultClient()
+ if err != nil {
+ t.Fatal(err)
+ }
+ ctx := context.Background()
+ databaseRoleId := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name)
+ if err := client.DatabaseRoles.Create(ctx, sdk.NewCreateDatabaseRoleRequest(databaseRoleId).WithOrReplace(true)); err != nil {
+ t.Fatal(fmt.Errorf("error database role (%s): %w", databaseRoleId.FullyQualifiedName(), err))
+ }
+}
+
+// queriedPrivilegesEqualTo will check if all the privileges specified in the argument are granted in Snowflake.
+// Any additional grants (other than usage and ownership) will be treated as an error.
+func queriedPrivilegesEqualTo(databaseRoleName sdk.DatabaseObjectIdentifier, privileges ...string) func(s *terraform.State) error {
+ return func(s *terraform.State) error {
+ db := acc.TestAccProvider.Meta().(*sql.DB)
+ ctx := context.Background()
+ client := sdk.NewClientFromDB(db)
+ grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{
+ To: &sdk.ShowGrantsTo{
+ DatabaseRole: databaseRoleName,
+ },
+ })
+ if err != nil {
+ return err
+ }
+ for _, grant := range grants {
+ if grant.Privilege == "USAGE" || grant.Privilege == "OWNERSHIP" {
+ log.Printf("Skipping check for %s privilege as its one of the privileges that are implicitly granted by Snowflake", grant.Privilege)
+ continue
+ }
+ if !slices.Contains(privileges, grant.Privilege) {
+ return fmt.Errorf("grant not expected, grant: %v, not in %v", grants, privileges)
+ }
+ }
+
+ return nil
+ }
+}
+
+// queriedPrivilegesContainAtLeast will check if all the privileges specified in the argument are granted in Snowflake.
+// Any additional grants will be ignored.
+func queriedPrivilegesContainAtLeast(databaseRoleName sdk.DatabaseObjectIdentifier, privileges ...string) func(s *terraform.State) error {
+ return func(s *terraform.State) error {
+ db := acc.TestAccProvider.Meta().(*sql.DB)
+ ctx := context.Background()
+ client := sdk.NewClientFromDB(db)
+ grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{
+ To: &sdk.ShowGrantsTo{
+ DatabaseRole: databaseRoleName,
+ },
+ })
+ if err != nil {
+ return err
+ }
+ var grantedPrivileges []string
+ for _, grant := range grants {
+ grantedPrivileges = append(grantedPrivileges, grant.Privilege)
+ }
+ notAllPrivilegesInGrantedPrivileges := slices.ContainsFunc(privileges, func(privilege string) bool {
+ return !slices.Contains(grantedPrivileges, privilege)
+ })
+ if notAllPrivilegesInGrantedPrivileges {
+ return fmt.Errorf("not every privilege from the list: %v was found in grant privileges: %v, for database role name: %s", privileges, grantedPrivileges, databaseRoleName.FullyQualifiedName())
+ }
+
+ return nil
+ }
+}
+
+func testAccCheckDatabaseRolePrivilegesRevoked(s *terraform.State) error {
+ db := acc.TestAccProvider.Meta().(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "snowflake_grant_privileges_to_database_role" {
+ continue
+ }
+ ctx := context.Background()
+
+ id := sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(rs.Primary.Attributes["database_role_name"])
+ grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{
+ To: &sdk.ShowGrantsTo{
+ DatabaseRole: id,
+ },
+ })
+ if err != nil {
+ return err
+ }
+ var grantedPrivileges []string
+ for _, grant := range grants {
+ // usage is the default privilege available after creation (it won't be revoked)
+ if grant.Privilege != "USAGE" {
+ grantedPrivileges = append(grantedPrivileges, grant.Privilege)
+ }
+ }
+ if len(grantedPrivileges) > 0 {
+ return fmt.Errorf("database role (%s) still grants , granted privileges %v", id.FullyQualifiedName(), grantedPrivileges)
+ }
+ }
+ return nil
+}
diff --git a/pkg/resources/grant_privileges_to_database_role_identifier.go b/pkg/resources/grant_privileges_to_database_role_identifier.go
new file mode 100644
index 0000000000..f297e968d8
--- /dev/null
+++ b/pkg/resources/grant_privileges_to_database_role_identifier.go
@@ -0,0 +1,234 @@
+package resources
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+)
+
+type DatabaseRoleGrantKind string
+
+const (
+ OnDatabaseDatabaseRoleGrantKind DatabaseRoleGrantKind = "OnDatabase"
+ OnSchemaDatabaseRoleGrantKind DatabaseRoleGrantKind = "OnSchema"
+ OnSchemaObjectDatabaseRoleGrantKind DatabaseRoleGrantKind = "OnSchemaObject"
+)
+
+type OnSchemaGrantKind string
+
+const (
+ OnSchemaSchemaGrantKind OnSchemaGrantKind = "OnSchema"
+ OnAllSchemasInDatabaseSchemaGrantKind OnSchemaGrantKind = "OnAllSchemasInDatabase"
+ OnFutureSchemasInDatabaseSchemaGrantKind OnSchemaGrantKind = "OnFutureSchemasInDatabase"
+)
+
+type OnSchemaObjectGrantKind string
+
+const (
+ OnObjectSchemaObjectGrantKind OnSchemaObjectGrantKind = "OnObject"
+ OnAllSchemaObjectGrantKind OnSchemaObjectGrantKind = "OnAll"
+ OnFutureSchemaObjectGrantKind OnSchemaObjectGrantKind = "OnFuture"
+)
+
+type GrantPrivilegesToDatabaseRoleId struct {
+ DatabaseRoleName sdk.DatabaseObjectIdentifier
+ WithGrantOption bool
+ AlwaysApply bool
+ AllPrivileges bool
+ Privileges []string
+ Kind DatabaseRoleGrantKind
+ Data fmt.Stringer
+}
+
+func (g *GrantPrivilegesToDatabaseRoleId) String() string {
+ var parts []string
+ parts = append(parts, g.DatabaseRoleName.FullyQualifiedName())
+ parts = append(parts, strconv.FormatBool(g.WithGrantOption))
+ parts = append(parts, strconv.FormatBool(g.AlwaysApply))
+ if g.AllPrivileges {
+ parts = append(parts, "ALL")
+ } else {
+ parts = append(parts, strings.Join(g.Privileges, ","))
+ }
+ parts = append(parts, string(g.Kind))
+ parts = append(parts, g.Data.String())
+ return strings.Join(parts, helpers.IDDelimiter)
+}
+
+type OnDatabaseGrantData struct {
+ DatabaseName sdk.AccountObjectIdentifier
+}
+
+func (d *OnDatabaseGrantData) String() string {
+ return d.DatabaseName.FullyQualifiedName()
+}
+
+type OnSchemaGrantData struct {
+ Kind OnSchemaGrantKind
+ SchemaName *sdk.DatabaseObjectIdentifier
+ DatabaseName *sdk.AccountObjectIdentifier
+}
+
+func (d *OnSchemaGrantData) String() string {
+ var parts []string
+ parts = append(parts, string(d.Kind))
+ switch d.Kind {
+ case OnSchemaSchemaGrantKind:
+ parts = append(parts, d.SchemaName.FullyQualifiedName())
+ case OnAllSchemasInDatabaseSchemaGrantKind, OnFutureSchemasInDatabaseSchemaGrantKind:
+ parts = append(parts, d.DatabaseName.FullyQualifiedName())
+ }
+ return strings.Join(parts, helpers.IDDelimiter)
+}
+
+type OnSchemaObjectGrantData struct {
+ Kind OnSchemaObjectGrantKind
+ Object *sdk.Object
+ OnAllOrFuture *BulkOperationGrantData
+}
+
+func (d *OnSchemaObjectGrantData) String() string {
+ var parts []string
+ parts = append(parts, string(d.Kind))
+ switch d.Kind {
+ case OnObjectSchemaObjectGrantKind:
+ parts = append(parts, fmt.Sprintf("%s|%s", d.Object.ObjectType, d.Object.Name.FullyQualifiedName()))
+ case OnAllSchemaObjectGrantKind, OnFutureSchemaObjectGrantKind:
+ parts = append(parts, d.OnAllOrFuture.ObjectNamePlural.String())
+ parts = append(parts, string(d.OnAllOrFuture.Kind))
+ switch d.OnAllOrFuture.Kind {
+ case InDatabaseBulkOperationGrantKind:
+ parts = append(parts, d.OnAllOrFuture.Database.FullyQualifiedName())
+ case InSchemaBulkOperationGrantKind:
+ parts = append(parts, d.OnAllOrFuture.Schema.FullyQualifiedName())
+ }
+ }
+ return strings.Join(parts, helpers.IDDelimiter)
+}
+
+type BulkOperationGrantKind string
+
+const (
+ InDatabaseBulkOperationGrantKind BulkOperationGrantKind = "InDatabase"
+ InSchemaBulkOperationGrantKind BulkOperationGrantKind = "InSchema"
+)
+
+type BulkOperationGrantData struct {
+ ObjectNamePlural sdk.PluralObjectType
+ Kind BulkOperationGrantKind
+ Database *sdk.AccountObjectIdentifier
+ Schema *sdk.DatabaseObjectIdentifier
+}
+
+func ParseGrantPrivilegesToDatabaseRoleId(id string) (GrantPrivilegesToDatabaseRoleId, error) {
+ var databaseRoleId GrantPrivilegesToDatabaseRoleId
+
+ parts := strings.Split(id, helpers.IDDelimiter)
+ if len(parts) < 6 {
+ return databaseRoleId, sdk.NewError(`database role identifier should hold at least 5 parts "|||||"`)
+ }
+
+ // TODO: Identifier parsing should be replaced with better version introduced in SNOW-999049.
+ // Right now, it's same as sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName, but with error handling.
+ databaseRoleNameParts := strings.Split(parts[0], ".")
+ if len(databaseRoleNameParts) == 0 ||
+ (len(databaseRoleNameParts) == 1 && databaseRoleNameParts[0] == "") ||
+ (len(databaseRoleNameParts) == 2 && databaseRoleNameParts[1] == "") ||
+ len(databaseRoleNameParts) > 2 {
+ return databaseRoleId, sdk.NewError(fmt.Sprintf(`invalid DatabaseRoleName value: %s, should be a fully qualified name of database object .`, parts[0]))
+ }
+ databaseRoleId.DatabaseRoleName = sdk.NewDatabaseObjectIdentifier(
+ strings.Trim(databaseRoleNameParts[0], `"`),
+ strings.Trim(databaseRoleNameParts[1], `"`),
+ )
+
+ if parts[1] != "false" && parts[1] != "true" {
+ return databaseRoleId, sdk.NewError(fmt.Sprintf(`invalid WithGrantOption value: %s, should be either "true" or "false"`, parts[1]))
+ }
+ databaseRoleId.WithGrantOption = parts[1] == "true"
+
+ if parts[2] != "false" && parts[2] != "true" {
+ return databaseRoleId, sdk.NewError(fmt.Sprintf(`invalid AlwaysApply value: %s, should be either "true" or "false"`, parts[2]))
+ }
+ databaseRoleId.AlwaysApply = parts[2] == "true"
+
+ privileges := strings.Split(parts[3], ",")
+ if len(privileges) == 0 || (len(privileges) == 1 && privileges[0] == "") {
+ return databaseRoleId, sdk.NewError(fmt.Sprintf(`invalid Privileges value: %s, should be either a comma separated list of privileges or "ALL" / "ALL PRIVILEGES" for all privileges`, parts[3]))
+ }
+ if len(privileges) == 1 && (privileges[0] == "ALL" || privileges[0] == "ALL PRIVILEGES") {
+ databaseRoleId.AllPrivileges = true
+ } else {
+ databaseRoleId.Privileges = privileges
+ }
+ databaseRoleId.Kind = DatabaseRoleGrantKind(parts[4])
+
+ switch databaseRoleId.Kind {
+ case OnDatabaseDatabaseRoleGrantKind:
+ databaseRoleId.Data = &OnDatabaseGrantData{
+ DatabaseName: sdk.NewAccountObjectIdentifierFromFullyQualifiedName(parts[5]),
+ }
+ case OnSchemaDatabaseRoleGrantKind:
+ if len(parts) < 7 {
+ return databaseRoleId, sdk.NewError(`database role identifier should hold at least 7 parts "||||||..."`)
+ }
+ onSchemaGrantData := OnSchemaGrantData{
+ Kind: OnSchemaGrantKind(parts[5]),
+ }
+ switch onSchemaGrantData.Kind {
+ case OnSchemaSchemaGrantKind:
+ onSchemaGrantData.SchemaName = sdk.Pointer(sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(parts[6]))
+ case OnAllSchemasInDatabaseSchemaGrantKind, OnFutureSchemasInDatabaseSchemaGrantKind:
+ onSchemaGrantData.DatabaseName = sdk.Pointer(sdk.NewAccountObjectIdentifier(parts[6]))
+ default:
+ return databaseRoleId, sdk.NewError(fmt.Sprintf("invalid OnSchemaGrantKind: %s", onSchemaGrantData.Kind))
+ }
+ databaseRoleId.Data = &onSchemaGrantData
+ case OnSchemaObjectDatabaseRoleGrantKind:
+ if len(parts) < 7 {
+ return databaseRoleId, sdk.NewError(`database role identifier should hold at least 7 parts "||||||..."`)
+ }
+ onSchemaObjectGrantData := OnSchemaObjectGrantData{
+ Kind: OnSchemaObjectGrantKind(parts[5]),
+ }
+ switch onSchemaObjectGrantData.Kind {
+ case OnObjectSchemaObjectGrantKind:
+ if len(parts) != 8 {
+ return databaseRoleId, sdk.NewError(`database role identifier should hold 8 parts "||||OnSchemaObject|OnObject||"`)
+ }
+ onSchemaObjectGrantData.Object = &sdk.Object{
+ ObjectType: sdk.ObjectType(parts[6]),
+ Name: sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(parts[7]),
+ }
+ case OnAllSchemaObjectGrantKind, OnFutureSchemaObjectGrantKind:
+ bulkOperationGrantData := &BulkOperationGrantData{
+ ObjectNamePlural: sdk.PluralObjectType(parts[6]),
+ }
+ if len(parts) > 7 {
+ if len(parts) != 9 {
+ return databaseRoleId, sdk.NewError(`database role identifier should hold 9 parts "||||OnSchemaObject|On[All or Future]||In[Database or Schema]|"`)
+ }
+ bulkOperationGrantData.Kind = BulkOperationGrantKind(parts[7])
+ switch bulkOperationGrantData.Kind {
+ case InDatabaseBulkOperationGrantKind:
+ bulkOperationGrantData.Database = sdk.Pointer(sdk.NewAccountObjectIdentifierFromFullyQualifiedName(parts[8]))
+ case InSchemaBulkOperationGrantKind:
+ bulkOperationGrantData.Schema = sdk.Pointer(sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(parts[8]))
+ default:
+ return databaseRoleId, sdk.NewError(fmt.Sprintf("invalid BulkOperationGrantKind: %s", bulkOperationGrantData.Kind))
+ }
+ }
+ onSchemaObjectGrantData.OnAllOrFuture = bulkOperationGrantData
+ default:
+ return databaseRoleId, sdk.NewError(fmt.Sprintf("invalid OnSchemaObjectGrantKind: %s", onSchemaObjectGrantData.Kind))
+ }
+ databaseRoleId.Data = &onSchemaObjectGrantData
+ default:
+ return databaseRoleId, sdk.NewError(fmt.Sprintf("invalid DatabaseRoleGrantKind: %s", databaseRoleId.Kind))
+ }
+
+ return databaseRoleId, nil
+}
diff --git a/pkg/resources/grant_privileges_to_database_role_identifier_test.go b/pkg/resources/grant_privileges_to_database_role_identifier_test.go
new file mode 100644
index 0000000000..6d6668816b
--- /dev/null
+++ b/pkg/resources/grant_privileges_to_database_role_identifier_test.go
@@ -0,0 +1,429 @@
+package resources
+
+import (
+ "testing"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParseGrantPrivilegesToDatabaseRoleId(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Identifier string
+ Expected GrantPrivilegesToDatabaseRoleId
+ Error string
+ }{
+ {
+ Name: "grant database role on database",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnDatabase|"on-database-name"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnDatabaseDatabaseRoleGrantKind,
+ Data: &OnDatabaseGrantData{
+ DatabaseName: sdk.NewAccountObjectIdentifier("on-database-name"),
+ },
+ },
+ },
+ {
+ Name: "grant database role on database - always apply with grant option",
+ Identifier: `"database-name"."database-role"|true|true|CREATE SCHEMA,USAGE,MONITOR|OnDatabase|"on-database-name"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: true,
+ AlwaysApply: true,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnDatabaseDatabaseRoleGrantKind,
+ Data: &OnDatabaseGrantData{
+ DatabaseName: sdk.NewAccountObjectIdentifier("on-database-name"),
+ },
+ },
+ },
+ {
+ Name: "grant database role on database - all privileges",
+ Identifier: `"database-name"."database-role"|false|false|ALL|OnDatabase|"on-database-name"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ AllPrivileges: true,
+ Privileges: nil,
+ Kind: OnDatabaseDatabaseRoleGrantKind,
+ Data: &OnDatabaseGrantData{
+ DatabaseName: sdk.NewAccountObjectIdentifier("on-database-name"),
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema with schema name",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|OnSchema|"database-name"."schema-name"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaDatabaseRoleGrantKind,
+ Data: &OnSchemaGrantData{
+ Kind: OnSchemaSchemaGrantKind,
+ SchemaName: sdk.Pointer(sdk.NewDatabaseObjectIdentifier("database-name", "schema-name")),
+ },
+ },
+ },
+ {
+ Name: "grant database role on all schemas in database",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|OnAllSchemasInDatabase|"database-name-123"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaDatabaseRoleGrantKind,
+ Data: &OnSchemaGrantData{
+ Kind: OnAllSchemasInDatabaseSchemaGrantKind,
+ DatabaseName: sdk.Pointer(sdk.NewAccountObjectIdentifier("database-name-123")),
+ },
+ },
+ },
+ {
+ Name: "grant database role on future schemas in database",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|OnFutureSchemasInDatabase|"database-name-123"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaDatabaseRoleGrantKind,
+ Data: &OnSchemaGrantData{
+ Kind: OnFutureSchemasInDatabaseSchemaGrantKind,
+ DatabaseName: sdk.Pointer(sdk.NewAccountObjectIdentifier("database-name-123")),
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema object with on object option",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnObject|TABLE|"database-name"."schema-name"."table-name"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnObjectSchemaObjectGrantKind,
+ Object: &sdk.Object{
+ ObjectType: sdk.ObjectTypeTable,
+ Name: sdk.NewSchemaObjectIdentifier("database-name", "schema-name", "table-name"),
+ },
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema object with on all option",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnAll|TABLES`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnAllSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: "TABLES",
+ },
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema object with on all option in database",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnAll|TABLES|InDatabase|"database-name-123"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnAllSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: "TABLES",
+ Kind: InDatabaseBulkOperationGrantKind,
+ Database: sdk.Pointer(sdk.NewAccountObjectIdentifier("database-name-123")),
+ },
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema object with on all option in schema",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnAll|TABLES|InSchema|"database-name"."schema-name"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnAllSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: "TABLES",
+ Kind: InSchemaBulkOperationGrantKind,
+ Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifier("database-name", "schema-name")),
+ },
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema object with on future option",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnFuture|TABLES`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnFutureSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: "TABLES",
+ },
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema object with on all option in database",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnFuture|TABLES|InDatabase|"database-name-123"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnFutureSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: "TABLES",
+ Kind: InDatabaseBulkOperationGrantKind,
+ Database: sdk.Pointer(sdk.NewAccountObjectIdentifier("database-name-123")),
+ },
+ },
+ },
+ },
+ {
+ Name: "grant database role on schema object with on all option in schema",
+ Identifier: `"database-name"."database-role"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnFuture|TABLES|InSchema|"database-name"."schema-name"`,
+ Expected: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "database-role"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnFutureSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: "TABLES",
+ Kind: InSchemaBulkOperationGrantKind,
+ Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifier("database-name", "schema-name")),
+ },
+ },
+ },
+ },
+ {
+ Name: "validation: grant database role not enough parts",
+ Identifier: `"database-name"."role-name"|false|false`,
+ Error: "database role identifier should hold at least 5 parts",
+ },
+ {
+ Name: "validation: grant database role not enough parts for OnDatabase kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnDatabase`,
+ Error: "database role identifier should hold at least 5 parts",
+ },
+ {
+ Name: "validation: grant database role not enough parts for OnSchema kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|OnAllSchemasInDatabase`,
+ Error: "database role identifier should hold at least 7 parts",
+ },
+ {
+ Name: "validation: grant database role not enough parts for OnSchemaObject kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnObject`,
+ Error: "database role identifier should hold at least 7 parts",
+ },
+ {
+ Name: "validation: grant database role not enough parts for OnSchemaObject kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnObject|TABLE`,
+ Error: "database role identifier should hold 8 parts",
+ },
+ {
+ Name: "validation: grant database role not enough parts for OnSchemaObject.InDatabase kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnAll|TABLES|InDatabase`,
+ Error: "database role identifier should hold 9 parts",
+ },
+ {
+ Name: "validation: grant database role invalid DatabaseRoleGrantKind kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|some-kind|some-data`,
+ Error: "invalid DatabaseRoleGrantKind: some-kind",
+ },
+ {
+ Name: "validation: grant database role invalid OnSchemaGrantKind kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|some-kind|some-data`,
+ Error: "invalid OnSchemaGrantKind: some-kind",
+ },
+ {
+ Name: "validation: grant database role invalid OnSchemaObjectGrantKind kind",
+ Identifier: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|some-kind|some-data`,
+ Error: "invalid OnSchemaObjectGrantKind: some-kind",
+ },
+ {
+ Name: "validation: grant database role empty privileges",
+ Identifier: `"database-name"."database-role"|false|false||OnDatabase|"on-database-name"`,
+ Error: `invalid Privileges value: , should be either a comma separated list of privileges or "ALL" / "ALL PRIVILEGES" for all privileges`,
+ },
+ {
+ Name: "validation: grant database role empty with grant option",
+ Identifier: `"database-name"."database-role"||false|ALL PRIVILEGES|OnDatabase|"on-database-name"`,
+ Error: `invalid WithGrantOption value: , should be either "true" or "false"`,
+ },
+ {
+ Name: "validation: grant database role empty always apply",
+ Identifier: `"database-name"."database-role"|false||ALL PRIVILEGES|OnDatabase|"on-database-name"`,
+ Error: `invalid AlwaysApply value: , should be either "true" or "false"`,
+ },
+ {
+ Name: "validation: grant database role empty database role name",
+ Identifier: `|false|false|ALL PRIVILEGES|OnDatabase|"on-database-name"`,
+ Error: "invalid DatabaseRoleName value: , should be a fully qualified name of database object .",
+ },
+ {
+ Name: "validation: grant database role empty type",
+ Identifier: `"database-name"."database-role"|false|false|ALL PRIVILEGES||"on-database-name"`,
+ Error: "invalid DatabaseRoleGrantKind: ",
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.Name, func(t *testing.T) {
+ id, err := ParseGrantPrivilegesToDatabaseRoleId(tt.Identifier)
+ if tt.Error == "" {
+ assert.NoError(t, err)
+ assert.Equal(t, tt.Expected, id)
+ } else {
+ assert.ErrorContains(t, err, tt.Error)
+ }
+ })
+ }
+}
+
+func TestGrantPrivilegesToDatabaseRoleIdString(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Identifier GrantPrivilegesToDatabaseRoleId
+ Expected string
+ Error string
+ }{
+ {
+ Name: "grant database role on database",
+ Identifier: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "role-name"),
+ WithGrantOption: true,
+ AllPrivileges: true,
+ Kind: OnDatabaseDatabaseRoleGrantKind,
+ AlwaysApply: true,
+ Data: &OnDatabaseGrantData{
+ DatabaseName: sdk.NewAccountObjectIdentifier("database-name"),
+ },
+ },
+ Expected: `"database-name"."role-name"|true|true|ALL|OnDatabase|"database-name"`,
+ },
+ {
+ Name: "grant database role on schema on schema",
+ Identifier: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "role-name"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaDatabaseRoleGrantKind,
+ Data: &OnSchemaGrantData{
+ Kind: OnSchemaSchemaGrantKind,
+ SchemaName: sdk.Pointer(sdk.NewDatabaseObjectIdentifier("database-name", "schema-name")),
+ },
+ },
+ Expected: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|OnSchema|"database-name"."schema-name"`,
+ },
+ {
+ Name: "grant database role on all schemas in database",
+ Identifier: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "role-name"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaDatabaseRoleGrantKind,
+ Data: &OnSchemaGrantData{
+ Kind: OnAllSchemasInDatabaseSchemaGrantKind,
+ DatabaseName: sdk.Pointer(sdk.NewAccountObjectIdentifier("database-name")),
+ },
+ },
+ Expected: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|OnAllSchemasInDatabase|"database-name"`,
+ },
+ {
+ Name: "grant database role on future schemas in database",
+ Identifier: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "role-name"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaDatabaseRoleGrantKind,
+ Data: &OnSchemaGrantData{
+ Kind: OnFutureSchemasInDatabaseSchemaGrantKind,
+ DatabaseName: sdk.Pointer(sdk.NewAccountObjectIdentifier("database-name")),
+ },
+ },
+ Expected: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchema|OnFutureSchemasInDatabase|"database-name"`,
+ },
+ {
+ Name: "grant database role on schema object on object",
+ Identifier: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "role-name"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnObjectSchemaObjectGrantKind,
+ Object: &sdk.Object{
+ ObjectType: sdk.ObjectTypeTable,
+ Name: sdk.NewSchemaObjectIdentifier("database-name", "schema-name", "table-name"),
+ },
+ },
+ },
+ Expected: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnObject|TABLE|"database-name"."schema-name"."table-name"`,
+ },
+ {
+ Name: "grant database role on schema object on all tables in database",
+ Identifier: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "role-name"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnAllSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: sdk.PluralObjectTypeTables,
+ Kind: InDatabaseBulkOperationGrantKind,
+ Database: sdk.Pointer(sdk.NewAccountObjectIdentifier("database-name")),
+ },
+ },
+ },
+ Expected: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnAll|TABLES|InDatabase|"database-name"`,
+ },
+ {
+ Name: "grant database role on schema object on all tables in schema",
+ Identifier: GrantPrivilegesToDatabaseRoleId{
+ DatabaseRoleName: sdk.NewDatabaseObjectIdentifier("database-name", "role-name"),
+ WithGrantOption: false,
+ Privileges: []string{"CREATE SCHEMA", "USAGE", "MONITOR"},
+ Kind: OnSchemaObjectDatabaseRoleGrantKind,
+ Data: &OnSchemaObjectGrantData{
+ Kind: OnAllSchemaObjectGrantKind,
+ OnAllOrFuture: &BulkOperationGrantData{
+ ObjectNamePlural: sdk.PluralObjectTypeTables,
+ Kind: InSchemaBulkOperationGrantKind,
+ Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifier("database-name", "schema-name")),
+ },
+ },
+ },
+ Expected: `"database-name"."role-name"|false|false|CREATE SCHEMA,USAGE,MONITOR|OnSchemaObject|OnAll|TABLES|InSchema|"database-name"."schema-name"`,
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.Name, func(t *testing.T) {
+ assert.Equal(t, tt.Expected, tt.Identifier.String())
+ })
+ }
+}
diff --git a/pkg/resources/grant_privileges_to_role.go b/pkg/resources/grant_privileges_to_role.go
index 8de01aa7ae..25deef01bc 100644
--- a/pkg/resources/grant_privileges_to_role.go
+++ b/pkg/resources/grant_privileges_to_role.go
@@ -40,17 +40,17 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
+ ForceNew: true,
Description: "If true, the privileges will be granted on the account.",
ConflictsWith: []string{"on_account_object", "on_schema", "on_schema_object"},
- ForceNew: true,
},
"on_account_object": {
Type: schema.TypeList,
Optional: true,
+ ForceNew: true,
MaxItems: 1,
ConflictsWith: []string{"on_account", "on_schema", "on_schema_object"},
Description: "Specifies the account object on which privileges will be granted ",
- ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"object_type": {
@@ -91,22 +91,24 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
Optional: true,
Description: "The fully qualified name of the schema.",
ConflictsWith: []string{"on_schema.0.all_schemas_in_database", "on_schema.0.future_schemas_in_database"},
- ValidateDiagFunc: IsValidIdentifier[sdk.DatabaseObjectIdentifier](),
ForceNew: true,
+ ValidateDiagFunc: IsValidIdentifier[sdk.DatabaseObjectIdentifier](),
},
"all_schemas_in_database": {
- Type: schema.TypeString,
- Optional: true,
- Description: "The fully qualified name of the database.",
- ConflictsWith: []string{"on_schema.0.schema_name", "on_schema.0.future_schemas_in_database"},
- ForceNew: true,
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "The fully qualified name of the database.",
+ ConflictsWith: []string{"on_schema.0.schema_name", "on_schema.0.future_schemas_in_database"},
+ ForceNew: true,
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
},
"future_schemas_in_database": {
- Type: schema.TypeString,
- Optional: true,
- Description: "The fully qualified name of the database.",
- ConflictsWith: []string{"on_schema.0.schema_name", "on_schema.0.all_schemas_in_database"},
- ForceNew: true,
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "The fully qualified name of the database.",
+ ConflictsWith: []string{"on_schema.0.schema_name", "on_schema.0.all_schemas_in_database"},
+ ForceNew: true,
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
},
},
},
@@ -121,45 +123,22 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"object_type": {
- Type: schema.TypeString,
- Optional: true,
- Description: "The object type of the schema object on which privileges will be granted. Valid values are: ALERT | DYNAMIC TABLE | EVENT TABLE | FILE FORMAT | FUNCTION | ICEBERG TABLE | PROCEDURE | SECRET | SEQUENCE | PIPE | MASKING POLICY | PASSWORD POLICY | ROW ACCESS POLICY | SESSION POLICY | TAG | STAGE | STREAM | TABLE | EXTERNAL TABLE | TASK | VIEW | MATERIALIZED VIEW",
- RequiredWith: []string{"on_schema_object.0.object_name"},
- ConflictsWith: []string{"on_schema_object.0.all", "on_schema_object.0.future"},
- ForceNew: true,
- ValidateFunc: validation.StringInSlice([]string{
- "ALERT",
- "DYNAMIC TABLE",
- "EVENT TABLE",
- "FILE FORMAT",
- "FUNCTION",
- "ICEBERG TABLE",
- "PROCEDURE",
- "SECRET",
- "SEQUENCE",
- "PIPE",
- "MASKING POLICY",
- "PASSWORD POLICY",
- "ROW ACCESS POLICY",
- "SESSION POLICY",
- "TAG",
- "STAGE",
- "STREAM",
- "TABLE",
- "EXTERNAL TABLE",
- "TASK",
- "VIEW",
- "MATERIALIZED VIEW",
- }, true),
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "The object type of the schema object on which privileges will be granted. Valid values are: ALERT | DYNAMIC TABLE | EVENT TABLE | FILE FORMAT | FUNCTION | ICEBERG TABLE | PROCEDURE | SECRET | SEQUENCE | PIPE | MASKING POLICY | PASSWORD POLICY | ROW ACCESS POLICY | SESSION POLICY | TAG | STAGE | STREAM | TABLE | EXTERNAL TABLE | TASK | VIEW | MATERIALIZED VIEW",
+ RequiredWith: []string{"on_schema_object.0.object_name"},
+ ConflictsWith: []string{"on_schema_object.0.all", "on_schema_object.0.future"},
+ ForceNew: true,
+ ValidateDiagFunc: ValidObjectType(),
},
"object_name": {
Type: schema.TypeString,
Optional: true,
+ ForceNew: true,
Description: "The fully qualified name of the object on which privileges will be granted.",
RequiredWith: []string{"on_schema_object.0.object_type"},
ConflictsWith: []string{"on_schema_object.0.all", "on_schema_object.0.future"},
ValidateDiagFunc: IsValidIdentifier[sdk.SchemaObjectIdentifier](),
- ForceNew: true,
},
"all": {
Type: schema.TypeList,
@@ -170,50 +149,27 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"object_type_plural": {
- Type: schema.TypeString,
- Required: true,
- Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | DYNAMIC TABLES | EVENT TABLES | FILE FORMATS | FUNCTIONS | ICEBERG TABLES | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS",
- ForceNew: true,
- ValidateFunc: validation.StringInSlice([]string{
- "ALERTS",
- "DYNAMIC TABLES",
- "EVENT TABLES",
- "FILE FORMATS",
- "FUNCTIONS",
- "ICEBERG TABLES",
- "PROCEDURES",
- "SECRETS",
- "SEQUENCES",
- "PIPES",
- "MASKING POLICIES",
- "PASSWORD POLICIES",
- "ROW ACCESS POLICIES",
- "SESSION POLICIES",
- "TAGS",
- "STAGES",
- "STREAMS",
- "TABLES",
- "EXTERNAL TABLES",
- "TASKS",
- "VIEWS",
- "MATERIALIZED VIEWS",
- }, true),
+ Type: schema.TypeString,
+ Required: true,
+ Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | DYNAMIC TABLES | EVENT TABLES | FILE FORMATS | FUNCTIONS | ICEBERG TABLES | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS",
+ ForceNew: true,
+ ValidateDiagFunc: ValidPluralObjectType(),
},
"in_database": {
Type: schema.TypeString,
Optional: true,
+ ForceNew: true,
Description: "The fully qualified name of the database.",
ConflictsWith: []string{"on_schema_object.0.all.in_schema"},
ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
- ForceNew: true,
},
"in_schema": {
Type: schema.TypeString,
Optional: true,
+ ForceNew: true,
Description: "The fully qualified name of the schema.",
ConflictsWith: []string{"on_schema_object.0.all.in_database"},
ValidateDiagFunc: IsValidIdentifier[sdk.DatabaseObjectIdentifier](),
- ForceNew: true,
},
},
},
@@ -227,34 +183,11 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"object_type_plural": {
- Type: schema.TypeString,
- Required: true,
- Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | DYNAMIC TABLES | EVENT TABLES | FILE FORMATS | FUNCTIONS | ICEBERG TABLES | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS",
- ForceNew: true,
- ValidateFunc: validation.StringInSlice([]string{
- "ALERTS",
- "DYNAMIC TABLES",
- "EVENT TABLES",
- "FILE FORMATS",
- "FUNCTIONS",
- "ICEBERG TABLES",
- "PROCEDURES",
- "SECRETS",
- "SEQUENCES",
- "PIPES",
- "MASKING POLICIES",
- "PASSWORD POLICIES",
- "ROW ACCESS POLICIES",
- "SESSION POLICIES",
- "TAGS",
- "STAGES",
- "STREAMS",
- "TABLES",
- "EXTERNAL TABLES",
- "TASKS",
- "VIEWS",
- "MATERIALIZED VIEWS",
- }, true),
+ Type: schema.TypeString,
+ Required: true,
+ Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | DYNAMIC TABLES | EVENT TABLES | FILE FORMATS | FUNCTIONS | ICEBERG TABLES | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS",
+ ForceNew: true,
+ ValidateDiagFunc: ValidPluralObjectType(),
},
"in_database": {
Type: schema.TypeString,
@@ -279,10 +212,11 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
},
},
"role_name": {
- Type: schema.TypeString,
- Required: true,
- Description: "The fully qualified name of the role to which privileges will be granted.",
- ForceNew: true,
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ Description: "The fully qualified name of the role to which privileges will be granted.",
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
},
"with_grant_option": {
Type: schema.TypeBool,
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply/test.tf
new file mode 100644
index 0000000000..4b855bb708
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply/test.tf
@@ -0,0 +1,6 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "${var.database}.${var.name}"
+ all_privileges = var.all_privileges
+ on_database = var.database
+ always_apply = var.always_apply
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply/variables.tf
new file mode 100644
index 0000000000..563945ecc7
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/AlwaysApply/variables.tf
@@ -0,0 +1,15 @@
+variable "name" {
+ type = string
+}
+
+variable "all_privileges" {
+ type = bool
+}
+
+variable "database" {
+ type = string
+}
+
+variable "always_apply" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase/test.tf
new file mode 100644
index 0000000000..ebde56ec07
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase/test.tf
@@ -0,0 +1,9 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ with_grant_option = var.with_grant_option
+
+ on_schema {
+ all_schemas_in_database = "\"${var.database}\""
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase/variables.tf
new file mode 100644
index 0000000000..0e22e903d7
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllSchemasInDatabase/variables.tf
@@ -0,0 +1,15 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "with_grant_option" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase/test.tf
new file mode 100644
index 0000000000..83113802be
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase/test.tf
@@ -0,0 +1,6 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ on_database = "\"${var.database}\""
+ with_grant_option = var.with_grant_option
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase/variables.tf
new file mode 100644
index 0000000000..0e22e903d7
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnDatabase/variables.tf
@@ -0,0 +1,15 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "with_grant_option" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase/test.tf
new file mode 100644
index 0000000000..dcecbfd504
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase/test.tf
@@ -0,0 +1,9 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ with_grant_option = var.with_grant_option
+
+ on_schema {
+ future_schemas_in_database = "\"${var.database}\""
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase/variables.tf
new file mode 100644
index 0000000000..0e22e903d7
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnFutureSchemasInDatabase/variables.tf
@@ -0,0 +1,15 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "with_grant_option" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema/test.tf
new file mode 100644
index 0000000000..ef5613bda3
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema/test.tf
@@ -0,0 +1,9 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ with_grant_option = var.with_grant_option
+
+ on_schema {
+ schema_name = "\"${var.database}\".\"${var.schema}\""
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema/variables.tf
new file mode 100644
index 0000000000..44c69f32ee
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema/variables.tf
@@ -0,0 +1,19 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "schema" {
+ type = string
+}
+
+variable "with_grant_option" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase/test.tf
new file mode 100644
index 0000000000..230a702d23
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase/test.tf
@@ -0,0 +1,12 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ with_grant_option = var.with_grant_option
+
+ on_schema_object {
+ all {
+ object_type_plural = "TABLES"
+ in_database = "\"${var.database}\""
+ }
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase/variables.tf
new file mode 100644
index 0000000000..0e22e903d7
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnAll_InDatabase/variables.tf
@@ -0,0 +1,15 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "with_grant_option" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase/test.tf
new file mode 100644
index 0000000000..3463a24a8f
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase/test.tf
@@ -0,0 +1,12 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ with_grant_option = var.with_grant_option
+
+ on_schema_object {
+ future {
+ object_type_plural = "TABLES"
+ in_database = "\"${var.database}\""
+ }
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase/variables.tf
new file mode 100644
index 0000000000..0e22e903d7
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnFuture_InDatabase/variables.tf
@@ -0,0 +1,15 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "with_grant_option" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject/test.tf
new file mode 100644
index 0000000000..2d480820ae
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject/test.tf
@@ -0,0 +1,22 @@
+resource "snowflake_table" "test" {
+ database = var.database
+ schema = var.schema
+ name = var.table_name
+
+ column {
+ name = "id"
+ type = "NUMBER(38,0)"
+ }
+}
+
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ depends_on = [snowflake_table.test]
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ with_grant_option = var.with_grant_option
+
+ on_schema_object {
+ object_type = "TABLE"
+ object_name = "\"${var.database}\".\"${var.schema}\".\"${var.table_name}\""
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject/variables.tf
new file mode 100644
index 0000000000..e779508540
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchemaObject_OnObject/variables.tf
@@ -0,0 +1,23 @@
+variable "name" {
+ type = string
+}
+
+variable "table_name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "schema" {
+ type = string
+}
+
+variable "with_grant_option" {
+ type = bool
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema_ExactlyOneOf/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema_ExactlyOneOf/test.tf
new file mode 100644
index 0000000000..aea96bd446
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnSchema_ExactlyOneOf/test.tf
@@ -0,0 +1,9 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "some_database.role_name"
+ privileges = ["USAGE"]
+
+ on_schema {
+ schema_name = "some_database.schema_name"
+ all_schemas_in_database = "some_database"
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/all_privileges/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/all_privileges/test.tf
new file mode 100644
index 0000000000..3fc26c3028
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/all_privileges/test.tf
@@ -0,0 +1,5 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ all_privileges = var.all_privileges
+ on_database = "\"${var.database}\""
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/all_privileges/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/all_privileges/variables.tf
new file mode 100644
index 0000000000..cb4441bfce
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/all_privileges/variables.tf
@@ -0,0 +1,11 @@
+variable "name" {
+ type = string
+}
+
+variable "all_privileges" {
+ type = bool
+}
+
+variable "database" {
+ type = string
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges/test.tf
new file mode 100644
index 0000000000..c1ea0cb24f
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges/test.tf
@@ -0,0 +1,5 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ on_database = "\"${var.database}\""
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges/variables.tf
new file mode 100644
index 0000000000..27eccc7883
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges/privileges/variables.tf
@@ -0,0 +1,11 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/all_privileges/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/all_privileges/test.tf
new file mode 100644
index 0000000000..3fc26c3028
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/all_privileges/test.tf
@@ -0,0 +1,5 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ all_privileges = var.all_privileges
+ on_database = "\"${var.database}\""
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/all_privileges/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/all_privileges/variables.tf
new file mode 100644
index 0000000000..cb4441bfce
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/all_privileges/variables.tf
@@ -0,0 +1,11 @@
+variable "name" {
+ type = string
+}
+
+variable "all_privileges" {
+ type = bool
+}
+
+variable "database" {
+ type = string
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/on_schema/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/on_schema/test.tf
new file mode 100644
index 0000000000..f7bd4d9f19
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/on_schema/test.tf
@@ -0,0 +1,13 @@
+resource "snowflake_schema" "test" {
+ database = var.database
+ name = var.schema_name
+}
+
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ depends_on = [snowflake_schema.test]
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ on_schema {
+ schema_name = "${var.database}.${var.schema_name}"
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/on_schema/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/on_schema/variables.tf
new file mode 100644
index 0000000000..90d9c04448
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/on_schema/variables.tf
@@ -0,0 +1,15 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
+
+variable "schema_name" {
+ type = string
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges/test.tf
new file mode 100644
index 0000000000..c1ea0cb24f
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges/test.tf
@@ -0,0 +1,5 @@
+resource "snowflake_grant_privileges_to_database_role" "test" {
+ database_role_name = "\"${var.database}\".\"${var.name}\""
+ privileges = var.privileges
+ on_database = "\"${var.database}\""
+}
diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges/variables.tf
new file mode 100644
index 0000000000..27eccc7883
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/UpdatePrivileges_SnowflakeChecked/privileges/variables.tf
@@ -0,0 +1,11 @@
+variable "name" {
+ type = string
+}
+
+variable "privileges" {
+ type = list(string)
+}
+
+variable "database" {
+ type = string
+}
diff --git a/pkg/resources/validators.go b/pkg/resources/validators.go
index a517a7e0ab..b14e122d7f 100644
--- a/pkg/resources/validators.go
+++ b/pkg/resources/validators.go
@@ -3,6 +3,7 @@ package resources
import (
"fmt"
"reflect"
+ "strings"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
@@ -108,3 +109,80 @@ func getExpectedIdentifierForm(id any) string {
}
return ""
}
+
+func ValidObjectType() schema.SchemaValidateDiagFunc {
+ return StringInSlice([]string{
+ sdk.ObjectTypeAlert.String(),
+ sdk.ObjectTypeDynamicTable.String(),
+ sdk.ObjectTypeEventTable.String(),
+ sdk.ObjectTypeFileFormat.String(),
+ sdk.ObjectTypeFunction.String(),
+ sdk.ObjectTypeProcedure.String(),
+ sdk.ObjectTypeSecret.String(),
+ sdk.ObjectTypeSequence.String(),
+ sdk.ObjectTypePipe.String(),
+ sdk.ObjectTypeMaskingPolicy.String(),
+ sdk.ObjectTypePasswordPolicy.String(),
+ sdk.ObjectTypeRowAccessPolicy.String(),
+ sdk.ObjectTypeSessionPolicy.String(),
+ sdk.ObjectTypeTag.String(),
+ sdk.ObjectTypeStage.String(),
+ sdk.ObjectTypeStream.String(),
+ sdk.ObjectTypeTable.String(),
+ sdk.ObjectTypeExternalTable.String(),
+ sdk.ObjectTypeTask.String(),
+ sdk.ObjectTypeView.String(),
+ sdk.ObjectTypeMaterializedView.String(),
+ sdk.ObjectTypeNetworkRule.String(),
+ sdk.ObjectTypePackagesPolicy.String(),
+ sdk.ObjectTypeIcebergTable.String(),
+ }, true)
+}
+
+func ValidPluralObjectType() schema.SchemaValidateDiagFunc {
+ return StringInSlice(
+ []string{
+ sdk.PluralObjectTypeAlerts.String(),
+ sdk.PluralObjectTypeDynamicTables.String(),
+ sdk.PluralObjectTypeEventTables.String(),
+ sdk.PluralObjectTypeFileFormats.String(),
+ sdk.PluralObjectTypeFunctions.String(),
+ sdk.PluralObjectTypeProcedures.String(),
+ sdk.PluralObjectTypeSecrets.String(),
+ sdk.PluralObjectTypeSequences.String(),
+ sdk.PluralObjectTypePipes.String(),
+ sdk.PluralObjectTypeMaskingPolicies.String(),
+ sdk.PluralObjectTypePasswordPolicies.String(),
+ sdk.PluralObjectTypeRowAccessPolicies.String(),
+ sdk.PluralObjectTypeSessionPolicies.String(),
+ sdk.PluralObjectTypeTags.String(),
+ sdk.PluralObjectTypeStages.String(),
+ sdk.PluralObjectTypeStreams.String(),
+ sdk.PluralObjectTypeTables.String(),
+ sdk.PluralObjectTypeExternalTables.String(),
+ sdk.PluralObjectTypeTasks.String(),
+ sdk.PluralObjectTypeViews.String(),
+ sdk.PluralObjectTypeMaterializedViews.String(),
+ sdk.PluralObjectTypeNetworkRules.String(),
+ sdk.PluralObjectTypePackagesPolicies.String(),
+ sdk.PluralObjectTypeIcebergTables.String(),
+ }, true)
+}
+
+// StringInSlice has the same implementation as validation.StringInSlice, but adapted to schema.SchemaValidateDiagFunc
+func StringInSlice(valid []string, ignoreCase bool) schema.SchemaValidateDiagFunc {
+ return func(i interface{}, path cty.Path) diag.Diagnostics {
+ v, ok := i.(string)
+ if !ok {
+ return diag.Errorf("expected type of %v to be string", path)
+ }
+
+ for _, str := range valid {
+ if v == str || (ignoreCase && strings.EqualFold(v, str)) {
+ return nil
+ }
+ }
+
+ return diag.Errorf("expected %v to be one of %q, got %s", path, valid, v)
+ }
+}
diff --git a/pkg/sdk/grants.go b/pkg/sdk/grants.go
index f4dc79fcb4..9ce8e48d93 100644
--- a/pkg/sdk/grants.go
+++ b/pkg/sdk/grants.go
@@ -97,6 +97,7 @@ type DatabaseRoleGrantPrivileges struct {
DatabasePrivileges []AccountObjectPrivilege `ddl:"-"`
SchemaPrivileges []SchemaPrivilege `ddl:"-"`
SchemaObjectPrivileges []SchemaObjectPrivilege `ddl:"-"`
+ AllPrivileges *bool `ddl:"keyword" sql:"ALL PRIVILEGES"`
}
type DatabaseRoleGrantOn struct {
diff --git a/pkg/sdk/grants_test.go b/pkg/sdk/grants_test.go
index 927c954bcd..260c6cca7e 100644
--- a/pkg/sdk/grants_test.go
+++ b/pkg/sdk/grants_test.go
@@ -351,7 +351,7 @@ func TestGrants_GrantPrivilegesToDatabaseRole(t *testing.T) {
t.Run("validation: no privileges set", func(t *testing.T) {
opts := defaultGrantsForDb()
opts.privileges = &DatabaseRoleGrantPrivileges{}
- assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges"))
+ assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges", "AllPrivileges"))
})
t.Run("validation: too many privileges set", func(t *testing.T) {
@@ -360,7 +360,7 @@ func TestGrants_GrantPrivilegesToDatabaseRole(t *testing.T) {
DatabasePrivileges: []AccountObjectPrivilege{AccountObjectPrivilegeCreateSchema},
SchemaPrivileges: []SchemaPrivilege{SchemaPrivilegeCreateAlert},
}
- assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges"))
+ assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges", "AllPrivileges"))
})
t.Run("validation: no on set", func(t *testing.T) {
@@ -480,6 +480,14 @@ func TestGrants_GrantPrivilegesToDatabaseRole(t *testing.T) {
}
assertOptsValidAndSQLEquals(t, opts, `GRANT APPLY ON FUTURE TABLES IN SCHEMA "db1"."schema1" TO DATABASE ROLE "db1"."role1"`)
})
+
+ t.Run("grant all privileges", func(t *testing.T) {
+ opts := defaultGrantsForSchemaObject()
+ opts.privileges = &DatabaseRoleGrantPrivileges{
+ AllPrivileges: Bool(true),
+ }
+ assertOptsValidAndSQLEquals(t, opts, `GRANT ALL PRIVILEGES ON TABLE "db1"."schema1"."table1" TO DATABASE ROLE "db1"."role1"`)
+ })
}
func TestGrants_RevokePrivilegesFromDatabaseRoleRole(t *testing.T) {
@@ -537,7 +545,7 @@ func TestGrants_RevokePrivilegesFromDatabaseRoleRole(t *testing.T) {
t.Run("validation: no privileges set", func(t *testing.T) {
opts := defaultGrantsForDb()
opts.privileges = &DatabaseRoleGrantPrivileges{}
- assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges"))
+ assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges", "AllPrivileges"))
})
t.Run("validation: too many privileges set", func(t *testing.T) {
@@ -546,7 +554,7 @@ func TestGrants_RevokePrivilegesFromDatabaseRoleRole(t *testing.T) {
DatabasePrivileges: []AccountObjectPrivilege{AccountObjectPrivilegeCreateSchema},
SchemaPrivileges: []SchemaPrivilege{SchemaPrivilegeCreateAlert},
}
- assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges"))
+ assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges", "AllPrivileges"))
})
t.Run("validation: nil on set", func(t *testing.T) {
diff --git a/pkg/sdk/grants_validations.go b/pkg/sdk/grants_validations.go
index cfddde6a30..339a78a5e8 100644
--- a/pkg/sdk/grants_validations.go
+++ b/pkg/sdk/grants_validations.go
@@ -160,8 +160,8 @@ func (opts *GrantPrivilegesToDatabaseRoleOptions) validate() error {
func (v *DatabaseRoleGrantPrivileges) validate() error {
var errs []error
- if !exactlyOneValueSet(v.DatabasePrivileges, v.SchemaPrivileges, v.SchemaObjectPrivileges) {
- errs = append(errs, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges"))
+ if !exactlyOneValueSet(v.DatabasePrivileges, v.SchemaPrivileges, v.SchemaObjectPrivileges, v.AllPrivileges) {
+ errs = append(errs, errExactlyOneOf("DatabaseRoleGrantPrivileges", "DatabasePrivileges", "SchemaPrivileges", "SchemaObjectPrivileges", "AllPrivileges"))
}
if valueSet(v.DatabasePrivileges) {
allowedPrivileges := []AccountObjectPrivilege{
diff --git a/pkg/sdk/object_types.go b/pkg/sdk/object_types.go
index 6b9ea5b19a..ea858b9441 100644
--- a/pkg/sdk/object_types.go
+++ b/pkg/sdk/object_types.go
@@ -60,6 +60,8 @@ const (
ObjectTypeColumn ObjectType = "COLUMN"
ObjectTypeIcebergTable ObjectType = "ICEBERG TABLE"
ObjectTypeExternalVolume ObjectType = "EXTERNAL VOLUME"
+ ObjectTypeNetworkRule ObjectType = "NETWORK RULE"
+ ObjectTypePackagesPolicy ObjectType = "PACKAGES POLICY"
)
func (o ObjectType) String() string {
@@ -112,6 +114,8 @@ func objectTypeSingularToPluralMap() map[ObjectType]PluralObjectType {
ObjectTypeStreamlit: PluralObjectTypeStreamlits,
ObjectTypeIcebergTable: PluralObjectTypeIcebergTables,
ObjectTypeExternalVolume: PluralObjectTypeExternalVolumes,
+ ObjectTypeNetworkRule: PluralObjectTypeNetworkRules,
+ ObjectTypePackagesPolicy: PluralObjectTypePackagesPolicies,
}
}
@@ -204,6 +208,8 @@ const (
PluralObjectTypeStreamlits PluralObjectType = "STREAMLITS"
PluralObjectTypeIcebergTables PluralObjectType = "ICEBERG TABLES"
PluralObjectTypeExternalVolumes PluralObjectType = "EXTERNAL VOLUMES"
+ PluralObjectTypeNetworkRules PluralObjectType = "NETWORK RULES"
+ PluralObjectTypePackagesPolicies PluralObjectType = "PACKAGES POLICIES"
)
func (p PluralObjectType) String() string {
diff --git a/templates/resources/grant_privileges_to_database_role.md.tmpl b/templates/resources/grant_privileges_to_database_role.md.tmpl
new file mode 100644
index 0000000000..6423a74cab
--- /dev/null
+++ b/templates/resources/grant_privileges_to_database_role.md.tmpl
@@ -0,0 +1,99 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}"
+subcategory: ""
+description: |-
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+---
+
+{{/* SNOW-990811 */}}
+!> **Warning** Be careful when using `always_apply` field. It will always produce a plan (even when no changes were made) and can be harmful in some setups. For more details why we decided to introduce it to go our document explaining those design decisions (coming soon).
+
+# {{.Name}} ({{.Type}})
+
+{{ .Description | trimspace }}
+
+{{ if .HasExample -}}
+## Example Usage
+
+{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}}
+{{- end }}
+
+{{ .SchemaMarkdown | trimspace }}
+
+## Import
+
+~> **Note** All the ..._name parts should be fully qualified names, e.g. for database object it is `"".""`
+~> **Note** To import all_privileges write ALL or ALL PRIVILEGES in place of ``
+
+Import is supported using the following syntax:
+
+`terraform import "|||||"`
+
+where:
+- database_role_name - fully qualified identifier
+- with_grant_option - boolean
+- always_apply - boolean
+- privileges - list of privileges, comma separated; to import all_privileges write "ALL" or "ALL PRIVILEGES"
+- grant_type - enum
+- grant_data - enum data
+
+It has varying number of parts, depending on grant_type. All the possible types are:
+
+### OnDatabase
+`terraform import "||||OnDatabase|"`
+
+### OnSchema
+
+On schema contains inner types for all options.
+
+#### OnSchema
+`terraform import "||||OnSchema|OnSchema|"`
+
+#### OnAllSchemasInDatabase
+`terraform import "||||OnSchema|OnAllSchemasInDatabase|"`
+
+#### OnFutureSchemasInDatabase
+`terraform import "||||OnSchema|OnFutureSchemasInDatabase|"`
+
+### OnSchemaObject
+
+On schema object contains inner types for all options.
+
+#### OnObject
+`terraform import "||||OnSchemaObject|OnObject||"`
+
+#### OnAll
+
+On all contains inner types for all options.
+
+##### InDatabase
+`terraform import "||||OnSchemaObject|OnAll||InDatabase|"`
+
+##### InSchema
+`terraform import "||||OnSchemaObject|OnAll||InSchema|"`
+
+#### OnFuture
+
+On future contains inner types for all options.
+
+##### InDatabase
+`terraform import "||||OnSchemaObject|OnFuture||InDatabase|"`
+
+##### InSchema
+`terraform import "||||OnSchemaObject|OnFuture||InSchema|"`
+
+### Import examples
+
+#### Grant all privileges OnDatabase
+`terraform import "\"test_db\".\"test_db_role\"|false|false|ALL|OnDatabase|\"test_db\""`
+
+#### Grant list of privileges OnAllSchemasInDatabase
+`terraform import "\"test_db\".\"test_db_role\"|false|false|CREATE TAG,CREATE TABLE|OnSchema|OnAllSchemasInDatabase|\"test_db\""`
+
+#### Grant list of privileges on table
+`terraform import "\"test_db\".\"test_db_role\"|false|false|SELECT,DELETE,INSERT|OnSchemaObject|OnObject|TABLE|\"test_db\".\"test_schema\".\"test_table\""`
+
+#### Grant list of privileges OnAll tables in schema
+`terraform import "\"test_db\".\"test_db_role\"|false|false|SELECT,DELETE,INSERT|OnSchemaObject|OnAll|TABLES|InSchema|\"test_db\".\"test_schema\""`
+