From 557d1b089a288c06d9e3201a9cb311cee26bb2ee Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Mon, 26 Aug 2024 23:18:32 -0400 Subject: [PATCH] feat: Add password_policy.password_history_size arg for aws_cognito_user_pool --- .changelog/39043.txt | 3 ++ internal/service/cognitoidp/user_pool.go | 14 +++++ internal/service/cognitoidp/user_pool_test.go | 54 +++++++++++++++++++ .../docs/r/cognito_user_pool.html.markdown | 3 ++ 4 files changed, 74 insertions(+) create mode 100644 .changelog/39043.txt diff --git a/.changelog/39043.txt b/.changelog/39043.txt new file mode 100644 index 00000000000..30ce79ea59f --- /dev/null +++ b/.changelog/39043.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_cognito_user_pool: Add `password_policy.password_history_size` argument +``` \ No newline at end of file diff --git a/internal/service/cognitoidp/user_pool.go b/internal/service/cognitoidp/user_pool.go index cd3b2532656..d5e146dfbf2 100644 --- a/internal/service/cognitoidp/user_pool.go +++ b/internal/service/cognitoidp/user_pool.go @@ -386,6 +386,11 @@ func resourceUserPool() *schema.Resource { Optional: true, ValidateFunc: validation.IntBetween(6, 99), }, + "password_history_size": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 24), + }, "require_lowercase": { Type: schema.TypeBool, Optional: true, @@ -405,6 +410,7 @@ func resourceUserPool() *schema.Resource { "temporary_password_validity_days": { Type: schema.TypeInt, Optional: true, + Computed: true, ValidateFunc: validation.IntBetween(0, 365), }, }, @@ -1589,6 +1595,10 @@ func expandPasswordPolicyType(tfMap map[string]interface{}) *awstypes.PasswordPo apiObject.MinimumLength = aws.Int32(int32(v.(int))) } + if v, ok := tfMap["password_history_size"]; ok { + apiObject.PasswordHistorySize = aws.Int32(int32(v.(int))) + } + if v, ok := tfMap["require_lowercase"]; ok { apiObject.RequireLowercase = v.(bool) } @@ -1886,6 +1896,10 @@ func flattenPasswordPolicyType(apiObject *awstypes.PasswordPolicyType) []interfa tfMap["minimum_length"] = aws.ToInt32(apiObject.MinimumLength) } + if apiObject.PasswordHistorySize != nil { + tfMap["password_history_size"] = aws.ToInt32(apiObject.PasswordHistorySize) + } + if len(tfMap) > 0 { return []interface{}{tfMap} } diff --git a/internal/service/cognitoidp/user_pool_test.go b/internal/service/cognitoidp/user_pool_test.go index 1550c9e4fcb..51665d730f8 100644 --- a/internal/service/cognitoidp/user_pool_test.go +++ b/internal/service/cognitoidp/user_pool_test.go @@ -371,6 +371,43 @@ func TestAccCognitoIDPUserPool_withEmailVerificationMessage(t *testing.T) { }) } +func TestAccCognitoIDPUserPool_passwordHistorySize(t *testing.T) { + ctx := acctest.Context(t) + var pool awstypes.UserPoolType + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_cognito_user_pool.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckIdentityProvider(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.CognitoIDPServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckUserPoolDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccUserPoolConfig_passwordHistorySize(rName, 24), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckUserPoolExists(ctx, resourceName, &pool), + resource.TestCheckResourceAttr(resourceName, "password_policy.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "password_policy.0.password_history_size", "24"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccUserPoolConfig_passwordHistorySize(rName, 0), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckUserPoolExists(ctx, resourceName, &pool), + resource.TestCheckResourceAttr(resourceName, "password_policy.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "password_policy.0.password_history_size", acctest.Ct0), + ), + }, + }, + }) +} + func TestAccCognitoIDPUserPool_MFA_sms(t *testing.T) { ctx := acctest.Context(t) var pool awstypes.UserPoolType @@ -2148,6 +2185,23 @@ resource "aws_cognito_user_pool" "test" { `, rName, enabled) } +func testAccUserPoolConfig_passwordHistorySize(rName string, passwordHistorySize int) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q + + password_policy { + minimum_length = 6 + password_history_size = %[2]d + } + + user_pool_add_ons { + advanced_security_mode = "ENFORCED" + } +} +`, rName, passwordHistorySize) +} + func testAccUserPoolConfig_smsAuthenticationMessage(rName, smsAuthenticationMessage string) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { diff --git a/website/docs/r/cognito_user_pool.html.markdown b/website/docs/r/cognito_user_pool.html.markdown index c9778385c07..fad381e45c1 100644 --- a/website/docs/r/cognito_user_pool.html.markdown +++ b/website/docs/r/cognito_user_pool.html.markdown @@ -158,6 +158,9 @@ The following arguments are optional: ### password_policy * `minimum_length` - (Optional) Minimum length of the password policy that you have set. +* `password_history_size` - (Optional) Number of previous passwords that you want Amazon Cognito to restrict each user from reusing. Users can't set a password that matches any of number of previous passwords specified by this argument. A value of 0 means that password history is not enforced. Valid values are between 0 and 24. + + **Note:** This argument requires advanced security features to be active in the user pool. * `require_lowercase` - (Optional) Whether you have required users to use at least one lowercase letter in their password. * `require_numbers` - (Optional) Whether you have required users to use at least one number in their password. * `require_symbols` - (Optional) Whether you have required users to use at least one symbol in their password.