From 17fba7e83e61f034ae013970b4c8fd2039b36ca4 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 10:38:11 +0200 Subject: [PATCH 01/12] rework users datasource starts here From a45ac4ff5fc5f6a61e07c7e8a8ba513da6778b2c Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 11:32:51 +0200 Subject: [PATCH 02/12] Add user describe schema and mapper; rework users datasource --- pkg/datasources/users.go | 235 ++++++++++++++++++----------------- pkg/schemas/user_describe.go | 231 ++++++++++++++++++++++++++++++++++ 2 files changed, 355 insertions(+), 111 deletions(-) create mode 100644 pkg/schemas/user_describe.go diff --git a/pkg/datasources/users.go b/pkg/datasources/users.go index e620ac6ee5..18fe56371a 100644 --- a/pkg/datasources/users.go +++ b/pkg/datasources/users.go @@ -2,93 +2,87 @@ package datasources import ( "context" - "fmt" - "log" - "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "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 usersSchema = map[string]*schema.Schema{ - "pattern": { - Type: schema.TypeString, - Required: true, - Description: "Users pattern for which to return metadata. Please refer to LIKE keyword from " + - "snowflake documentation : https://docs.snowflake.com/en/sql-reference/sql/show-users.html#parameters", + "with_describe": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Runs DESC USER for each user returned by SHOW USERS. The output of describe is saved to the description field. By default this value is set to true.", }, - "users": { + "with_parameters": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Runs SHOW PARAMETERS FOR USER for each user returned by SHOW USERS. The output of describe is saved to the parameters field as a map. By default this value is set to true.", + }, + "like": { + Type: schema.TypeString, + Optional: true, + Description: "Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`).", + }, + "starts_with": { + Type: schema.TypeString, + Optional: true, + Description: "Filters the output with **case-sensitive** characters indicating the beginning of the object name.", + }, + "limit": { Type: schema.TypeList, - Computed: true, - Description: "The users in the database", + Optional: true, + Description: "Limits the number of rows returned. If the `limit.from` is set, then the limit wll start from the first element matched by the expression. The expression is only used to match with the first element, later on the elements are not matched by the prefix, but you can enforce a certain pattern with `starts_with` or `like`.", + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Computed: true, - }, - "login_name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "comment": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "disabled": { - Type: schema.TypeBool, - Optional: true, - Computed: true, - }, - "default_warehouse": { - Type: schema.TypeString, - Optional: true, - Computed: true, + "rows": { + Type: schema.TypeInt, + Required: true, + Description: "The maximum number of rows to return.", }, - "default_namespace": { - Type: schema.TypeString, - Optional: true, - Computed: true, + "from": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a **case-sensitive** pattern that is used to match object name. After the first match, the limit on the number of rows will be applied.", }, - "default_role": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "default_secondary_roles": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Computed: true, - }, - "has_rsa_public_key": { - Type: schema.TypeBool, - Computed: true, - }, - "email": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "display_name": { - Type: schema.TypeString, - Computed: true, - Optional: true, + }, + }, + }, + "users": { + Type: schema.TypeList, + Computed: true, + Description: "Holds the aggregated output of all user details queries.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + resources.ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Holds the output of SHOW USERS.", + Elem: &schema.Resource{ + Schema: schemas.ShowUserSchema, + }, }, - "first_name": { - Type: schema.TypeString, - Optional: true, - Computed: true, + resources.DescribeOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Holds the output of DESCRIBE USER.", + Elem: &schema.Resource{ + Schema: schemas.UserDescribeSchema, + }, }, - "last_name": { - Type: schema.TypeString, - Optional: true, - Computed: true, + resources.ParametersAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Holds the output of SHOW PARAMETERS FOR USER.", + Elem: &schema.Resource{ + Schema: schemas.ShowUserParametersSchema, + }, }, }, }, @@ -97,57 +91,76 @@ var usersSchema = map[string]*schema.Schema{ func Users() *schema.Resource { return &schema.Resource{ - Read: ReadUsers, - Schema: usersSchema, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, + ReadContext: ReadUsers, + Schema: usersSchema, + Description: "Datasource used to get details of filtered users. Filtering is aligned with the current possibilities for [SHOW USERS](https://docs.snowflake.com/en/sql-reference/sql/show-users) query. The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.", } } -func ReadUsers(d *schema.ResourceData, meta interface{}) error { +func ReadUsers(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - ctx := context.Background() + var opts sdk.ShowUserOptions - userPattern := d.Get("pattern").(string) + if likePattern, ok := d.GetOk("like"); ok { + opts.Like = &sdk.Like{ + Pattern: sdk.String(likePattern.(string)), + } + } - account, err1 := client.ContextFunctions.CurrentAccount(ctx) - region, err2 := client.ContextFunctions.CurrentRegion(ctx) - if err1 != nil || err2 != nil { - log.Print("[DEBUG] unable to retrieve current account") - d.SetId("") - return nil + if startsWith, ok := d.GetOk("starts_with"); ok { + opts.StartsWith = sdk.String(startsWith.(string)) } - d.SetId(fmt.Sprintf("%s.%s", account, region)) - extractedUsers, err := client.Users.Show(ctx, &sdk.ShowUserOptions{ - Like: &sdk.Like{Pattern: sdk.String(userPattern)}, - }) + if limit, ok := d.GetOk("limit"); ok && len(limit.([]any)) == 1 { + limitMap := limit.([]any)[0].(map[string]any) + + rows := limitMap["rows"].(int) + opts.Limit = &rows + + if from, ok := limitMap["from"].(string); ok { + opts.From = &from + } + } + + users, err := client.Users.Show(ctx, &opts) if err != nil { - log.Printf("[DEBUG] no users found in account (%s)", d.Id()) - d.SetId("") - return nil + return diag.FromErr(err) } + d.SetId("users_read") + + flattenedUsers := make([]map[string]any, len(users)) - users := make([]map[string]any, len(extractedUsers)) - - for i, user := range extractedUsers { - users[i] = map[string]any{ - "name": user.Name, - "login_name": user.LoginName, - "comment": user.Comment, - "disabled": user.Disabled, - "default_warehouse": user.DefaultWarehouse, - "default_namespace": user.DefaultNamespace, - "default_role": user.DefaultRole, - "default_secondary_roles": strings.Split(helpers.ListContentToString(user.DefaultSecondaryRoles), ","), - "has_rsa_public_key": user.HasRsaPublicKey, - "email": user.Email, - "display_name": user.DisplayName, - "first_name": user.FirstName, - "last_name": user.LastName, + for i, user := range users { + user := user + var userDescription []map[string]any + if d.Get("with_describe").(bool) { + describeResult, err := client.Users.Describe(ctx, user.ID()) + if err != nil { + return diag.FromErr(err) + } + userDescription = schemas.UserDescriptionToSchema(*describeResult) } + + var userParameters []map[string]any + if d.Get("with_parameters").(bool) { + parameters, err := client.Users.ShowParameters(ctx, user.ID()) + if err != nil { + return diag.FromErr(err) + } + userParameters = []map[string]any{schemas.UserParametersToSchema(parameters)} + } + + flattenedUsers[i] = map[string]any{ + resources.ShowOutputAttributeName: []map[string]any{schemas.UserToSchema(&user)}, + resources.DescribeOutputAttributeName: userDescription, + resources.ParametersAttributeName: userParameters, + } + } + + err = d.Set("users", flattenedUsers) + if err != nil { + return diag.FromErr(err) } - return d.Set("users", users) + return nil } diff --git a/pkg/schemas/user_describe.go b/pkg/schemas/user_describe.go new file mode 100644 index 0000000000..8292999d39 --- /dev/null +++ b/pkg/schemas/user_describe.go @@ -0,0 +1,231 @@ +package schemas + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// UserDescribeSchema represents output of DESCRIBE query for the single UserDetails. +var UserDescribeSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "comment": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "login_name": { + Type: schema.TypeString, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + }, + "middle_name": { + Type: schema.TypeString, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + }, + "email": { + Type: schema.TypeString, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Computed: true, + }, + "must_change_password": { + Type: schema.TypeBool, + Computed: true, + }, + "disabled": { + Type: schema.TypeBool, + Computed: true, + }, + "snowflake_lock": { + Type: schema.TypeBool, + Computed: true, + }, + "snowflake_support": { + Type: schema.TypeBool, + Computed: true, + }, + "days_to_expiry": { + Type: schema.TypeFloat, + Computed: true, + }, + "mins_to_unlock": { + Type: schema.TypeInt, + Computed: true, + }, + "default_warehouse": { + Type: schema.TypeString, + Computed: true, + }, + "default_namespace": { + Type: schema.TypeString, + Computed: true, + }, + "default_role": { + Type: schema.TypeString, + Computed: true, + }, + "default_secondary_roles": { + Type: schema.TypeString, + Computed: true, + }, + "ext_authn_duo": { + Type: schema.TypeBool, + Computed: true, + }, + "ext_authn_uid": { + Type: schema.TypeString, + Computed: true, + }, + "mins_to_bypass_mfa": { + Type: schema.TypeInt, + Computed: true, + }, + "mins_to_bypass_network_policy": { + Type: schema.TypeInt, + Computed: true, + }, + "rsa_public_key": { + Type: schema.TypeString, + Computed: true, + }, + "rsa_public_key_fp": { + Type: schema.TypeString, + Computed: true, + }, + "rsa_public_key2": { + Type: schema.TypeString, + Computed: true, + }, + "rsa_public_key2_fp": { + Type: schema.TypeString, + Computed: true, + }, + "password_last_set_time": { + Type: schema.TypeString, + Computed: true, + }, + "custom_landing_page_url": { + Type: schema.TypeString, + Computed: true, + }, + "custom_landing_page_url_flush_next_ui_load": { + Type: schema.TypeBool, + Computed: true, + }, +} + +var _ = UserDescribeSchema + +func UserDescriptionToSchema(userDetails sdk.UserDetails) []map[string]any { + userDetailsSchema := make(map[string]any) + if userDetails.Name != nil { + userDetailsSchema["name"] = userDetails.Name.Value + } + if userDetails.Comment != nil { + userDetailsSchema["comment"] = userDetails.Comment.Value + } + if userDetails.DisplayName != nil { + userDetailsSchema["display_name"] = userDetails.DisplayName.Value + } + if userDetails.LoginName != nil { + userDetailsSchema["login_name"] = userDetails.LoginName.Value + } + if userDetails.FirstName != nil { + userDetailsSchema["first_name"] = userDetails.FirstName.Value + } + if userDetails.MiddleName != nil { + userDetailsSchema["middle_name"] = userDetails.MiddleName.Value + } + if userDetails.LastName != nil { + userDetailsSchema["last_name"] = userDetails.LastName.Value + } + if userDetails.Email != nil { + userDetailsSchema["email"] = userDetails.Email.Value + } + if userDetails.Password != nil { + userDetailsSchema["password"] = userDetails.Password.Value + } + if userDetails.MustChangePassword != nil { + userDetailsSchema["must_change_password"] = userDetails.MustChangePassword.Value + } + if userDetails.Disabled != nil { + userDetailsSchema["disabled"] = userDetails.Disabled.Value + } + if userDetails.SnowflakeLock != nil { + userDetailsSchema["snowflake_lock"] = userDetails.SnowflakeLock.Value + } + if userDetails.SnowflakeSupport != nil { + userDetailsSchema["snowflake_support"] = userDetails.SnowflakeSupport.Value + } + if userDetails.DaysToExpiry != nil && userDetails.DaysToExpiry.Value != nil { + userDetailsSchema["days_to_expiry"] = *userDetails.DaysToExpiry.Value + } + if userDetails.MinsToUnlock != nil && userDetails.MinsToUnlock.Value != nil { + userDetailsSchema["mins_to_unlock"] = *userDetails.MinsToUnlock.Value + } + if userDetails.DefaultWarehouse != nil { + userDetailsSchema["default_warehouse"] = userDetails.DefaultWarehouse.Value + } + if userDetails.DefaultNamespace != nil { + userDetailsSchema["default_namespace"] = userDetails.DefaultNamespace.Value + } + if userDetails.DefaultRole != nil { + userDetailsSchema["default_role"] = userDetails.DefaultRole.Value + } + if userDetails.DefaultSecondaryRoles != nil { + userDetailsSchema["default_secondary_roles"] = userDetails.DefaultSecondaryRoles.Value + } + if userDetails.ExtAuthnDuo != nil { + userDetailsSchema["ext_authn_duo"] = userDetails.ExtAuthnDuo.Value + } + if userDetails.ExtAuthnUid != nil { + userDetailsSchema["ext_authn_uid"] = userDetails.ExtAuthnUid.Value + } + if userDetails.MinsToBypassMfa != nil && userDetails.MinsToBypassMfa.Value != nil { + userDetailsSchema["mins_to_bypass_mfa"] = userDetails.MinsToBypassMfa.Value + } + if userDetails.MinsToBypassNetworkPolicy != nil && userDetails.MinsToBypassNetworkPolicy.Value != nil { + userDetailsSchema["mins_to_bypass_network_policy"] = userDetails.MinsToBypassNetworkPolicy.Value + } + if userDetails.RsaPublicKey != nil { + userDetailsSchema["rsa_public_key"] = userDetails.RsaPublicKey.Value + } + if userDetails.RsaPublicKeyFp != nil { + userDetailsSchema["rsa_public_key_fp"] = userDetails.RsaPublicKeyFp.Value + } + if userDetails.RsaPublicKey2 != nil { + userDetailsSchema["rsa_public_key2"] = userDetails.RsaPublicKey2.Value + } + if userDetails.RsaPublicKey2Fp != nil { + userDetailsSchema["rsa_public_key2_fp"] = userDetails.RsaPublicKey2Fp.Value + } + if userDetails.PasswordLastSetTime != nil { + userDetailsSchema["password_last_set_time"] = userDetails.PasswordLastSetTime.Value + } + if userDetails.CustomLandingPageUrl != nil { + userDetailsSchema["custom_landing_page_url"] = userDetails.CustomLandingPageUrl.Value + } + if userDetails.CustomLandingPageUrlFlushNextUiLoad != nil { + userDetailsSchema["custom_landing_page_url_flush_next_ui_load"] = userDetails.CustomLandingPageUrlFlushNextUiLoad.Value + } + return []map[string]any{ + userDetailsSchema, + } +} + +var _ = UserDescriptionToSchema From 10a3e7b993ba1fdae16f4694fc2d8846b68312ad Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 11:50:49 +0200 Subject: [PATCH 03/12] Pass really basic test --- pkg/datasources/users_acceptance_test.go | 75 +++++++++++++++--------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/pkg/datasources/users_acceptance_test.go b/pkg/datasources/users_acceptance_test.go index 4f06e8419d..661641b841 100644 --- a/pkg/datasources/users_acceptance_test.go +++ b/pkg/datasources/users_acceptance_test.go @@ -1,56 +1,73 @@ package datasources_test import ( - "fmt" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -func TestAcc_Users(t *testing.T) { - userName := acc.TestClient().Ids.Alpha() +func TestAcc_Users_Complete(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.ConfigureClientOnce) + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + comment := random.Comment() + pass := random.Password() + key1, _ := random.GenerateRSAPublicKey(t) + key2, _ := random.GenerateRSAPublicKey(t) + + userModelAllAttributes := model.User("u", id.Name()). + WithPassword(pass). + WithLoginName(id.Name() + "_login"). + WithDisplayName("Display Name"). + WithFirstName("Jan"). + WithMiddleName("Jakub"). + WithLastName("Testowski"). + WithEmail("fake@email.com"). + WithMustChangePassword("true"). + WithDisabled("false"). + WithDaysToExpiry(8). + WithMinsToUnlock(9). + WithDefaultWarehouse("some_warehouse"). + WithDefaultNamespace("some.namespace"). + WithDefaultRole("some_role"). + WithDefaultSecondaryRolesStringList("ALL"). + WithMinsToBypassMfa(10). + WithRsaPublicKey(key1). + WithRsaPublicKey2(key2). + WithComment(comment). + WithDisableMfa("true") + resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, + CheckDestroy: acc.CheckDestroy(t, resources.User), Steps: []resource.TestStep{ { - Config: users(userName), + Config: config.FromModel(t, userModelAllAttributes) + datasourceWithLike(), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.snowflake_users.u", "users.#"), - resource.TestCheckResourceAttr("data.snowflake_users.u", "users.#", "1"), - resource.TestCheckResourceAttr("data.snowflake_users.u", "users.0.name", userName), - resource.TestCheckResourceAttr("data.snowflake_users.u", "users.0.disabled", "false"), + resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1"), + + resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.name", id.Name()), ), }, }, }) } -func users(userName string) string { - return fmt.Sprintf(` - resource "snowflake_user" "u" { - name = "%s" - comment = "test comment" - login_name = "%s_login" - display_name = "Display Name" - first_name = "Alex" - last_name = "Kita" - email = "fake@email.com" - disabled = false - default_warehouse="foo" - default_role="FOO" - default_secondary_roles = ["ALL"] - default_namespace="FOO" - } - - data snowflake_users "u" { - pattern = "%s" - depends_on = [snowflake_user.u] +func datasourceWithLike() string { + return ` + data "snowflake_users" "test" { + like = snowflake_user.u.name } - `, userName, userName, userName) +` } From 6f1361ed9fe468b59fb05bb7a511a2bcdaecd42e Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 11:52:52 +0200 Subject: [PATCH 04/12] Add comment to handle sensitive in show output schemas --- pkg/schemas/user_ext.go | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pkg/schemas/user_ext.go diff --git a/pkg/schemas/user_ext.go b/pkg/schemas/user_ext.go new file mode 100644 index 0000000000..696d7b7124 --- /dev/null +++ b/pkg/schemas/user_ext.go @@ -0,0 +1,3 @@ +package schemas + +// add sensitive to show output/describe output From 9fef8f1f0c944633d779b064272785a2ca96de31 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 13:33:04 +0200 Subject: [PATCH 05/12] Add a way to use show_output assertions in data sources test --- .../assert/resource_assertions.go | 20 +++++++++++++++---- .../users_datasource_show_output_ext.go | 18 +++++++++++++++++ pkg/datasources/users_acceptance_test.go | 17 +++++++++++++--- pkg/schemas/user_ext.go | 16 ++++++++++++++- 4 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/users_datasource_show_output_ext.go diff --git a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go index 13fa6b1ae2..11cdedf6eb 100644 --- a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go +++ b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go @@ -21,10 +21,11 @@ var ( // ResourceAssert is an embeddable struct that should be used to construct new resource assertions (for resource, show output, parameters, etc.). // It implements both TestCheckFuncProvider and ImportStateCheckFuncProvider which makes it easy to create new resource assertions. type ResourceAssert struct { - name string - id string - prefix string - assertions []ResourceAssertion + name string + id string + prefix string + assertions []ResourceAssertion + additionalPrefix string } // NewResourceAssert creates a ResourceAssert where the resource name should be used as a key for assertions. @@ -45,6 +46,16 @@ func NewImportedResourceAssert(id string, prefix string) *ResourceAssert { } } +// NewDatasourceAssert creates a ResourceAssert for data sources. +func NewDatasourceAssert(name string, prefix string, additionalPrefix string) *ResourceAssert { + return &ResourceAssert{ + name: name, + prefix: prefix, + assertions: make([]ResourceAssertion, 0), + additionalPrefix: additionalPrefix, + } +} + type resourceAssertionType string const ( @@ -59,6 +70,7 @@ type ResourceAssertion struct { } func (r *ResourceAssert) AddAssertion(assertion ResourceAssertion) { + assertion.fieldName = r.additionalPrefix + assertion.fieldName r.assertions = append(r.assertions, assertion) } diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/users_datasource_show_output_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/users_datasource_show_output_ext.go new file mode 100644 index 0000000000..76887e6bcd --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/users_datasource_show_output_ext.go @@ -0,0 +1,18 @@ +package resourceshowoutputassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +// UsersDatasourceShowOutput is a temporary workaround to have better show output assertions in data source acceptance tests. +func UsersDatasourceShowOutput(t *testing.T, name string) *UserShowOutputAssert { + t.Helper() + + u := UserShowOutputAssert{ + ResourceAssert: assert.NewDatasourceAssert("data."+name, "show_output", "users.0."), + } + u.AddAssertion(assert.ValueSet("show_output.#", "1")) + return &u +} diff --git a/pkg/datasources/users_acceptance_test.go b/pkg/datasources/users_acceptance_test.go index 661641b841..52f603227c 100644 --- a/pkg/datasources/users_acceptance_test.go +++ b/pkg/datasources/users_acceptance_test.go @@ -1,10 +1,13 @@ package datasources_test import ( + "fmt" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" @@ -54,10 +57,18 @@ func TestAcc_Users_Complete(t *testing.T) { Steps: []resource.TestStep{ { Config: config.FromModel(t, userModelAllAttributes) + datasourceWithLike(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1"), + Check: assert.AssertThat(t, + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1")), - resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.name", id.Name()), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.name", id.Name())), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.show_output.0.created_on")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.login_name", fmt.Sprintf("%s_LOGIN", id.Name()))), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.display_name", "Display Name")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.first_name", "Jan")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.last_name", "Testowski")), + + resourceshowoutputassert.UsersDatasourceShowOutput(t, "snowflake_users.test"). + HasName(id.Name()), ), }, }, diff --git a/pkg/schemas/user_ext.go b/pkg/schemas/user_ext.go index 696d7b7124..2b261bb08f 100644 --- a/pkg/schemas/user_ext.go +++ b/pkg/schemas/user_ext.go @@ -1,3 +1,17 @@ package schemas -// add sensitive to show output/describe output +// This init is currently necessary to add sensitiveness to schemas which are generated (without such an underlying functionality yet). +func init() { + // show output does not contain password or middle_name + ShowUserSchema["login_name"].Sensitive = true + ShowUserSchema["first_name"].Sensitive = true + ShowUserSchema["last_name"].Sensitive = true + ShowUserSchema["email"].Sensitive = true + + UserDescribeSchema["login_name"].Sensitive = true + UserDescribeSchema["first_name"].Sensitive = true + UserDescribeSchema["middle_name"].Sensitive = true + UserDescribeSchema["last_name"].Sensitive = true + UserDescribeSchema["email"].Sensitive = true + UserDescribeSchema["password"].Sensitive = true +} From db4b05ac774a841892d486861a338a7e66ca392f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 13:50:50 +0200 Subject: [PATCH 06/12] Add assertions to show output in users data source acc test --- .../assert/resource_assertions.go | 17 ++++++++++-- .../user_show_output_ext.go | 25 +++++++++++++++++ pkg/datasources/users_acceptance_test.go | 27 ++++++++++++------- 3 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/user_show_output_ext.go diff --git a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go index 11cdedf6eb..3a404a4bcf 100644 --- a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go +++ b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go @@ -59,8 +59,9 @@ func NewDatasourceAssert(name string, prefix string, additionalPrefix string) *R type resourceAssertionType string const ( - resourceAssertionTypeValueSet = "VALUE_SET" - resourceAssertionTypeValueNotSet = "VALUE_NOT_SET" + resourceAssertionTypeValueSet = "VALUE_SET" + resourceAssertionTypeValueNotSet = "VALUE_NOT_SET" + resourceAssertionTypeValuePresent = "VALUE_PRESENT" ) type ResourceAssertion struct { @@ -104,6 +105,11 @@ func ResourceShowOutputValueSet(fieldName string, expected string) ResourceAsser return ResourceAssertion{fieldName: showOutputPrefix + fieldName, expectedValue: expected, resourceAssertionType: resourceAssertionTypeValueSet} } +// TODO [SNOW-1501905]: generate assertions with resourceAssertionTypeValuePresent +func ResourceShowOutputValuePresent(fieldName string) ResourceAssertion { + return ResourceAssertion{fieldName: showOutputPrefix + fieldName, resourceAssertionType: resourceAssertionTypeValuePresent} +} + const ( parametersPrefix = "parameters.0." parametersValueSuffix = ".0.value" @@ -149,6 +155,11 @@ func (r *ResourceAssert) ToTerraformTestCheckFunc(t *testing.T) resource.TestChe errCut, _ := strings.CutPrefix(err.Error(), fmt.Sprintf("%s: ", r.name)) result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %s", r.name, r.prefix, i+1, len(r.assertions), errCut)) } + case resourceAssertionTypeValuePresent: + if err := resource.TestCheckResourceAttrSet(r.name, a.fieldName)(s); err != nil { + errCut, _ := strings.CutPrefix(err.Error(), fmt.Sprintf("%s: ", r.name)) + result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %s", r.name, r.prefix, i+1, len(r.assertions), errCut)) + } } } @@ -171,6 +182,8 @@ func (r *ResourceAssert) ToTerraformImportStateCheckFunc(t *testing.T) resource. } case resourceAssertionTypeValueNotSet: panic("implement") + case resourceAssertionTypeValuePresent: + panic("implement") } } diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/user_show_output_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/user_show_output_ext.go new file mode 100644 index 0000000000..9daf9a672f --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/user_show_output_ext.go @@ -0,0 +1,25 @@ +package resourceshowoutputassert + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +func (u *UserShowOutputAssert) HasCreatedOnNotEmpty() *UserShowOutputAssert { + u.AddAssertion(assert.ResourceShowOutputValuePresent("created_on")) + return u +} + +func (u *UserShowOutputAssert) HasDaysToExpiryNotEmpty() *UserShowOutputAssert { + u.AddAssertion(assert.ResourceShowOutputValuePresent("days_to_expiry")) + return u +} + +func (u *UserShowOutputAssert) HasMinsToUnlockNotEmpty() *UserShowOutputAssert { + u.AddAssertion(assert.ResourceShowOutputValuePresent("mins_to_unlock")) + return u +} + +func (u *UserShowOutputAssert) HasMinsToBypassMfaNotEmpty() *UserShowOutputAssert { + u.AddAssertion(assert.ResourceShowOutputValuePresent("mins_to_bypass_mfa")) + return u +} diff --git a/pkg/datasources/users_acceptance_test.go b/pkg/datasources/users_acceptance_test.go index 52f603227c..bd17cab69a 100644 --- a/pkg/datasources/users_acceptance_test.go +++ b/pkg/datasources/users_acceptance_test.go @@ -59,16 +59,25 @@ func TestAcc_Users_Complete(t *testing.T) { Config: config.FromModel(t, userModelAllAttributes) + datasourceWithLike(), Check: assert.AssertThat(t, assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1")), - - assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.name", id.Name())), - assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.show_output.0.created_on")), - assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.login_name", fmt.Sprintf("%s_LOGIN", id.Name()))), - assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.display_name", "Display Name")), - assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.first_name", "Jan")), - assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.show_output.0.last_name", "Testowski")), - resourceshowoutputassert.UsersDatasourceShowOutput(t, "snowflake_users.test"). - HasName(id.Name()), + HasName(id.Name()). + HasCreatedOnNotEmpty(). + HasLoginName(fmt.Sprintf("%s_LOGIN", id.Name())). + HasDisplayName("Display Name"). + HasFirstName("Jan"). + HasLastName("Testowski"). + HasEmail("fake@email.com"). + HasMustChangePassword(true). + HasDisabled(false). + HasDaysToExpiryNotEmpty(). + HasMinsToUnlockNotEmpty(). + HasDefaultWarehouse("some_warehouse"). + HasDefaultNamespace("some.namespace"). + HasDefaultRole("some_role"). + HasDefaultSecondaryRoles(`["ALL"]`). + HasMinsToBypassMfaNotEmpty(). + HasHasRsaPublicKey(true). + HasComment(comment), ), }, }, From 09ae8f621a25dca26188553a7cad30fb5da4d47f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 13:55:39 +0200 Subject: [PATCH 07/12] Add assertions to parameters in users data source acc test --- .../users_datasource_parameters_ext.go | 18 ++++++++++++++++++ pkg/datasources/users_acceptance_test.go | 3 +++ 2 files changed, 21 insertions(+) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceparametersassert/users_datasource_parameters_ext.go diff --git a/pkg/acceptance/bettertestspoc/assert/resourceparametersassert/users_datasource_parameters_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceparametersassert/users_datasource_parameters_ext.go new file mode 100644 index 0000000000..77e2af50b9 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceparametersassert/users_datasource_parameters_ext.go @@ -0,0 +1,18 @@ +package resourceparametersassert + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +// UsersDatasourceParameters is a temporary workaround to have better parameter assertions in data source acceptance tests. +func UsersDatasourceParameters(t *testing.T, name string) *UserResourceParametersAssert { + t.Helper() + + u := UserResourceParametersAssert{ + ResourceAssert: assert.NewDatasourceAssert("data."+name, "parameters", "users.0."), + } + u.AddAssertion(assert.ValueSet("parameters.#", "1")) + return &u +} diff --git a/pkg/datasources/users_acceptance_test.go b/pkg/datasources/users_acceptance_test.go index bd17cab69a..6989f05a6d 100644 --- a/pkg/datasources/users_acceptance_test.go +++ b/pkg/datasources/users_acceptance_test.go @@ -7,6 +7,7 @@ import ( acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceparametersassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" @@ -78,6 +79,8 @@ func TestAcc_Users_Complete(t *testing.T) { HasMinsToBypassMfaNotEmpty(). HasHasRsaPublicKey(true). HasComment(comment), + resourceparametersassert.UsersDatasourceParameters(t, "snowflake_users.test"). + HasAllDefaults(), ), }, }, From 71af1da23500aa7d11e1a69038082e35127f06a4 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 14:07:23 +0200 Subject: [PATCH 08/12] Check user details --- pkg/datasources/users_acceptance_test.go | 35 ++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/pkg/datasources/users_acceptance_test.go b/pkg/datasources/users_acceptance_test.go index 6989f05a6d..f04c74a0cc 100644 --- a/pkg/datasources/users_acceptance_test.go +++ b/pkg/datasources/users_acceptance_test.go @@ -24,8 +24,8 @@ func TestAcc_Users_Complete(t *testing.T) { comment := random.Comment() pass := random.Password() - key1, _ := random.GenerateRSAPublicKey(t) - key2, _ := random.GenerateRSAPublicKey(t) + key1, key1Fp := random.GenerateRSAPublicKey(t) + key2, key2Fp := random.GenerateRSAPublicKey(t) userModelAllAttributes := model.User("u", id.Name()). WithPassword(pass). @@ -70,6 +70,7 @@ func TestAcc_Users_Complete(t *testing.T) { HasEmail("fake@email.com"). HasMustChangePassword(true). HasDisabled(false). + HasSnowflakeLock(false). HasDaysToExpiryNotEmpty(). HasMinsToUnlockNotEmpty(). HasDefaultWarehouse("some_warehouse"). @@ -81,6 +82,36 @@ func TestAcc_Users_Complete(t *testing.T) { HasComment(comment), resourceparametersassert.UsersDatasourceParameters(t, "snowflake_users.test"). HasAllDefaults(), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.name", id.Name())), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.comment", comment)), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.display_name", "Display Name")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.login_name", fmt.Sprintf("%s_LOGIN", id.Name()))), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.first_name", "Jan")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.middle_name", "Jakub")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.last_name", "Testowski")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.email", "fake@email.com")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.password", "********")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.must_change_password", "true")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.disabled", "false")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_lock", "false")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_support", "false")), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.days_to_expiry")), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_unlock")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_warehouse", "some_warehouse")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_namespace", "some.namespace")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_role", "some_role")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_secondary_roles", `["ALL"]`)), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_duo", "false")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_uid", "")), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_mfa")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_network_policy", "0")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key", key1)), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key_fp", "SHA256:"+key1Fp)), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2", key2)), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2_fp", "SHA256:"+key2Fp)), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.password_last_set_time")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url_flush_next_ui_load", "false")), ), }, }, From e477ebf1d2275914ce35bc4f4c0aa31017fd47ff Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 14:26:42 +0200 Subject: [PATCH 09/12] Finish users datasource acceptance tests --- .../TestAcc_Users/without_user/test.tf | 10 ++ pkg/datasources/users_acceptance_test.go | 149 +++++++++++++++++- 2 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Users/without_user/test.tf diff --git a/pkg/datasources/testdata/TestAcc_Users/without_user/test.tf b/pkg/datasources/testdata/TestAcc_Users/without_user/test.tf new file mode 100644 index 0000000000..622994fe7f --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Users/without_user/test.tf @@ -0,0 +1,10 @@ +data "snowflake_users" "test" { + like = "non-existing-user" + + lifecycle { + postcondition { + condition = length(self.users) > 0 + error_message = "there should be at least one user" + } + } +} diff --git a/pkg/datasources/users_acceptance_test.go b/pkg/datasources/users_acceptance_test.go index f04c74a0cc..1ea103e079 100644 --- a/pkg/datasources/users_acceptance_test.go +++ b/pkg/datasources/users_acceptance_test.go @@ -2,6 +2,8 @@ package datasources_test import ( "fmt" + "regexp" + "strings" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" @@ -12,14 +14,12 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/tfversion" ) func TestAcc_Users_Complete(t *testing.T) { - _ = testenvs.GetOrSkipTest(t, testenvs.ConfigureClientOnce) id := acc.TestClient().Ids.RandomAccountObjectIdentifier() comment := random.Comment() @@ -27,6 +27,7 @@ func TestAcc_Users_Complete(t *testing.T) { key1, key1Fp := random.GenerateRSAPublicKey(t) key2, key2Fp := random.GenerateRSAPublicKey(t) + userModelNoAttributes := model.User("u", id.Name()) userModelAllAttributes := model.User("u", id.Name()). WithPassword(pass). WithLoginName(id.Name() + "_login"). @@ -114,6 +115,105 @@ func TestAcc_Users_Complete(t *testing.T) { assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url_flush_next_ui_load", "false")), ), }, + { + Config: config.FromModel(t, userModelNoAttributes) + datasourceWithLike(), + Check: assert.AssertThat(t, + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1")), + resourceshowoutputassert.UsersDatasourceShowOutput(t, "snowflake_users.test"). + HasName(id.Name()). + HasCreatedOnNotEmpty(). + HasLoginName(strings.ToUpper(id.Name())). + HasDisplayName(""). + HasFirstName(""). + HasLastName(""). + HasEmail(""). + HasMustChangePassword(false). + HasDisabled(false). + HasSnowflakeLock(false). + HasDaysToExpiry(""). + HasMinsToUnlock(""). + HasDefaultWarehouse(""). + HasDefaultNamespace(""). + HasDefaultRole(""). + HasDefaultSecondaryRoles(""). + HasMinsToBypassMfa(""). + HasHasRsaPublicKey(false). + HasComment(""), + resourceparametersassert.UsersDatasourceParameters(t, "snowflake_users.test"). + HasAllDefaults(), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.name", id.Name())), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.comment", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.display_name", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.login_name", strings.ToUpper(id.Name()))), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.first_name", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.middle_name", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.last_name", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.email", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.password", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.must_change_password", "false")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.disabled", "false")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_lock", "false")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_support", "false")), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.days_to_expiry")), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_unlock")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_warehouse", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_namespace", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_role", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_secondary_roles", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_duo", "false")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_uid", "")), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_mfa")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_network_policy", "0")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key_fp", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2_fp", "")), + assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.password_last_set_time")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url", "")), + assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url_flush_next_ui_load", "false")), + ), + }, + }, + }) +} + +func TestAcc_Users_DifferentFiltering(t *testing.T) { + prefix := random.AlphaN(4) + id := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix) + id2 := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix) + id3 := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + userModel := model.User("u", id.Name()) + user2Model := model.User("u2", id2.Name()) + user3Model := model.User("u3", id3.Name()) + + allUsersConfig := config.FromModel(t, userModel) + config.FromModel(t, user2Model) + config.FromModel(t, user3Model) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.User), + Steps: []resource.TestStep{ + { + Config: allUsersConfig + datasourceWithLikeMultipleUsers(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1"), + ), + }, + { + Config: allUsersConfig + datasourceWithStartsWithMultipleUsers(prefix), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "2"), + ), + }, + { + Config: allUsersConfig + datasourceWithLimitMultipleUsers(1, prefix), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1"), + ), + }, }, }) } @@ -125,3 +225,48 @@ func datasourceWithLike() string { } ` } + +func datasourceWithLikeMultipleUsers() string { + return ` + data "snowflake_users" "test" { + depends_on = [snowflake_user.u, snowflake_user.u2, snowflake_user.u3] + like = snowflake_user.u.name + } +` +} + +func datasourceWithStartsWithMultipleUsers(startsWith string) string { + return fmt.Sprintf(` + data "snowflake_users" "test" { + depends_on = [snowflake_user.u, snowflake_user.u2, snowflake_user.u3] + starts_with = "%s" + } +`, startsWith) +} + +func datasourceWithLimitMultipleUsers(rows int, from string) string { + return fmt.Sprintf(` + data "snowflake_users" "test" { + depends_on = [snowflake_user.u, snowflake_user.u2, snowflake_user.u3] + limit { + rows = %d + from = "%s" + } + } +`, rows, from) +} + +func TestAcc_Users_UserNotFound_WithPostConditions(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Users/without_user"), + ExpectError: regexp.MustCompile("there should be at least one user"), + }, + }, + }) +} From fa7156e63d568b009e68e473882325115c91e2a3 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 14:36:04 +0200 Subject: [PATCH 10/12] Add users data source changes to the migration guide References: #2902 --- MIGRATION_GUIDE.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index eec752d556..90a84e5c2b 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -255,6 +255,19 @@ Type changes: Validation changes: - `default_secondary_roles` - only 1-element lists with `"ALL"` element are now supported. Check [Snowflake docs](https://docs.snowflake.com/en/sql-reference/sql/create-user#optional-object-properties-objectproperties) for more details. +#### *(breaking change)* refactored snowflake_users datasource +Changes: +- account checking logic was entirely removed +- `pattern` renamed to `like` +- `like`, `starts_with`, and `limit` filters added +- `SHOW USERS` output is enclosed in `show_output` field inside `users` (all the previous fields in `users` map were removed) +- Added outputs from **DESC USER** and **SHOW PARAMETERS IN USER** (they can be turned off by declaring `with_describe = false` and `with_parameters = false`, **they're turned on by default**). + The additional parameters call **DESC USER** (with `with_describe` turned on) and **SHOW PARAMETERS IN USER** (with `with_parameters` turned on) **per user** returned by **SHOW USERS**. + The outputs of both commands are held in `users` entry, where **DESC USER** is saved in the `describe_output` field, and **SHOW PARAMETERS IN USER** in the `parameters` field. + It's important to limit the records and calls to Snowflake to the minimum. That's why we recommend assessing which information you need from the data source and then providing strong filters and turning off additional fields for better plan performance. + +Connected issues: [#2902](https://github.com/Snowflake-Labs/terraform-provider-snowflake/pull/2902) + ## v0.94.0 ➞ v0.94.1 ### changes in snowflake_schema From 1787d5efdbb854cd0d5cc0208ddbda15a2f9b451 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 14:41:09 +0200 Subject: [PATCH 11/12] Update users data source documentation --- docs/data-sources/users.md | 926 +++++++++++++++++- .../snowflake_users/data-source.tf | 76 +- templates/data-sources/users.md.tmpl | 24 + 3 files changed, 1015 insertions(+), 11 deletions(-) create mode 100644 templates/data-sources/users.md.tmpl diff --git a/docs/data-sources/users.md b/docs/data-sources/users.md index 6a3bc45366..3ba291158e 100644 --- a/docs/data-sources/users.md +++ b/docs/data-sources/users.md @@ -2,48 +2,958 @@ page_title: "snowflake_users Data Source - terraform-provider-snowflake" subcategory: "" description: |- - + Datasource used to get details of filtered users. Filtering is aligned with the current possibilities for SHOW USERS https://docs.snowflake.com/en/sql-reference/sql/show-users query. The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. --- -# snowflake_users (Data Source) +!> **V1 release candidate** This data source was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +# snowflake_users (Data Source) +Datasource used to get details of filtered users. Filtering is aligned with the current possibilities for [SHOW USERS](https://docs.snowflake.com/en/sql-reference/sql/show-users) query. The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. ## Example Usage ```terraform -data "snowflake_users" "current" { - pattern = "user1" +# Simple usage +data "snowflake_users" "simple" { +} + +output "simple_output" { + value = data.snowflake_users.simple.users +} + +# Filtering (like) +data "snowflake_users" "like" { + like = "user-name" +} + +output "like_output" { + value = data.snowflake_users.like.users +} + +# Filtering (starts_with) +data "snowflake_users" "starts_with" { + starts_with = "user-" +} + +output "starts_with_output" { + value = data.snowflake_users.starts_with.users +} + +# Filtering (limit) +data "snowflake_users" "limit" { + limit { + rows = 10 + from = "user-" + } +} + +output "limit_output" { + value = data.snowflake_users.limit.users +} + +# Without additional data (to limit the number of calls make for every found user) +data "snowflake_users" "only_show" { + # with_describe is turned on by default and it calls DESCRIBE USER for every user found and attaches its output to users.*.describe_output field + with_describe = false + + # with_parameters is turned on by default and it calls SHOW PARAMETERS FOR USER for every user found and attaches its output to users.*.parameters field + with_parameters = false +} + +output "only_show_output" { + value = data.snowflake_users.only_show.users +} + +# Ensure the number of users is equal to at least one element (with the use of postcondition) +data "snowflake_users" "assert_with_postcondition" { + starts_with = "user-name" + lifecycle { + postcondition { + condition = length(self.users) > 0 + error_message = "there should be at least one user" + } + } +} + +# Ensure the number of users is equal to at exactly one element (with the use of check block) +check "user_check" { + data "snowflake_users" "assert_with_check_block" { + like = "user-name" + } + + assert { + condition = length(data.snowflake_users.assert_with_check_block.users) == 1 + error_message = "users filtered by '${data.snowflake_users.assert_with_check_block.like}' returned ${length(data.snowflake_users.assert_with_check_block.users)} users where one was expected" + } } ``` ## Schema -### Required +### Optional -- `pattern` (String) Users pattern for which to return metadata. Please refer to LIKE keyword from snowflake documentation : https://docs.snowflake.com/en/sql-reference/sql/show-users.html#parameters +- `like` (String) Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`). +- `limit` (Block List, Max: 1) Limits the number of rows returned. If the `limit.from` is set, then the limit wll start from the first element matched by the expression. The expression is only used to match with the first element, later on the elements are not matched by the prefix, but you can enforce a certain pattern with `starts_with` or `like`. (see [below for nested schema](#nestedblock--limit)) +- `starts_with` (String) Filters the output with **case-sensitive** characters indicating the beginning of the object name. +- `with_describe` (Boolean) Runs DESC USER for each user returned by SHOW USERS. The output of describe is saved to the description field. By default this value is set to true. +- `with_parameters` (Boolean) Runs SHOW PARAMETERS FOR USER for each user returned by SHOW USERS. The output of describe is saved to the parameters field as a map. By default this value is set to true. ### Read-Only - `id` (String) The ID of this resource. -- `users` (List of Object) The users in the database (see [below for nested schema](#nestedatt--users)) +- `users` (List of Object) Holds the aggregated output of all user details queries. (see [below for nested schema](#nestedatt--users)) + + +### Nested Schema for `limit` + +Required: + +- `rows` (Number) The maximum number of rows to return. + +Optional: + +- `from` (String) Specifies a **case-sensitive** pattern that is used to match object name. After the first match, the limit on the number of rows will be applied. + ### Nested Schema for `users` Read-Only: +- `describe_output` (List of Object) (see [below for nested schema](#nestedobjatt--users--describe_output)) +- `parameters` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters)) +- `show_output` (List of Object) (see [below for nested schema](#nestedobjatt--users--show_output)) + + +### Nested Schema for `users.describe_output` + +Read-Only: + +- `comment` (String) +- `custom_landing_page_url` (String) +- `custom_landing_page_url_flush_next_ui_load` (Boolean) +- `days_to_expiry` (Number) +- `default_namespace` (String) +- `default_role` (String) +- `default_secondary_roles` (String) +- `default_warehouse` (String) +- `disabled` (Boolean) +- `display_name` (String) +- `email` (String) +- `ext_authn_duo` (Boolean) +- `ext_authn_uid` (String) +- `first_name` (String) +- `last_name` (String) +- `login_name` (String) +- `middle_name` (String) +- `mins_to_bypass_mfa` (Number) +- `mins_to_bypass_network_policy` (Number) +- `mins_to_unlock` (Number) +- `must_change_password` (Boolean) +- `name` (String) +- `password` (String) +- `password_last_set_time` (String) +- `rsa_public_key` (String) +- `rsa_public_key2` (String) +- `rsa_public_key2_fp` (String) +- `rsa_public_key_fp` (String) +- `snowflake_lock` (Boolean) +- `snowflake_support` (Boolean) + + + +### Nested Schema for `users.parameters` + +Read-Only: + +- `abort_detached_query` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--abort_detached_query)) +- `autocommit` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--autocommit)) +- `binary_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--binary_input_format)) +- `binary_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--binary_output_format)) +- `client_memory_limit` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_memory_limit)) +- `client_metadata_request_use_connection_ctx` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_metadata_request_use_connection_ctx)) +- `client_prefetch_threads` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_prefetch_threads)) +- `client_result_chunk_size` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_result_chunk_size)) +- `client_result_column_case_insensitive` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_result_column_case_insensitive)) +- `client_session_keep_alive` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_session_keep_alive)) +- `client_session_keep_alive_heartbeat_frequency` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_session_keep_alive_heartbeat_frequency)) +- `client_timestamp_type_mapping` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_timestamp_type_mapping)) +- `date_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--date_input_format)) +- `date_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--date_output_format)) +- `enable_unload_physical_type_optimization` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--enable_unload_physical_type_optimization)) +- `enable_unredacted_query_syntax_error` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--enable_unredacted_query_syntax_error)) +- `error_on_nondeterministic_merge` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--error_on_nondeterministic_merge)) +- `error_on_nondeterministic_update` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--error_on_nondeterministic_update)) +- `geography_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--geography_output_format)) +- `geometry_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--geometry_output_format)) +- `jdbc_treat_decimal_as_int` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--jdbc_treat_decimal_as_int)) +- `jdbc_treat_timestamp_ntz_as_utc` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--jdbc_treat_timestamp_ntz_as_utc)) +- `jdbc_use_session_timezone` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--jdbc_use_session_timezone)) +- `json_indent` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--json_indent)) +- `lock_timeout` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--lock_timeout)) +- `log_level` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--log_level)) +- `multi_statement_count` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--multi_statement_count)) +- `network_policy` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--network_policy)) +- `noorder_sequence_as_default` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--noorder_sequence_as_default)) +- `odbc_treat_decimal_as_int` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--odbc_treat_decimal_as_int)) +- `prevent_unload_to_internal_stages` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--prevent_unload_to_internal_stages)) +- `query_tag` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--query_tag)) +- `quoted_identifiers_ignore_case` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--quoted_identifiers_ignore_case)) +- `rows_per_resultset` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--rows_per_resultset)) +- `s3_stage_vpce_dns_name` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--s3_stage_vpce_dns_name)) +- `search_path` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--search_path)) +- `simulated_data_sharing_consumer` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--simulated_data_sharing_consumer)) +- `statement_queued_timeout_in_seconds` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--statement_queued_timeout_in_seconds)) +- `statement_timeout_in_seconds` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--statement_timeout_in_seconds)) +- `strict_json_output` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--strict_json_output)) +- `time_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--time_input_format)) +- `time_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--time_output_format)) +- `timestamp_day_is_always_24h` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_day_is_always_24h)) +- `timestamp_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_input_format)) +- `timestamp_ltz_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_ltz_output_format)) +- `timestamp_ntz_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_ntz_output_format)) +- `timestamp_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_output_format)) +- `timestamp_type_mapping` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_type_mapping)) +- `timestamp_tz_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_tz_output_format)) +- `timezone` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timezone)) +- `trace_level` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--trace_level)) +- `transaction_abort_on_error` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--transaction_abort_on_error)) +- `transaction_default_isolation_level` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--transaction_default_isolation_level)) +- `two_digit_century_start` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--two_digit_century_start)) +- `unsupported_ddl_action` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--unsupported_ddl_action)) +- `use_cached_result` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--use_cached_result)) +- `week_of_year_policy` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--week_of_year_policy)) +- `week_start` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--week_start)) + + +### Nested Schema for `users.parameters.abort_detached_query` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.autocommit` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.binary_input_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.binary_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_memory_limit` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_metadata_request_use_connection_ctx` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_prefetch_threads` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_result_chunk_size` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_result_column_case_insensitive` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_session_keep_alive` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_session_keep_alive_heartbeat_frequency` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.client_timestamp_type_mapping` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.date_input_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.date_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.enable_unload_physical_type_optimization` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.enable_unredacted_query_syntax_error` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.error_on_nondeterministic_merge` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.error_on_nondeterministic_update` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.geography_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.geometry_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.jdbc_treat_decimal_as_int` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.jdbc_treat_timestamp_ntz_as_utc` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.jdbc_use_session_timezone` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.json_indent` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.lock_timeout` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.log_level` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.multi_statement_count` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.network_policy` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.noorder_sequence_as_default` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.odbc_treat_decimal_as_int` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.prevent_unload_to_internal_stages` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.query_tag` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.quoted_identifiers_ignore_case` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.rows_per_resultset` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.s3_stage_vpce_dns_name` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.search_path` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.simulated_data_sharing_consumer` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.statement_queued_timeout_in_seconds` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.statement_timeout_in_seconds` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.strict_json_output` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.time_input_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.time_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timestamp_day_is_always_24h` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timestamp_input_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timestamp_ltz_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timestamp_ntz_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timestamp_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timestamp_type_mapping` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timestamp_tz_output_format` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.timezone` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.trace_level` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.transaction_abort_on_error` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.transaction_default_isolation_level` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.two_digit_century_start` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.unsupported_ddl_action` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.use_cached_result` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.week_of_year_policy` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + +### Nested Schema for `users.parameters.week_start` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + + +### Nested Schema for `users.show_output` + +Read-Only: + - `comment` (String) +- `created_on` (String) +- `days_to_expiry` (String) - `default_namespace` (String) - `default_role` (String) -- `default_secondary_roles` (Set of String) +- `default_secondary_roles` (String) - `default_warehouse` (String) - `disabled` (Boolean) - `display_name` (String) - `email` (String) +- `expires_at_time` (String) +- `ext_authn_duo` (Boolean) +- `ext_authn_uid` (String) - `first_name` (String) +- `has_mfa` (Boolean) +- `has_password` (Boolean) - `has_rsa_public_key` (Boolean) - `last_name` (String) +- `last_success_login` (String) +- `locked_until_time` (String) - `login_name` (String) +- `mins_to_bypass_mfa` (String) +- `mins_to_unlock` (String) +- `must_change_password` (Boolean) - `name` (String) +- `owner` (String) +- `snowflake_lock` (Boolean) +- `type` (String) diff --git a/examples/data-sources/snowflake_users/data-source.tf b/examples/data-sources/snowflake_users/data-source.tf index 117dd0d224..1cce26ac20 100644 --- a/examples/data-sources/snowflake_users/data-source.tf +++ b/examples/data-sources/snowflake_users/data-source.tf @@ -1,3 +1,73 @@ -data "snowflake_users" "current" { - pattern = "user1" -} \ No newline at end of file +# Simple usage +data "snowflake_users" "simple" { +} + +output "simple_output" { + value = data.snowflake_users.simple.users +} + +# Filtering (like) +data "snowflake_users" "like" { + like = "user-name" +} + +output "like_output" { + value = data.snowflake_users.like.users +} + +# Filtering (starts_with) +data "snowflake_users" "starts_with" { + starts_with = "user-" +} + +output "starts_with_output" { + value = data.snowflake_users.starts_with.users +} + +# Filtering (limit) +data "snowflake_users" "limit" { + limit { + rows = 10 + from = "user-" + } +} + +output "limit_output" { + value = data.snowflake_users.limit.users +} + +# Without additional data (to limit the number of calls make for every found user) +data "snowflake_users" "only_show" { + # with_describe is turned on by default and it calls DESCRIBE USER for every user found and attaches its output to users.*.describe_output field + with_describe = false + + # with_parameters is turned on by default and it calls SHOW PARAMETERS FOR USER for every user found and attaches its output to users.*.parameters field + with_parameters = false +} + +output "only_show_output" { + value = data.snowflake_users.only_show.users +} + +# Ensure the number of users is equal to at least one element (with the use of postcondition) +data "snowflake_users" "assert_with_postcondition" { + starts_with = "user-name" + lifecycle { + postcondition { + condition = length(self.users) > 0 + error_message = "there should be at least one user" + } + } +} + +# Ensure the number of users is equal to at exactly one element (with the use of check block) +check "user_check" { + data "snowflake_users" "assert_with_check_block" { + like = "user-name" + } + + assert { + condition = length(data.snowflake_users.assert_with_check_block.users) == 1 + error_message = "users filtered by '${data.snowflake_users.assert_with_check_block.like}' returned ${length(data.snowflake_users.assert_with_check_block.users)} users where one was expected" + } +} diff --git a/templates/data-sources/users.md.tmpl b/templates/data-sources/users.md.tmpl new file mode 100644 index 0000000000..d3ff8d9c6c --- /dev/null +++ b/templates/data-sources/users.md.tmpl @@ -0,0 +1,24 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This data source was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/data-sources/%s/data-source.tf" .Name)}} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} From 06438510ca5469e96f37f2831fadc18148741c2b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 2 Sep 2024 17:02:29 +0200 Subject: [PATCH 12/12] Fix failing test --- pkg/resources/database_acceptance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/resources/database_acceptance_test.go b/pkg/resources/database_acceptance_test.go index 9debdb1a3a..ee0efcf51e 100644 --- a/pkg/resources/database_acceptance_test.go +++ b/pkg/resources/database_acceptance_test.go @@ -815,7 +815,7 @@ func TestAcc_Database_IntParameter(t *testing.T) { }, Check: assert.AssertThat(t, assert.Check(resource.TestCheckResourceAttr("snowflake_database.test", "data_retention_time_in_days", "1")), - objectparametersassert.DatabaseParameters(t, id).HasDataRetentionTimeInDays(25).HasDataRetentionTimeInDaysLevel(sdk.ParameterTypeDatabase), + objectparametersassert.DatabaseParameters(t, id).HasDataRetentionTimeInDays(1).HasDataRetentionTimeInDaysLevel(sdk.ParameterTypeDatabase), ), }, // remove the param from config