Skip to content

Commit 6f9c4a5

Browse files
Merge branch 'main' into functions-sdk
2 parents a121f43 + c189782 commit 6f9c4a5

File tree

15 files changed

+351
-91
lines changed

15 files changed

+351
-91
lines changed

docs/resources/password_policy.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ A password policy specifies the requirements that must be met to create and rese
2424
### Optional
2525

2626
- `comment` (String) Adds a comment or overwrites an existing comment for the password policy.
27+
- `history` (Number) Specifies the number of the most recent passwords that Snowflake stores. These stored passwords cannot be repeated when a user updates their password value. The current password value does not count towards the history. When you increase the history value, Snowflake saves the previous values. When you decrease the value, Snowflake saves the stored values up to that value that is set. For example, if the history value is 8 and you change the history value to 3, Snowflake stores the most recent 3 passwords and deletes the 5 older password values from the history. Default: 0 Max: 24
2728
- `if_not_exists` (Boolean) Prevent overwriting a previous password policy with the same name.
2829
- `lockout_time_mins` (Number) Specifies the number of minutes the user account will be locked after exhausting the designated number of password retries (i.e. PASSWORD_MAX_RETRIES). Supported range: 1 to 999, inclusive. Default: 15
2930
- `max_age_days` (Number) Specifies the maximum number of days before the password must be changed. Supported range: 0 to 999, inclusive. A value of zero (i.e. 0) indicates that the password does not need to be changed. Snowflake does not recommend choosing this value for a default account-level password policy or for any user-level policy. Instead, choose a value that meets your internal security guidelines. Default: 90, which means the password must be changed every 90 days.
3031
- `max_length` (Number) Specifies the maximum number of characters the password must contain. This number must be greater than or equal to the sum of PASSWORD_MIN_LENGTH, PASSWORD_MIN_UPPER_CASE_CHARS, and PASSWORD_MIN_LOWER_CASE_CHARS. Supported range: 8 to 256, inclusive. Default: 256
3132
- `max_retries` (Number) Specifies the maximum number of attempts to enter a password before being locked out. Supported range: 1 to 10, inclusive. Default: 5
33+
- `min_age_days` (Number) Specifies the number of days the user must wait before a recently changed password can be changed again. Supported range: 0 to 999, inclusive. Default: 0
3234
- `min_length` (Number) Specifies the minimum number of characters the password must contain. Supported range: 8 to 256, inclusive. Default: 8
3335
- `min_lower_case_chars` (Number) Specifies the minimum number of lowercase characters the password must contain. Supported range: 0 to 256, inclusive. Default: 1
3436
- `min_numeric_chars` (Number) Specifies the minimum number of numeric characters the password must contain. Supported range: 0 to 256, inclusive. Default: 1

pkg/resources/password_policy.go

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ var passwordPolicySchema = map[string]*schema.Schema{
9090
Description: "Specifies the minimum number of special characters the password must contain. Supported range: 0 to 256, inclusive. Default: 1",
9191
ValidateFunc: validation.IntBetween(0, 256),
9292
},
93+
"min_age_days": {
94+
Type: schema.TypeInt,
95+
Optional: true,
96+
Default: 0,
97+
Description: "Specifies the number of days the user must wait before a recently changed password can be changed again. Supported range: 0 to 999, inclusive. Default: 0",
98+
ValidateFunc: validation.IntBetween(0, 999),
99+
},
93100
"max_age_days": {
94101
Type: schema.TypeInt,
95102
Optional: true,
@@ -111,6 +118,13 @@ var passwordPolicySchema = map[string]*schema.Schema{
111118
Description: "Specifies the number of minutes the user account will be locked after exhausting the designated number of password retries (i.e. PASSWORD_MAX_RETRIES). Supported range: 1 to 999, inclusive. Default: 15",
112119
ValidateFunc: validation.IntBetween(1, 999),
113120
},
121+
"history": {
122+
Type: schema.TypeInt,
123+
Optional: true,
124+
Default: 0,
125+
Description: "Specifies the number of the most recent passwords that Snowflake stores. These stored passwords cannot be repeated when a user updates their password value. The current password value does not count towards the history. When you increase the history value, Snowflake saves the previous values. When you decrease the value, Snowflake saves the stored values up to that value that is set. For example, if the history value is 8 and you change the history value to 3, Snowflake stores the most recent 3 passwords and deletes the 5 older password values from the history. Default: 0 Max: 24",
126+
ValidateFunc: validation.IntBetween(0, 24),
127+
},
114128
"comment": {
115129
Type: schema.TypeString,
116130
Optional: true,
@@ -157,9 +171,11 @@ func CreatePasswordPolicy(d *schema.ResourceData, meta interface{}) error {
157171
PasswordMinLowerCaseChars: sdk.Int(d.Get("min_lower_case_chars").(int)),
158172
PasswordMinNumericChars: sdk.Int(d.Get("min_numeric_chars").(int)),
159173
PasswordMinSpecialChars: sdk.Int(d.Get("min_special_chars").(int)),
174+
PasswordMinAgeDays: sdk.Int(d.Get("min_age_days").(int)),
160175
PasswordMaxAgeDays: sdk.Int(d.Get("max_age_days").(int)),
161176
PasswordMaxRetries: sdk.Int(d.Get("max_retries").(int)),
162177
PasswordLockoutTimeMins: sdk.Int(d.Get("lockout_time_mins").(int)),
178+
PasswordHistory: sdk.Int(d.Get("history").(int)),
163179
}
164180

165181
if v, ok := d.GetOk("comment"); ok {
@@ -225,6 +241,9 @@ func ReadPasswordPolicy(d *schema.ResourceData, meta interface{}) error {
225241
if err := setIntProperty(d, "min_special_chars", passwordPolicyDetails.PasswordMinSpecialChars); err != nil {
226242
return err
227243
}
244+
if err := setIntProperty(d, "min_age_days", passwordPolicyDetails.PasswordMinAgeDays); err != nil {
245+
return err
246+
}
228247
if err := setIntProperty(d, "max_age_days", passwordPolicyDetails.PasswordMaxAgeDays); err != nil {
229248
return err
230249
}
@@ -234,6 +253,9 @@ func ReadPasswordPolicy(d *schema.ResourceData, meta interface{}) error {
234253
if err := setIntProperty(d, "lockout_time_mins", passwordPolicyDetails.PasswordLockoutTimeMins); err != nil {
235254
return err
236255
}
256+
if err := setIntProperty(d, "history", passwordPolicyDetails.PasswordHistory); err != nil {
257+
return err
258+
}
237259

238260
return nil
239261
}
@@ -315,6 +337,18 @@ func UpdatePasswordPolicy(d *schema.ResourceData, meta interface{}) error {
315337
}
316338
}
317339

340+
if d.HasChange("min_age_days") {
341+
alterOptions := &sdk.AlterPasswordPolicyOptions{
342+
Set: &sdk.PasswordPolicySet{
343+
PasswordMinAgeDays: sdk.Int(d.Get("min_age_days").(int)),
344+
},
345+
}
346+
err := client.PasswordPolicies.Alter(ctx, objectIdentifier, alterOptions)
347+
if err != nil {
348+
return err
349+
}
350+
}
351+
318352
if d.HasChange("max_age_days") {
319353
alterOptions := &sdk.AlterPasswordPolicyOptions{
320354
Set: &sdk.PasswordPolicySet{
@@ -338,6 +372,7 @@ func UpdatePasswordPolicy(d *schema.ResourceData, meta interface{}) error {
338372
return err
339373
}
340374
}
375+
341376
if d.HasChange("lockout_time_mins") {
342377
alterOptions := &sdk.AlterPasswordPolicyOptions{
343378
Set: &sdk.PasswordPolicySet{
@@ -350,20 +385,28 @@ func UpdatePasswordPolicy(d *schema.ResourceData, meta interface{}) error {
350385
}
351386
}
352387

388+
if d.HasChange("history") {
389+
alterOptions := &sdk.AlterPasswordPolicyOptions{
390+
Set: &sdk.PasswordPolicySet{
391+
PasswordHistory: sdk.Int(d.Get("history").(int)),
392+
},
393+
}
394+
err := client.PasswordPolicies.Alter(ctx, objectIdentifier, alterOptions)
395+
if err != nil {
396+
return err
397+
}
398+
}
399+
353400
if d.HasChange("comment") {
354401
alterOptions := &sdk.AlterPasswordPolicyOptions{}
355402
if v, ok := d.GetOk("comment"); ok {
356403
alterOptions.Set = &sdk.PasswordPolicySet{
357404
Comment: sdk.String(v.(string)),
358405
}
359406
} else {
360-
alterOptions.Set = &sdk.PasswordPolicySet{
361-
Comment: sdk.String(""),
362-
}
363-
/* todo [SNOW-928909]: uncomment this once comments are working again
364407
alterOptions.Unset = &sdk.PasswordPolicyUnset{
365408
Comment: sdk.Bool(true),
366-
}*/
409+
}
367410
}
368411
err := client.PasswordPolicies.Alter(ctx, objectIdentifier, alterOptions)
369412
if err != nil {

pkg/resources/password_policy_acceptance_test.go

Lines changed: 101 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,96 @@
11
package resources_test
22

33
import (
4-
"fmt"
54
"strings"
65
"testing"
76

87
acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"
8+
"github.com/hashicorp/terraform-plugin-testing/config"
99
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
1010
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
"github.com/hashicorp/terraform-plugin-testing/tfversion"
1112
)
1213

1314
func TestAcc_PasswordPolicy(t *testing.T) {
1415
accName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
16+
m := func(minLength int, maxLength int, minUpperCaseChars int, minLowerCaseChars int, minNumericChars int, minSpecialChars int, minAgeDays int, maxAgeDays int, maxRetries int, lockoutTimeMins int, history int, comment string) map[string]config.Variable {
17+
return map[string]config.Variable{
18+
"name": config.StringVariable(accName),
19+
"database": config.StringVariable(acc.TestDatabaseName),
20+
"schema": config.StringVariable(acc.TestSchemaName),
21+
"min_length": config.IntegerVariable(minLength),
22+
"max_length": config.IntegerVariable(maxLength),
23+
"min_upper_case_chars": config.IntegerVariable(minUpperCaseChars),
24+
"min_lower_case_chars": config.IntegerVariable(minLowerCaseChars),
25+
"min_numeric_chars": config.IntegerVariable(minNumericChars),
26+
"min_special_chars": config.IntegerVariable(minSpecialChars),
27+
"min_age_days": config.IntegerVariable(minAgeDays),
28+
"max_age_days": config.IntegerVariable(maxAgeDays),
29+
"max_retries": config.IntegerVariable(maxRetries),
30+
"lockout_time_mins": config.IntegerVariable(lockoutTimeMins),
31+
"history": config.IntegerVariable(history),
32+
"comment": config.StringVariable(comment),
33+
}
34+
}
35+
variables1 := m(10, 30, 2, 3, 4, 5, 6, 7, 8, 9, 10, "this is a test resource")
36+
variables2 := m(20, 50, 1, 2, 3, 4, 5, 6, 7, 8, 9, "this is a test resource")
37+
variables3 := m(20, 50, 1, 2, 3, 4, 5, 6, 7, 8, 9, "")
1538

16-
resource.ParallelTest(t, resource.TestCase{
17-
Providers: acc.TestAccProviders(),
18-
PreCheck: func() { acc.TestAccPreCheck(t) },
39+
resource.Test(t, resource.TestCase{
40+
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
41+
PreCheck: func() { acc.TestAccPreCheck(t) },
42+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
43+
tfversion.RequireAbove(tfversion.Version1_5_0),
44+
},
1945
CheckDestroy: nil,
2046
Steps: []resource.TestStep{
2147
{
22-
Config: passwordPolicyConfig(accName, 10, 30, "this is a test resource", acc.TestDatabaseName, acc.TestSchemaName),
48+
ConfigDirectory: config.TestNameDirectory(),
49+
ConfigVariables: variables1,
2350
Check: resource.ComposeTestCheckFunc(
2451
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "name", accName),
2552
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_length", "10"),
2653
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_length", "30"),
54+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_upper_case_chars", "2"),
55+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_lower_case_chars", "3"),
56+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_numeric_chars", "4"),
57+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_special_chars", "5"),
58+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_age_days", "6"),
59+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_age_days", "7"),
60+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_retries", "8"),
61+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "lockout_time_mins", "9"),
62+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "history", "10"),
63+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "comment", "this is a test resource"),
2764
),
2865
},
2966
{
30-
Config: passwordPolicyConfig(accName, 20, 50, "this is a test resource", acc.TestDatabaseName, acc.TestSchemaName),
67+
ConfigDirectory: config.TestNameDirectory(),
68+
ConfigVariables: variables2,
3169
Check: resource.ComposeTestCheckFunc(
3270
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_length", "20"),
3371
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_length", "50"),
72+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_upper_case_chars", "1"),
73+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_lower_case_chars", "2"),
74+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_numeric_chars", "3"),
75+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_special_chars", "4"),
76+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "min_age_days", "5"),
77+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_age_days", "6"),
78+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_retries", "7"),
79+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "lockout_time_mins", "8"),
80+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "history", "9"),
81+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "comment", "this is a test resource"),
3482
),
3583
},
36-
/*
37-
todo [SNOW-928909]: fix once comments are working again for password policies
38-
query CREATE PASSWORD POLICY IF NOT EXISTS "T_Kn1bY6?2kx"."}k*3DrsXP:w9TRK#4wtS"."9ec016f6-ce74-0c94-2bd5-dc46547dbeff" PASSWORD_MIN_LENGTH = 10 PASSWORD_MAX_LENGTH = 20 PASSWORD_MIN_UPPER_CASE_CHARS = 5 COMMENT = 'test comment' err 001420 (22023): SQL compilation error: invalid property 'COMMENT' for 'PASSWORD_POLICY'
39-
{
40-
Config: passwordPolicyConfig(accName, 20, 50, ""),
41-
Check: resource.ComposeTestCheckFunc(
42-
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "comment", ""),
43-
),
44-
},
45-
*/
4684
{
85+
ConfigDirectory: config.TestNameDirectory(),
86+
ConfigVariables: variables3,
87+
Check: resource.ComposeTestCheckFunc(
88+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "comment", ""),
89+
),
90+
},
91+
{
92+
ConfigDirectory: config.TestNameDirectory(),
93+
ConfigVariables: variables3,
4794
ResourceName: "snowflake_password_policy.pa",
4895
ImportState: true,
4996
ImportStateVerify: true,
@@ -52,63 +99,71 @@ func TestAcc_PasswordPolicy(t *testing.T) {
5299
})
53100
}
54101

55-
func passwordPolicyConfig(s string, minLength int, maxLength int, comment string, databaseName string, schemaName string) string {
56-
return fmt.Sprintf(`
57-
resource "snowflake_password_policy" "pa" {
58-
name = "%v"
59-
database = "%s"
60-
schema = "%s"
61-
min_length = %d
62-
max_length = %d
63-
or_replace = true
64-
}
65-
`, s, databaseName, schemaName, minLength, maxLength)
66-
}
67-
68102
func TestAcc_PasswordPolicyMaxAgeDays(t *testing.T) {
69103
accName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
104+
m := func(maxAgeDays int) map[string]config.Variable {
105+
return map[string]config.Variable{
106+
"name": config.StringVariable(accName),
107+
"database": config.StringVariable(acc.TestDatabaseName),
108+
"schema": config.StringVariable(acc.TestSchemaName),
109+
"max_age_days": config.IntegerVariable(maxAgeDays),
110+
}
111+
}
70112

71-
resource.ParallelTest(t, resource.TestCase{
72-
Providers: acc.TestAccProviders(),
73-
PreCheck: func() { acc.TestAccPreCheck(t) },
113+
resource.Test(t, resource.TestCase{
114+
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
115+
PreCheck: func() { acc.TestAccPreCheck(t) },
116+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
117+
tfversion.RequireAbove(tfversion.Version1_5_0),
118+
},
74119
CheckDestroy: nil,
75120
Steps: []resource.TestStep{
76121
// Creation sets zero properly
77122
{
78-
Config: passwordPolicyDefaultMaxageDaysConfig(accName, acc.TestDatabaseName, acc.TestSchemaName, 0),
123+
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_PasswordPolicy_withMaxAgeDays"),
124+
ConfigVariables: m(0),
79125
Check: resource.ComposeTestCheckFunc(
80126
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_age_days", "0"),
81127
),
82128
},
83129
{
84-
Config: passwordPolicyDefaultMaxageDaysConfig(accName, acc.TestDatabaseName, acc.TestSchemaName, 10),
130+
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_PasswordPolicy_withMaxAgeDays"),
131+
ConfigVariables: m(10),
85132
Check: resource.ComposeTestCheckFunc(
86133
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_age_days", "10"),
87134
),
88135
},
89136
// Update sets zero properly
90137
{
91-
Config: passwordPolicyDefaultMaxageDaysConfig(accName, acc.TestDatabaseName, acc.TestSchemaName, 0),
138+
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_PasswordPolicy_withMaxAgeDays"),
139+
ConfigVariables: m(0),
92140
Check: resource.ComposeTestCheckFunc(
93141
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_age_days", "0"),
94142
),
95143
},
144+
// Unsets properly
145+
{
146+
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_PasswordPolicy_noOptionals"),
147+
ConfigVariables: map[string]config.Variable{
148+
"name": config.StringVariable(accName),
149+
"database": config.StringVariable(acc.TestDatabaseName),
150+
"schema": config.StringVariable(acc.TestSchemaName),
151+
},
152+
Check: resource.ComposeTestCheckFunc(
153+
resource.TestCheckResourceAttr("snowflake_password_policy.pa", "max_age_days", "90"),
154+
),
155+
},
96156
{
157+
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_PasswordPolicy_noOptionals"),
158+
ConfigVariables: map[string]config.Variable{
159+
"name": config.StringVariable(accName),
160+
"database": config.StringVariable(acc.TestDatabaseName),
161+
"schema": config.StringVariable(acc.TestSchemaName),
162+
},
97163
ResourceName: "snowflake_password_policy.pa",
98164
ImportState: true,
99165
ImportStateVerify: true,
100166
},
101167
},
102168
})
103169
}
104-
105-
func passwordPolicyDefaultMaxageDaysConfig(s string, databaseName string, schemaName string, maxAgeDays int) string {
106-
return fmt.Sprintf(`
107-
resource "snowflake_password_policy" "pa" {
108-
name = "%v"
109-
database = "%s"
110-
schema = "%s"
111-
max_age_days = %d
112-
}
113-
`, s, databaseName, schemaName, maxAgeDays)
114-
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
resource "snowflake_password_policy" "pa" {
2+
name = var.name
3+
database = var.database
4+
schema = var.schema
5+
min_length = var.min_length
6+
max_length = var.max_length
7+
min_upper_case_chars = var.min_upper_case_chars
8+
min_lower_case_chars = var.min_lower_case_chars
9+
min_numeric_chars = var.min_numeric_chars
10+
min_special_chars = var.min_special_chars
11+
min_age_days = var.min_age_days
12+
max_age_days = var.max_age_days
13+
max_retries = var.max_retries
14+
lockout_time_mins = var.lockout_time_mins
15+
history = var.history
16+
comment = var.comment
17+
or_replace = true
18+
}

0 commit comments

Comments
 (0)