diff --git a/docs/data-sources/account_roles.md b/docs/data-sources/account_roles.md
new file mode 100644
index 00000000000..3eca8fb8cde
--- /dev/null
+++ b/docs/data-sources/account_roles.md
@@ -0,0 +1,43 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "snowflake_account_roles Data Source - terraform-provider-snowflake"
+subcategory: ""
+description: |-
+
+---
+
+# snowflake_account_roles (Data Source)
+
+
+
+## Example Usage
+
+```terraform
+data "snowflake_account_roles" "all" {
+}
+
+data "snowflake_account_roles" "by_pattern" {
+ pattern = "some_prefix_%"
+}
+```
+
+
+## Schema
+
+### Optional
+
+- `pattern` (String) Filters the command output by object name.
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `roles` (List of Object) List of all the roles which you can view across your entire account, including the system-defined roles and any custom roles that exist. (see [below for nested schema](#nestedatt--roles))
+
+
+### Nested Schema for `roles`
+
+Read-Only:
+
+- `comment` (String)
+- `name` (String)
+- `owner` (String)
diff --git a/docs/resources/account_role.md b/docs/resources/account_role.md
new file mode 100644
index 00000000000..631a9e9b58f
--- /dev/null
+++ b/docs/resources/account_role.md
@@ -0,0 +1,57 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "snowflake_account_role Resource - terraform-provider-snowflake"
+subcategory: ""
+description: |-
+
+---
+
+# snowflake_account_role (Resource)
+
+
+
+## Example Usage
+
+```terraform
+resource "snowflake_account_role" "role" {
+ name = "role_name"
+ comment = "comment"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `name` (String)
+
+### Optional
+
+- `comment` (String)
+- `tag` (Block List, Deprecated) Definitions of a tag to associate with the resource. (see [below for nested schema](#nestedblock--tag))
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `tag`
+
+Required:
+
+- `name` (String) Tag name, e.g. department.
+- `value` (String) Tag value, e.g. marketing_info.
+
+Optional:
+
+- `database` (String) Name of the database that the tag was created in.
+- `schema` (String) Name of the schema that the tag was created in.
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+terraform import snowflake_account_role.example roleName
+```
diff --git a/examples/data-sources/snowflake_account_roles/data-source.tf b/examples/data-sources/snowflake_account_roles/data-source.tf
new file mode 100644
index 00000000000..c5846c0fab6
--- /dev/null
+++ b/examples/data-sources/snowflake_account_roles/data-source.tf
@@ -0,0 +1,6 @@
+data "snowflake_account_roles" "all" {
+}
+
+data "snowflake_account_roles" "by_pattern" {
+ pattern = "some_prefix_%"
+}
diff --git a/examples/resources/snowflake_account_role/import.sh b/examples/resources/snowflake_account_role/import.sh
new file mode 100644
index 00000000000..96c1cd42765
--- /dev/null
+++ b/examples/resources/snowflake_account_role/import.sh
@@ -0,0 +1 @@
+terraform import snowflake_account_role.example roleName
diff --git a/examples/resources/snowflake_account_role/resource.tf b/examples/resources/snowflake_account_role/resource.tf
new file mode 100644
index 00000000000..740f215598f
--- /dev/null
+++ b/examples/resources/snowflake_account_role/resource.tf
@@ -0,0 +1,4 @@
+resource "snowflake_account_role" "role" {
+ name = "role_name"
+ comment = "comment"
+}
diff --git a/pkg/datasources/account_roles.go b/pkg/datasources/account_roles.go
new file mode 100644
index 00000000000..c0c9e993bea
--- /dev/null
+++ b/pkg/datasources/account_roles.go
@@ -0,0 +1,95 @@
+package datasources
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+var accountRolesSchema = map[string]*schema.Schema{
+ "pattern": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Filters the command output by object name.",
+ },
+ "roles": {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "List of all the roles which you can view across your entire account, including the system-defined roles and any custom roles that exist.",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Identifier for the role.",
+ },
+ "comment": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "The comment on the role",
+ },
+ "owner": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "The owner of the role",
+ },
+ },
+ },
+ },
+}
+
+func AccountRoles() *schema.Resource {
+ return &schema.Resource{
+ ReadContext: ReadAccountRoles,
+ Schema: accountRolesSchema,
+ }
+}
+
+func ReadAccountRoles(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+
+ req := sdk.NewShowRoleRequest()
+ if pattern, ok := d.GetOk("pattern"); ok {
+ req.WithLike(sdk.NewLikeRequest(pattern.(string)))
+ }
+
+ roles, err := client.Roles.Show(ctx, req)
+ if err != nil {
+ d.SetId("")
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to show account roles",
+ Detail: fmt.Sprintf("Search pattern: %v, err: %s", d.Get("pattern").(string), err),
+ },
+ }
+ }
+
+ mappedRoles := make([]map[string]any, len(roles))
+ for i, role := range roles {
+ mappedRoles[i] = map[string]any{
+ "name": role.Name,
+ "comment": role.Comment,
+ "owner": role.Owner,
+ }
+ }
+
+ if err := d.Set("roles", mappedRoles); err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to set roles",
+ Detail: fmt.Sprintf("Search pattern: %v, err: %s", d.Get("pattern").(string), err),
+ },
+ }
+ }
+
+ d.SetId("roles_read")
+
+ return nil
+}
diff --git a/pkg/datasources/account_roles_acceptance_test.go b/pkg/datasources/account_roles_acceptance_test.go
new file mode 100644
index 00000000000..0c0cbfc2f0a
--- /dev/null
+++ b/pkg/datasources/account_roles_acceptance_test.go
@@ -0,0 +1,86 @@
+package datasources_test
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "testing"
+
+ acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"
+ "github.com/hashicorp/terraform-plugin-testing/config"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+ "github.com/hashicorp/terraform-plugin-testing/tfversion"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAcc_AccountRoles_basic(t *testing.T) {
+ accountRoleNamePrefix := "account_roles_test_prefix_"
+ accountRoleName1 := accountRoleNamePrefix + strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ accountRoleName2 := accountRoleNamePrefix + strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ accountRoleName3 := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ comment := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+
+ configVariables := config.Variables{
+ "account_role_name_1": config.StringVariable(accountRoleName1),
+ "account_role_name_2": config.StringVariable(accountRoleName2),
+ "account_role_name_3": config.StringVariable(accountRoleName3),
+ "pattern": config.StringVariable(accountRoleNamePrefix + "%"),
+ "comment": config.StringVariable(comment),
+ }
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ Steps: []resource.TestStep{
+ {
+ ConfigDirectory: config.TestNameDirectory(),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("data.snowflake_account_roles.test", "roles.#", "2"),
+ containsAccountRole(accountRoleName1, comment),
+ containsAccountRole(accountRoleName2, comment),
+ func(state *terraform.State) error {
+ err := containsAccountRole(accountRoleName3, comment)(state)
+ if err.Error() == fmt.Sprintf("role %s not found", accountRoleName3) {
+ return nil
+ }
+ return fmt.Errorf("expected %s not to be present", accountRoleName3)
+ },
+ ),
+ },
+ },
+ })
+}
+
+func containsAccountRole(name string, comment string) func(s *terraform.State) error {
+ return func(s *terraform.State) error {
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "snowflake_account_roles" {
+ continue
+ }
+
+ iter, err := strconv.ParseInt(rs.Primary.Attributes["roles.#"], 10, 32)
+ if err != nil {
+ return err
+ }
+
+ for i := 0; i < int(iter); i++ {
+ if rs.Primary.Attributes[fmt.Sprintf("roles.%d.name", i)] == name {
+ actualComment := rs.Primary.Attributes[fmt.Sprintf("roles.%d.comment", i)]
+ if actualComment != comment {
+ return fmt.Errorf("expected comment: %s, but got: %s", comment, actualComment)
+ }
+
+ return nil
+ }
+ }
+ }
+
+ return fmt.Errorf("role %s not found", name)
+ }
+}
diff --git a/pkg/datasources/role.go b/pkg/datasources/role.go
index 6cf81c4271b..4792af75c22 100644
--- a/pkg/datasources/role.go
+++ b/pkg/datasources/role.go
@@ -25,8 +25,9 @@ var roleSchema = map[string]*schema.Schema{
// Role Snowflake Role resource.
func Role() *schema.Resource {
return &schema.Resource{
- Read: ReadRole,
- Schema: roleSchema,
+ Read: ReadRole,
+ Schema: roleSchema,
+ DeprecationMessage: "This resource is deprecated and will be removed in a future major version release. Please use snowflake_account_roles instead.",
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
diff --git a/pkg/datasources/roles.go b/pkg/datasources/roles.go
index 781bed6e73d..4d94fd043e7 100644
--- a/pkg/datasources/roles.go
+++ b/pkg/datasources/roles.go
@@ -1,87 +1,13 @@
package datasources
import (
- "database/sql"
- "errors"
- "log"
-
- "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
-var rolesSchema = map[string]*schema.Schema{
- "pattern": {
- Type: schema.TypeString,
- Optional: true,
- Description: "Filters the command output by object name.",
- },
- "roles": {
- Type: schema.TypeList,
- Computed: true,
- Description: "List of all the roles which you can view across your entire account, including the system-defined roles and any custom roles that exist.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Computed: true,
- Description: "Identifier for the role.",
- },
- "comment": {
- Type: schema.TypeString,
- Computed: true,
- Description: "The comment on the role",
- },
- "owner": {
- Type: schema.TypeString,
- Computed: true,
- Description: "The owner of the role",
- },
- },
- },
- },
-}
-
-// Roles Snowflake Roles resource.
func Roles() *schema.Resource {
return &schema.Resource{
- Read: ReadRoles,
- Schema: rolesSchema,
- }
-}
-
-// ReadRoles Reads the database metadata information.
-func ReadRoles(d *schema.ResourceData, meta interface{}) error {
- db := meta.(*sql.DB)
- d.SetId("roles_read")
- rolePattern := d.Get("pattern").(string)
-
- listRoles, err := snowflake.ListRoles(db, rolePattern)
- if errors.Is(err, sql.ErrNoRows) {
- log.Printf("[DEBUG] no roles found in account (%s)", d.Id())
- d.SetId("")
- return nil
- } else if err != nil {
- log.Println("[DEBUG] failed to list roles")
- d.SetId("")
- return nil
- }
-
- log.Printf("[DEBUG] list roles: %v", listRoles)
-
- roles := []map[string]interface{}{}
- for _, role := range listRoles {
- roleMap := map[string]interface{}{}
- if !role.Name.Valid {
- continue
- }
- roleMap["name"] = role.Name.String
- roleMap["comment"] = role.Comment.String
- roleMap["owner"] = role.Owner.String
- roles = append(roles, roleMap)
- }
-
- if err := d.Set("roles", roles); err != nil {
- return err
+ ReadContext: ReadAccountRoles,
+ Schema: accountRolesSchema,
+ DeprecationMessage: "This resource is deprecated and will be removed in a future major version release. Please use snowflake_account_roles instead.",
}
- return nil
}
diff --git a/pkg/datasources/testdata/TestAcc_AccountRoles_basic/test.tf b/pkg/datasources/testdata/TestAcc_AccountRoles_basic/test.tf
new file mode 100644
index 00000000000..68d5033a36a
--- /dev/null
+++ b/pkg/datasources/testdata/TestAcc_AccountRoles_basic/test.tf
@@ -0,0 +1,23 @@
+resource "snowflake_account_role" "test1" {
+ name = var.account_role_name_1
+ comment = var.comment
+}
+
+resource "snowflake_account_role" "test2" {
+ name = var.account_role_name_2
+ comment = var.comment
+}
+
+resource "snowflake_account_role" "test3" {
+ name = var.account_role_name_3
+ comment = var.comment
+}
+
+data "snowflake_account_roles" "test" {
+ depends_on = [
+ snowflake_account_role.test1,
+ snowflake_account_role.test2,
+ snowflake_account_role.test3,
+ ]
+ pattern = var.pattern
+}
diff --git a/pkg/datasources/testdata/TestAcc_AccountRoles_basic/variables.tf b/pkg/datasources/testdata/TestAcc_AccountRoles_basic/variables.tf
new file mode 100644
index 00000000000..c632a5527e0
--- /dev/null
+++ b/pkg/datasources/testdata/TestAcc_AccountRoles_basic/variables.tf
@@ -0,0 +1,19 @@
+variable "account_role_name_1" {
+ type = string
+}
+
+variable "account_role_name_2" {
+ type = string
+}
+
+variable "account_role_name_3" {
+ type = string
+}
+
+variable "comment" {
+ type = string
+}
+
+variable "pattern" {
+ type = string
+}
diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go
index b72a7f76bb7..21b11488dbb 100644
--- a/pkg/provider/provider.go
+++ b/pkg/provider/provider.go
@@ -431,6 +431,7 @@ func getResources() map[string]*schema.Resource {
"snowflake_account": resources.Account(),
"snowflake_account_password_policy_attachment": resources.AccountPasswordPolicyAttachment(),
"snowflake_account_parameter": resources.AccountParameter(),
+ "snowflake_account_role": resources.AccountRole(),
"snowflake_alert": resources.Alert(),
"snowflake_api_integration": resources.APIIntegration(),
"snowflake_database": resources.Database(),
@@ -517,6 +518,7 @@ func getDataSources() map[string]*schema.Resource {
"snowflake_resource_monitors": datasources.ResourceMonitors(),
"snowflake_role": datasources.Role(),
"snowflake_roles": datasources.Roles(),
+ "snowflake_account_roles": datasources.AccountRoles(),
"snowflake_row_access_policies": datasources.RowAccessPolicies(),
"snowflake_schemas": datasources.Schemas(),
"snowflake_sequences": datasources.Sequences(),
diff --git a/pkg/resources/account_role.go b/pkg/resources/account_role.go
new file mode 100644
index 00000000000..63cf3e7205e
--- /dev/null
+++ b/pkg/resources/account_role.go
@@ -0,0 +1,253 @@
+package resources
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+var accountRoleSchema = map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
+ },
+ "comment": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "tag": tagReferenceSchema,
+}
+
+func AccountRole() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: CreateAccountRole,
+ ReadContext: ReadAccountRole,
+ DeleteContext: DeleteAccountRole,
+ UpdateContext: UpdateAccountRole,
+
+ Schema: accountRoleSchema,
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+ }
+}
+
+func CreateAccountRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+
+ name := d.Get("name").(string)
+ id, err := helpers.DecodeSnowflakeParameterID(name)
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to parse account role name",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", name, err),
+ },
+ }
+ }
+ req := sdk.NewCreateRoleRequest(id.(sdk.AccountObjectIdentifier))
+
+ if v, ok := d.GetOk("comment"); ok {
+ req.WithComment(v.(string))
+ }
+
+ if _, ok := d.GetOk("tag"); ok {
+ req.WithTag(getPropertyTags(d, "tag"))
+ }
+
+ err = client.Roles.Create(ctx, req)
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to create account role",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", name, err),
+ },
+ }
+ }
+
+ d.SetId(helpers.EncodeSnowflakeID(id))
+
+ return ReadAccountRole(ctx, d, meta)
+}
+
+func ReadAccountRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)
+
+ accountRole, err := client.Roles.ShowByID(ctx, sdk.NewShowByIdRoleRequest(id))
+ if err != nil {
+ if err.Error() == "object does not exist" {
+ d.SetId("")
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Warning,
+ Summary: "Account role not found; marking it as removed",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", id.FullyQualifiedName(), err),
+ },
+ }
+ }
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to show account role by id",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", id.FullyQualifiedName(), err),
+ },
+ }
+ }
+
+ if err := d.Set("name", accountRole.Name); err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to set account role name",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", accountRole.Name, err),
+ },
+ }
+ }
+
+ if err := d.Set("comment", accountRole.Comment); err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to set account role comment",
+ Detail: fmt.Sprintf("Account role name: %s, comment: %s, err: %s", accountRole.Name, accountRole.Comment, err),
+ },
+ }
+ }
+
+ d.SetId(helpers.EncodeSnowflakeID(id))
+
+ return nil
+}
+
+func UpdateAccountRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)
+
+ if d.HasChange("comment") {
+ if v, ok := d.GetOk("comment"); ok {
+ err := client.Roles.Alter(ctx, sdk.NewAlterRoleRequest(id).WithSetComment(v.(string)))
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to set account role comment",
+ Detail: fmt.Sprintf("Account role name: %s, comment: %s, err: %s", id.FullyQualifiedName(), v, err),
+ },
+ }
+ }
+ } else {
+ err := client.Roles.Alter(ctx, sdk.NewAlterRoleRequest(id).WithUnsetComment(true))
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to unset account role comment",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", id.FullyQualifiedName(), err),
+ },
+ }
+ }
+ }
+ }
+
+ if d.HasChange("tag") {
+ unsetTags, setTags := GetTagsDiff(d, "tag")
+
+ if len(unsetTags) > 0 {
+ err := client.Roles.Alter(ctx, sdk.NewAlterRoleRequest(id).WithUnsetTags(unsetTags))
+ if err != nil {
+ tagNames := make([]string, len(unsetTags))
+ for i, v := range unsetTags {
+ tagNames[i] = v.FullyQualifiedName()
+ }
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to unset account role tags",
+ Detail: fmt.Sprintf("Account role name: %s, tags to unset: %v, err: %s", id.FullyQualifiedName(), tagNames, err),
+ },
+ }
+ }
+ }
+
+ if len(setTags) > 0 {
+ err := client.Roles.Alter(ctx, sdk.NewAlterRoleRequest(id).WithSetTags(setTags))
+ if err != nil {
+ tagNames := make([]string, len(unsetTags))
+ for i, v := range unsetTags {
+ tagNames[i] = v.FullyQualifiedName()
+ }
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to set account role tags",
+ Detail: fmt.Sprintf("Account role name: %s, tags to set: %v, err: %s", id.FullyQualifiedName(), tagNames, err),
+ },
+ }
+ }
+ }
+ }
+
+ if d.HasChange("name") {
+ _, newName := d.GetChange("name")
+
+ newId, err := helpers.DecodeSnowflakeParameterID(newName.(string))
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to parse account role name",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", newName, err),
+ },
+ }
+ }
+
+ err = client.Roles.Alter(ctx, sdk.NewAlterRoleRequest(id).WithRenameTo(newId.(sdk.AccountObjectIdentifier)))
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to rename account role name",
+ Detail: fmt.Sprintf("Previous account role name: %s, new account role name: %s, err: %s", id, newName, err),
+ },
+ }
+ }
+
+ d.SetId(helpers.EncodeSnowflakeID(newId))
+ }
+
+ return nil
+}
+
+func DeleteAccountRole(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ db := meta.(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)
+
+ err := client.Roles.Drop(ctx, sdk.NewDropRoleRequest(id))
+ if err != nil {
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Failed to drop account role",
+ Detail: fmt.Sprintf("Account role name: %s, err: %s", d.Id(), err),
+ },
+ }
+ }
+
+ d.SetId("")
+
+ return nil
+}
diff --git a/pkg/resources/account_role_acceptance_test.go b/pkg/resources/account_role_acceptance_test.go
new file mode 100644
index 00000000000..5d58cbcfc33
--- /dev/null
+++ b/pkg/resources/account_role_acceptance_test.go
@@ -0,0 +1,127 @@
+package resources_test
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-testing/config"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+ "github.com/hashicorp/terraform-plugin-testing/tfversion"
+
+ acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"
+ "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAcc_AccountRole_basic(t *testing.T) {
+ name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ comment := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ configVariables := map[string]config.Variable{
+ "name": config.StringVariable(name),
+ "comment": config.StringVariable(comment),
+ }
+ resourceName := "snowflake_account_role.test"
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckAccountRoleDestroy(name),
+ Steps: []resource.TestStep{
+ {
+ ConfigDirectory: config.TestNameDirectory(),
+ ConfigVariables: configVariables,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "name", name),
+ resource.TestCheckResourceAttr(resourceName, "comment", comment),
+ resource.TestCheckResourceAttr(resourceName, "id", name),
+ ),
+ },
+ // test import
+ {
+ ConfigDirectory: config.TestNameDirectory(),
+ ConfigVariables: configVariables,
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAcc_AccountRole_updates(t *testing.T) {
+ configVariables := func(name string, comment string) config.Variables {
+ return config.Variables{
+ "name": config.StringVariable(name),
+ "comment": config.StringVariable(comment),
+ }
+ }
+
+ name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ newName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ comment := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ NewComment := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ resourceName := "snowflake_account_role.test"
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: testAccCheckAccountRoleDestroy(name),
+ Steps: []resource.TestStep{
+ {
+ ConfigDirectory: config.TestNameDirectory(),
+ ConfigVariables: configVariables(name, comment),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "name", name),
+ resource.TestCheckResourceAttr(resourceName, "comment", comment),
+ resource.TestCheckResourceAttr(resourceName, "id", name),
+ ),
+ },
+ {
+ ConfigDirectory: config.TestNameDirectory(),
+ ConfigVariables: configVariables(newName, NewComment),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(resourceName, "name", newName),
+ resource.TestCheckResourceAttr(resourceName, "comment", NewComment),
+ resource.TestCheckResourceAttr(resourceName, "id", newName),
+ ),
+ },
+ // test import
+ {
+ ConfigDirectory: config.TestNameDirectory(),
+ ConfigVariables: configVariables(newName, NewComment),
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func testAccCheckAccountRoleDestroy(accountRoleName string) func(state *terraform.State) error {
+ return func(state *terraform.State) error {
+ db := acc.TestAccProvider.Meta().(*sql.DB)
+ client := sdk.NewClientFromDB(db)
+ for _, rs := range state.RootModule().Resources {
+ if rs.Type != "snowflake_account_role" {
+ continue
+ }
+ ctx := context.Background()
+ id := sdk.NewAccountObjectIdentifier(rs.Primary.Attributes["name"])
+ _, err := client.Roles.ShowByID(ctx, sdk.NewShowByIdRoleRequest(id))
+ if err == nil {
+ return fmt.Errorf("account role %v still exists", accountRoleName)
+ }
+ }
+ return nil
+ }
+}
diff --git a/pkg/resources/database_role_acceptance_test.go b/pkg/resources/database_role_acceptance_test.go
index 071ef0f9b13..9729a449eba 100644
--- a/pkg/resources/database_role_acceptance_test.go
+++ b/pkg/resources/database_role_acceptance_test.go
@@ -10,14 +10,12 @@ import (
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
-var (
- resourceName = "snowflake_database_role.test_db_role"
- dbRoleName = "db_role_" + strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
- comment = "dummy"
- comment2 = "test comment"
-)
-
func TestAcc_DatabaseRole(t *testing.T) {
+ resourceName := "snowflake_database_role.test_db_role"
+ dbRoleName := "db_role_" + strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
+ comment := "dummy"
+ comment2 := "test comment"
+
resource.Test(t, resource.TestCase{
Providers: acc.TestAccProviders(),
PreCheck: func() { acc.TestAccPreCheck(t) },
diff --git a/pkg/resources/dynamic_table.go b/pkg/resources/dynamic_table.go
index 90cbdd1c228..520e02b24ff 100644
--- a/pkg/resources/dynamic_table.go
+++ b/pkg/resources/dynamic_table.go
@@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
-var dynamicTableShema = map[string]*schema.Schema{
+var dynamicTableSchema = map[string]*schema.Schema{
"or_replace": {
Type: schema.TypeBool,
Optional: true,
@@ -144,7 +144,7 @@ func DynamicTable() *schema.Resource {
Update: UpdateDynamicTable,
Delete: DeleteDynamicTable,
- Schema: dynamicTableShema,
+ Schema: dynamicTableSchema,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
diff --git a/pkg/resources/external_table.go b/pkg/resources/external_table.go
index f9d73c42379..5b1235ca163 100644
--- a/pkg/resources/external_table.go
+++ b/pkg/resources/external_table.go
@@ -285,18 +285,22 @@ func UpdateExternalTable(d *schema.ResourceData, meta any) error {
if d.HasChange("tag") {
unsetTags, setTags := GetTagsDiff(d, "tag")
- err := client.ExternalTables.Alter(ctx, sdk.NewAlterExternalTableRequest(id).WithUnsetTag(unsetTags))
- if err != nil {
- return fmt.Errorf("error setting tags on %v, err = %w", d.Id(), err)
+ if len(unsetTags) > 0 {
+ err := client.ExternalTables.Alter(ctx, sdk.NewAlterExternalTableRequest(id).WithUnsetTag(unsetTags))
+ if err != nil {
+ return fmt.Errorf("error setting tags on %v, err = %w", d.Id(), err)
+ }
}
- tagAssociationRequests := make([]*sdk.TagAssociationRequest, len(setTags))
- for i, t := range setTags {
- tagAssociationRequests[i] = sdk.NewTagAssociationRequest(t.Name, t.Value)
- }
- err = client.ExternalTables.Alter(ctx, sdk.NewAlterExternalTableRequest(id).WithSetTag(tagAssociationRequests))
- if err != nil {
- return fmt.Errorf("error setting tags on %v, err = %w", d.Id(), err)
+ if len(setTags) > 0 {
+ tagAssociationRequests := make([]*sdk.TagAssociationRequest, len(setTags))
+ for i, t := range setTags {
+ tagAssociationRequests[i] = sdk.NewTagAssociationRequest(t.Name, t.Value)
+ }
+ err := client.ExternalTables.Alter(ctx, sdk.NewAlterExternalTableRequest(id).WithSetTag(tagAssociationRequests))
+ if err != nil {
+ return fmt.Errorf("error setting tags on %v, err = %w", d.Id(), err)
+ }
}
}
diff --git a/pkg/resources/role.go b/pkg/resources/role.go
index 60268121d21..d63266e7816 100644
--- a/pkg/resources/role.go
+++ b/pkg/resources/role.go
@@ -1,154 +1,20 @@
package resources
import (
- "database/sql"
- "errors"
- "log"
-
- "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
-
- "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
-var roleSchema = map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Required: true,
- ValidateFunc: func(val interface{}, key string) ([]string, []error) {
- additionalCharsToIgnoreValidation := []string{".", " ", ":", "(", ")"}
- return sdk.ValidateIdentifier(val, additionalCharsToIgnoreValidation)
- },
- },
- "comment": {
- Type: schema.TypeString,
- Optional: true,
- // TODO validation
- },
- "tag": tagReferenceSchema,
-}
-
func Role() *schema.Resource {
return &schema.Resource{
- Create: CreateRole,
- Read: ReadRole,
- Delete: DeleteRole,
- Update: UpdateRole,
+ CreateContext: CreateAccountRole,
+ ReadContext: ReadAccountRole,
+ DeleteContext: DeleteAccountRole,
+ UpdateContext: UpdateAccountRole,
+ DeprecationMessage: "This resource is deprecated and will be removed in a future major version release. Please use snowflake_account_role instead.",
- Schema: roleSchema,
+ Schema: accountRoleSchema,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
}
}
-
-func CreateRole(d *schema.ResourceData, meta interface{}) error {
- name := d.Get("name").(string)
- db := meta.(*sql.DB)
- builder := snowflake.NewRoleBuilder(db, name)
- if v, ok := d.GetOk("comment"); ok {
- builder.WithComment(v.(string))
- }
- if v, ok := d.GetOk("tag"); ok {
- tags := getTags(v)
- builder.WithTags(tags.toSnowflakeTagValues())
- }
- err := builder.Create()
- if err != nil {
- return err
- }
- d.SetId(name)
- return ReadRole(d, meta)
-}
-
-func ReadRole(d *schema.ResourceData, meta interface{}) error {
- db := meta.(*sql.DB)
- id := d.Id()
- // If the name is not set (such as during import) then use the id
- name := d.Get("name").(string)
- if name == "" {
- name = id
- }
-
- builder := snowflake.NewRoleBuilder(db, name)
- role, err := builder.Show()
- if errors.Is(err, sql.ErrNoRows) {
- log.Printf("[WARN] role (%s) not found", name)
- d.SetId("")
- return nil
- } else if err != nil {
- return err
- }
- if err := d.Set("name", role.Name.String); err != nil {
- return err
- }
- if err := d.Set("comment", role.Comment.String); err != nil {
- return err
- }
- return nil
-}
-
-func UpdateRole(d *schema.ResourceData, meta interface{}) error {
- db := meta.(*sql.DB)
- name := d.Get("name").(string)
- builder := snowflake.NewRoleBuilder(db, name)
-
- if d.HasChange("name") {
- o, n := d.GetChange("name")
- builder.WithName(o.(string))
- err := builder.Rename(n.(string))
- if err != nil {
- return err
- }
- builder.WithName(n.(string))
- }
-
- if d.HasChange("comment") {
- o, n := d.GetChange("comment")
- if n == nil || n.(string) == "" {
- builder.WithComment(o.(string))
- err := builder.UnsetComment()
- if err != nil {
- return err
- }
- } else {
- err := builder.SetComment(n.(string))
- if err != nil {
- return err
- }
- }
- }
-
- if d.HasChange("tag") {
- o, n := d.GetChange("tag")
- removed, added, changed := getTags(o).diffs(getTags(n))
- for _, tA := range removed {
- err := builder.UnsetTag(tA.toSnowflakeTagValue())
- if err != nil {
- return err
- }
- }
- for _, tA := range added {
- err := builder.SetTag(tA.toSnowflakeTagValue())
- if err != nil {
- return err
- }
- }
- for _, tA := range changed {
- err := builder.ChangeTag(tA.toSnowflakeTagValue())
- if err != nil {
- return err
- }
- }
- }
-
- return nil
-}
-
-func DeleteRole(d *schema.ResourceData, meta interface{}) error {
- db := meta.(*sql.DB)
- name := d.Get("name").(string)
- builder := snowflake.NewRoleBuilder(db, name)
- err := builder.Drop()
- return err
-}
diff --git a/pkg/resources/schema.go b/pkg/resources/schema.go
index bdd30aa3ca7..7dc7474541b 100644
--- a/pkg/resources/schema.go
+++ b/pkg/resources/schema.go
@@ -231,18 +231,22 @@ func UpdateSchema(d *schema.ResourceData, meta interface{}) error {
if d.HasChange("tag") {
unsetTags, setTags := GetTagsDiff(d, "tag")
- err := client.Schemas.Alter(ctx, id, &sdk.AlterSchemaOptions{
- UnsetTag: unsetTags,
- })
- if err != nil {
- return fmt.Errorf("error occurred when dropping tags on %v, err = %w", d.Id(), err)
+ if len(unsetTags) > 0 {
+ err := client.Schemas.Alter(ctx, id, &sdk.AlterSchemaOptions{
+ UnsetTag: unsetTags,
+ })
+ if err != nil {
+ return fmt.Errorf("error occurred when dropping tags on %v, err = %w", d.Id(), err)
+ }
}
- err = client.Schemas.Alter(ctx, id, &sdk.AlterSchemaOptions{
- SetTag: setTags,
- })
- if err != nil {
- return fmt.Errorf("error occurred when setting tags on %v, err = %w", d.Id(), err)
+ if len(setTags) > 0 {
+ err := client.Schemas.Alter(ctx, id, &sdk.AlterSchemaOptions{
+ SetTag: setTags,
+ })
+ if err != nil {
+ return fmt.Errorf("error occurred when setting tags on %v, err = %w", d.Id(), err)
+ }
}
}
diff --git a/pkg/resources/testdata/TestAcc_AccountRole_basic/test.tf b/pkg/resources/testdata/TestAcc_AccountRole_basic/test.tf
new file mode 100644
index 00000000000..1aa83161f42
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_AccountRole_basic/test.tf
@@ -0,0 +1,4 @@
+resource "snowflake_account_role" "test" {
+ name = var.name
+ comment = var.comment
+}
diff --git a/pkg/resources/testdata/TestAcc_AccountRole_basic/variables.tf b/pkg/resources/testdata/TestAcc_AccountRole_basic/variables.tf
new file mode 100644
index 00000000000..821eeebe895
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_AccountRole_basic/variables.tf
@@ -0,0 +1,7 @@
+variable "name" {
+ type = string
+}
+
+variable "comment" {
+ type = string
+}
diff --git a/pkg/resources/testdata/TestAcc_AccountRole_updates/test.tf b/pkg/resources/testdata/TestAcc_AccountRole_updates/test.tf
new file mode 100644
index 00000000000..1aa83161f42
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_AccountRole_updates/test.tf
@@ -0,0 +1,4 @@
+resource "snowflake_account_role" "test" {
+ name = var.name
+ comment = var.comment
+}
diff --git a/pkg/resources/testdata/TestAcc_AccountRole_updates/variables.tf b/pkg/resources/testdata/TestAcc_AccountRole_updates/variables.tf
new file mode 100644
index 00000000000..821eeebe895
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_AccountRole_updates/variables.tf
@@ -0,0 +1,7 @@
+variable "name" {
+ type = string
+}
+
+variable "comment" {
+ type = string
+}