diff --git a/.changelog/20386.txt b/.changelog/20386.txt new file mode 100644 index 000000000000..617280584a84 --- /dev/null +++ b/.changelog/20386.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_cognito_user_pool: Fixes error when `schema` has empty `string_attribute_constraints` or `number_attribute_constraints` +``` diff --git a/internal/service/cognitoidp/user_pool.go b/internal/service/cognitoidp/user_pool.go index 3ee923d33ae1..a80454d1272c 100644 --- a/internal/service/cognitoidp/user_pool.go +++ b/internal/service/cognitoidp/user_pool.go @@ -4,6 +4,7 @@ package cognitoidp import ( + "bytes" "context" "fmt" "log" @@ -20,6 +21,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" @@ -422,6 +424,7 @@ func resourceUserPool() *schema.Resource { Optional: true, MinItems: 1, MaxItems: 50, + Set: resourceUserPoolSchemaHash, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "attribute_data_type": { @@ -1693,7 +1696,11 @@ func expandSchemaAttributeTypes(tfList []interface{}) []awstypes.SchemaAttribute sact.MinLength = aws.String(v.(string)) } - apiObject.StringAttributeConstraints = sact + if sact.MinLength == nil && sact.MaxLength == nil { + apiObject.StringAttributeConstraints = nil + } else { + apiObject.StringAttributeConstraints = sact + } } } } @@ -2279,3 +2286,50 @@ func userPoolSchemaAttributeMatchesStandardAttribute(apiObject *awstypes.SchemaA return false } + +func resourceUserPoolSchemaHash(v any) int { + var buf bytes.Buffer + m := v.(map[string]any) + buf.WriteString(fmt.Sprintf("%s-", m[names.AttrName].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["attribute_data_type"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["developer_only_attribute"].(bool))) + buf.WriteString(fmt.Sprintf("%t-", m["mutable"].(bool))) + buf.WriteString(fmt.Sprintf("%t-", m["required"].(bool))) + + if v, ok := m["string_attribute_constraints"]; ok { + data := v.([]any) + + if len(data) > 0 { + buf.WriteString("string_attribute_constraints-") + m, _ := data[0].(map[string]any) + if ok { + if l, ok := m["min_length"]; ok && l.(string) != "" { + buf.WriteString(fmt.Sprintf("%s-", l.(string))) + } + + if l, ok := m["max_length"]; ok && l.(string) != "" { + buf.WriteString(fmt.Sprintf("%s-", l.(string))) + } + } + } + } + + if v, ok := m["number_attribute_constraints"]; ok { + data := v.([]any) + + if len(data) > 0 { + buf.WriteString("number_attribute_constraints-") + m, _ := data[0].(map[string]any) + if ok { + if l, ok := m["min_value"]; ok && l.(string) != "" { + buf.WriteString(fmt.Sprintf("%s-", l.(string))) + } + + if l, ok := m["max_value"]; ok && l.(string) != "" { + buf.WriteString(fmt.Sprintf("%s-", l.(string))) + } + } + } + } + return create.StringHashcode(buf.String()) +} diff --git a/internal/service/cognitoidp/user_pool_test.go b/internal/service/cognitoidp/user_pool_test.go index fce568c49469..be19a8a58a6e 100644 --- a/internal/service/cognitoidp/user_pool_test.go +++ b/internal/service/cognitoidp/user_pool_test.go @@ -1389,7 +1389,7 @@ func TestAccCognitoIDPUserPool_schemaAttributes(t *testing.T) { Config: testAccUserPoolConfig_schemaAttributes(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckUserPoolExists(ctx, resourceName, &pool1), - resource.TestCheckResourceAttr(resourceName, "schema.#", acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, "schema.#", acctest.Ct4), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "schema.*", map[string]string{ "attribute_data_type": "String", "developer_only_attribute": acctest.CtFalse, @@ -1410,14 +1410,31 @@ func TestAccCognitoIDPUserPool_schemaAttributes(t *testing.T) { "required": acctest.CtFalse, "string_attribute_constraints.#": acctest.Ct0, }), - ), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "schema.*", map[string]string{ + "attribute_data_type": "String", + "developer_only_attribute": acctest.CtFalse, + "mutable": acctest.CtTrue, + names.AttrName: "strattr", + "number_attribute_constraints.#": acctest.Ct0, + "required": acctest.CtFalse, + "string_attribute_constraints.#": acctest.Ct1, + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "schema.*", map[string]string{ + "attribute_data_type": "Number", + "developer_only_attribute": acctest.CtFalse, + "mutable": acctest.CtTrue, + names.AttrName: "numattr", + "required": acctest.CtFalse, + "number_attribute_constraints.#": acctest.Ct1, + "string_attribute_constraints.#": acctest.Ct0, + })), }, { Config: testAccUserPoolConfig_schemaAttributesUpdated(rName, "mybool"), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckUserPoolExists(ctx, resourceName, &pool2), testAccCheckUserPoolNotRecreated(&pool1, &pool2), - resource.TestCheckResourceAttr(resourceName, "schema.#", acctest.Ct3), + resource.TestCheckResourceAttr(resourceName, "schema.#", "5"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "schema.*", map[string]string{ "attribute_data_type": "String", "developer_only_attribute": acctest.CtFalse, @@ -1449,12 +1466,36 @@ func TestAccCognitoIDPUserPool_schemaAttributes(t *testing.T) { "required": acctest.CtFalse, "string_attribute_constraints.#": acctest.Ct0, }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "schema.*", map[string]string{ + "attribute_data_type": "String", + "developer_only_attribute": acctest.CtFalse, + "mutable": acctest.CtTrue, + names.AttrName: "strattr", + "number_attribute_constraints.#": acctest.Ct0, + "required": acctest.CtFalse, + "string_attribute_constraints.#": acctest.Ct1, + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "schema.*", map[string]string{ + "attribute_data_type": "Number", + "developer_only_attribute": acctest.CtFalse, + "mutable": acctest.CtTrue, + names.AttrName: "numattr", + "required": acctest.CtFalse, + "number_attribute_constraints.#": acctest.Ct1, + "string_attribute_constraints.#": acctest.Ct0, + }), ), }, { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "schema.2.number_attribute_constraints.0.max_value", + "schema.2.number_attribute_constraints.0.min_value", + "schema.4.string_attribute_constraints.0.max_length", + "schema.4.string_attribute_constraints.0.min_length", + }, }, }, }) @@ -2662,6 +2703,24 @@ resource "aws_cognito_user_pool" "test" { } } + schema { + attribute_data_type = "String" + developer_only_attribute = false + mutable = true + name = "strattr" + required = false + string_attribute_constraints {} + } + + schema { + attribute_data_type = "Number" + developer_only_attribute = false + mutable = true + name = "numattr" + required = false + number_attribute_constraints {} + } + schema { attribute_data_type = "Boolean" developer_only_attribute = true @@ -2691,6 +2750,24 @@ resource "aws_cognito_user_pool" "test" { } } + schema { + attribute_data_type = "String" + developer_only_attribute = false + mutable = true + name = "strattr" + required = false + string_attribute_constraints {} + } + + schema { + attribute_data_type = "Number" + developer_only_attribute = false + mutable = true + name = "numattr" + required = false + number_attribute_constraints {} + } + schema { attribute_data_type = "Boolean" developer_only_attribute = true