From e9eebf77b9252af87700dcbbb7fc529af3b3d40e Mon Sep 17 00:00:00 2001 From: Gauthier Delacroix Date: Fri, 29 Mar 2019 10:25:34 +0100 Subject: [PATCH 01/11] Cognito User Pool UI Customization --- aws/cognito_user_pool_ui_customization.go | 119 ++++++++++++++++++ aws/resource_aws_cognito_user_pool.go | 20 +++ aws/resource_aws_cognito_user_pool_client.go | 20 +++ ...ource_aws_cognito_user_pool_client_test.go | 39 ++++++ aws/resource_aws_cognito_user_pool_test.go | 34 +++++ aws/resource_aws_lambda_function.go | 13 -- aws/structure.go | 22 ++++ aws/test-fixtures/logo.png | Bin 0 -> 1191 bytes aws/utils.go | 58 +++++++++ website/docs/r/cognito_user_pool.markdown | 6 + .../docs/r/cognito_user_pool_client.markdown | 28 +++++ 11 files changed, 346 insertions(+), 13 deletions(-) create mode 100644 aws/cognito_user_pool_ui_customization.go create mode 100644 aws/test-fixtures/logo.png diff --git a/aws/cognito_user_pool_ui_customization.go b/aws/cognito_user_pool_ui_customization.go new file mode 100644 index 00000000000..c6a527e42f7 --- /dev/null +++ b/aws/cognito_user_pool_ui_customization.go @@ -0,0 +1,119 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/terraform/helper/schema" +) + +func cognitoUserPoolUICustomizationSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "css": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return new == stringHashSum(old) + }, + StateFunc: func(v interface{}) string { + switch v.(type) { + case string: + return stringHashSum(v.(string)) + default: + return "" + } + }, + }, + "image_file": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return new == remoteFileHashSum(old) + }, + StateFunc: func(v interface{}) string { + switch v.(type) { + case string: + return remoteFileHashSum(v.(string)) + default: + return "" + } + }, + }, + }, + }, + } +} + +func cognitoUserPoolUICustomizationSet(d *schema.ResourceData, conn *cognitoidentityprovider.CognitoIdentityProvider) error { + var params *cognitoidentityprovider.SetUICustomizationInput + + // UI customization is applied to a single client or to all uncustomized pool + // clients, depending on the provided ResourceData. + // See https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SetUICustomization.html + + if d.Get("user_pool_id") == nil { + params = &cognitoidentityprovider.SetUICustomizationInput{ + UserPoolId: aws.String(d.Id()), + } + } else { + params = &cognitoidentityprovider.SetUICustomizationInput{ + ClientId: aws.String(d.Id()), + UserPoolId: aws.String(d.Get("user_pool_id").(string)), + } + } + + if v, ok := d.GetOk("ui_customization"); ok { + configs := v.([]interface{}) + config, ok := configs[0].(map[string]interface{}) + + if ok && config != nil { + if v, ok := config["css"]; ok && v.(string) != "" { + params.CSS = aws.String(v.(string)) + } + + if v, ok := config["image_file"]; ok && v.(string) != "" { + body, err := remoteFileContent(v.(string)) + + if err != nil { + return fmt.Errorf("Error reading image file: %s", err.Error()) + } + + params.ImageFile = body + } + } + } else { + params.CSS = nil + params.ImageFile = nil + } + + _, err := conn.SetUICustomization(params) + + return err +} + +func cognitoUserPoolUICustomizationGet(d *schema.ResourceData, conn *cognitoidentityprovider.CognitoIdentityProvider) (*cognitoidentityprovider.GetUICustomizationOutput, error) { + var params *cognitoidentityprovider.GetUICustomizationInput + + // UI customization is read from a single client or from the user pool, + // depending on the provided ResourceData. + // See https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_GetUICustomization.html + + if d.Get("user_pool_id") == nil { + params = &cognitoidentityprovider.GetUICustomizationInput{ + UserPoolId: aws.String(d.Id()), + } + } else { + params = &cognitoidentityprovider.GetUICustomizationInput{ + ClientId: aws.String(d.Id()), + UserPoolId: aws.String(d.Get("user_pool_id").(string)), + } + } + + return conn.GetUICustomization(params) +} diff --git a/aws/resource_aws_cognito_user_pool.go b/aws/resource_aws_cognito_user_pool.go index 341ee3043f1..411b8ab8c07 100644 --- a/aws/resource_aws_cognito_user_pool.go +++ b/aws/resource_aws_cognito_user_pool.go @@ -562,6 +562,8 @@ func resourceAwsCognitoUserPool() *schema.Resource { }, }, }, + + "ui_customization": cognitoUserPoolUICustomizationSchema(), }, } } @@ -807,6 +809,10 @@ func resourceAwsCognitoUserPoolCreate(d *schema.ResourceData, meta interface{}) } } + if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { + return fmt.Errorf("Error setting User Pool UI Customization: %s", err) + } + return resourceAwsCognitoUserPoolRead(d, meta) } @@ -934,6 +940,16 @@ func resourceAwsCognitoUserPoolRead(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("error setting software_token_mfa_configuration: %w", err) } + getUICustomizationOutput, err := cognitoUserPoolUICustomizationGet(d, conn) + + if err != nil { + return fmt.Errorf("Error reading Cognito User Pool UI Customization: %s", err) + } + + if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(getUICustomizationOutput.UICustomization)); err != nil { + return fmt.Errorf("Failed setting ui_customization: %s", err) + } + return nil } @@ -1194,6 +1210,10 @@ func resourceAwsCognitoUserPoolUpdate(d *schema.ResourceData, meta interface{}) } } + if err := cognitoUserPoolUICustomizationSet(d, conn); err != nil { + return fmt.Errorf("Error updating User Pool UI Customization: %s", err) + } + return resourceAwsCognitoUserPoolRead(d, meta) } diff --git a/aws/resource_aws_cognito_user_pool_client.go b/aws/resource_aws_cognito_user_pool_client.go index e2132390e5a..982e8a956cf 100644 --- a/aws/resource_aws_cognito_user_pool_client.go +++ b/aws/resource_aws_cognito_user_pool_client.go @@ -183,6 +183,8 @@ func resourceAwsCognitoUserPoolClient() *schema.Resource { }, }, }, + + "ui_customization": cognitoUserPoolUICustomizationSchema(), }, } } @@ -261,6 +263,10 @@ func resourceAwsCognitoUserPoolClientCreate(d *schema.ResourceData, meta interfa d.SetId(aws.StringValue(resp.UserPoolClient.ClientId)) + if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { + return fmt.Errorf("Error setting User Pool Client UI Customization: %s", err) + } + return resourceAwsCognitoUserPoolClientRead(d, meta) } @@ -306,6 +312,16 @@ func resourceAwsCognitoUserPoolClientRead(d *schema.ResourceData, meta interface return fmt.Errorf("error setting analytics_configuration: %s", err) } + getUICustomizationOutput, err := cognitoUserPoolUICustomizationGet(d, conn) + + if err != nil { + return fmt.Errorf("Error reading Cognito User Pool Client UI Customization: %s", err) + } + + if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(getUICustomizationOutput.UICustomization)); err != nil { + return fmt.Errorf("Failed setting ui_customization: %s", err) + } + return nil } @@ -380,6 +396,10 @@ func resourceAwsCognitoUserPoolClientUpdate(d *schema.ResourceData, meta interfa return fmt.Errorf("Error updating Cognito User Pool Client: %s", err) } + if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { + return fmt.Errorf("Error updating User Pool Client UI Customization: %s", err) + } + return resourceAwsCognitoUserPoolClientRead(d, meta) } diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index d1fff425c1b..3785a586566 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -166,6 +166,28 @@ func TestAccAWSCognitoUserPoolClient_allFields(t *testing.T) { }) } +func TestAccAWSCognitoUserPoolClient_withUICustomization(t *testing.T) { + userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) + clientName := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + css := ".label-customizable {font-weight: 400;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolClientConfig_withUICustomization(userPoolName, clientName, css), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aws_cognito_user_pool_client.client", "ui_customization.#", "1"), + resource.TestCheckResourceAttr("aws_cognito_user_pool_client.client", "ui_customization.0.css", css), + resource.TestCheckResourceAttrSet("aws_cognito_user_pool_client.client", "ui_customization.0.image_file"), + ), + }, + }, + }) +} + func TestAccAWSCognitoUserPoolClient_allFieldsUpdatingOneField(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) @@ -605,3 +627,20 @@ resource "aws_cognito_user_pool_client" "test" { } `, clientName) } + +func testAccAWSCognitoUserPoolClientConfig_withUICustomization(userPoolName, clientName string, css string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "pool" { + name = "%s" +} + +resource "aws_cognito_user_pool_client" "client" { + name = "%s" + user_pool_id = "${aws_cognito_user_pool.pool.id}" + ui_customization { + css = "%s" + image_file = "test-fixtures/logo.png" + } +} +`, userPoolName, clientName, css) +} diff --git a/aws/resource_aws_cognito_user_pool_test.go b/aws/resource_aws_cognito_user_pool_test.go index 4f4b941ff8b..a67f2ecfb89 100644 --- a/aws/resource_aws_cognito_user_pool_test.go +++ b/aws/resource_aws_cognito_user_pool_test.go @@ -1110,6 +1110,27 @@ func TestAccAWSCognitoUserPool_withVerificationMessageTemplate(t *testing.T) { }) } +func TestAccAWSCognitoUserPool_withUICustomization(t *testing.T) { + userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) + css := ".label-customizable {font-weight: 400;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomization(userPoolName, css), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("aws_cognito_user_pool_pool.pool", "ui_customization.#", "1"), + resource.TestCheckResourceAttr("aws_cognito_user_pool_pool.pool", "ui_customization.0.css", css), + resource.TestCheckResourceAttrSet("aws_cognito_user_pool_pool.pool", "ui_customization.0.image_file"), + ), + }, + }, + }) +} + func TestAccAWSCognitoUserPool_update(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") optionalMfa := "OPTIONAL" @@ -2017,3 +2038,16 @@ resource "aws_cognito_user_pool" "test" { } `, name, mfaconfig, smsAuthMsg) } + +func testAccAWSCognitoUserPoolConfig_withUICustomization(userPoolName, css string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "pool" { + name = "%s" + + ui_customization { + css = "%s" + image_file = "test-fixtures/logo.png" + } +} +`, userPoolName, css) +} diff --git a/aws/resource_aws_lambda_function.go b/aws/resource_aws_lambda_function.go index 0519e185f52..8a9331de10e 100644 --- a/aws/resource_aws_lambda_function.go +++ b/aws/resource_aws_lambda_function.go @@ -1206,19 +1206,6 @@ func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) e return resourceAwsLambdaFunctionRead(d, meta) } -// loadFileContent returns contents of a file in a given path -func loadFileContent(v string) ([]byte, error) { - filename, err := homedir.Expand(v) - if err != nil { - return nil, err - } - fileContent, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return fileContent, nil -} - func readEnvironmentVariables(ev map[string]interface{}) map[string]string { variables := make(map[string]string) for k, v := range ev { diff --git a/aws/structure.go b/aws/structure.go index 1781aa7bb81..fb7c2a19d21 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -3158,6 +3158,28 @@ func flattenCognitoUserPoolVerificationMessageTemplate(s *cognitoidentityprovide return []map[string]interface{}{} } +func flattenCognitoUserPoolUICustomization(s *cognitoidentityprovider.UICustomizationType) []map[string]interface{} { + m := map[string]interface{}{} + + if s == nil { + return nil + } + + if s.CSS != nil { + m["css"] = *s.CSS + } + + if s.ImageUrl != nil { + m["image_file"] = *s.ImageUrl + } + + if len(m) > 0 { + return []map[string]interface{}{m} + } + + return []map[string]interface{}{} +} + func sliceContainsMap(l []interface{}, m map[string]interface{}) (int, bool) { for i, t := range l { if reflect.DeepEqual(m, t.(map[string]interface{})) { diff --git a/aws/test-fixtures/logo.png b/aws/test-fixtures/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d8348fa7be78224d1f8a25f35538401833e9d530 GIT binary patch literal 1191 zcmV;Y1X%ltP)N2bZe?^J zG%hhNHDpIvQUCx1B}qgf?T-HoIKWGP2t~ z!T@AZj}ngtQ0O3e$sS4p5S5TNFC+by?`Wukyv5o|1&{zC>eJvZnTuLGA2I=;#`yqk zVcmSl27nqsJe7rTHTjrzlq&#iNrZe5Lw%OYhou0Z1`t0XEH8F*(N*LyrK@}ZP`7)+ zJDaOsts#f{@2aXAD17Wf0mx#o5$=dbazl78Ipngdx_WFi&kvpBd1a97%3s9*fFA>d zzp%a5(eP;tIrOrK=N}y7IOQ$R4@LpNGqO8>6$=10WWzTJ>1WTtRU$(aMS`I0hr);B zg;SdlK*C6S4ckUCOy1(S!L1xObOQ>WO)lQ-020<7`#r&ZWVoD#0}ihEc_?x!y>RN1 z01(TES!j*x9Sz@e$_FU?I9z$s=SD6O0Ms}i{G!^KDIaj3Ls2PxZsZ~W2q4W1tG^Gd z6VB$P0!SF?N%;`(bLA#%=h%g2auEPv;a`OC$`5YU>9mtIaNYo@ks82@EGQMkdzuyXQyrYa@$br*gt z385n}n|#SGmgS`aP=53L&{d8b*ccn!42op{(r2LXOHH+r_1)dk@_-m>hi{A^yXeKv z6@U_fZNJAeSV@L8kCH+VX%jHNZ^1>i*S*;!qfifkG02Bp04Og6;b9#a&U|8h3qaA& z17f%aziyllnE}AQFM=>6;n!mWnN^v-1_43@?ugqoA7FjqhW%s7?_&W_e!=#N*E_r+ zHh|fcbshlu;1{*aG#~t;+75RE$%n-NaGxvv_4Ov~b0cL1Kpo465XpxyeQuHhP)Q~=m8bDRedcYJOO{K=XRfCb|WC~-Le z8~_dg2LQXcT literal 0 HcmV?d00001 diff --git a/aws/utils.go b/aws/utils.go index 49c56f14d73..1db3a469780 100644 --- a/aws/utils.go +++ b/aws/utils.go @@ -1,12 +1,17 @@ package aws import ( + "crypto/sha1" "encoding/base64" + "encoding/hex" "encoding/json" + "io/ioutil" + "net/http" "reflect" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + homedir "github.com/mitchellh/go-homedir" ) // Base64Encode encodes data if the input isn't already encoded using base64.StdEncoding.EncodeToString. @@ -61,3 +66,56 @@ func appendUniqueString(slice []string, elem string) []string { } return append(slice, elem) } + +// loadFileContent returns contents of a file in a given path +func loadFileContent(v string) ([]byte, error) { + filename, err := homedir.Expand(v) + if err != nil { + return nil, err + } + fileContent, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return fileContent, nil +} + +func byteHashSum(content []byte) string { + hash := sha1.Sum(content) + + return hex.EncodeToString(hash[:]) +} + +func stringHashSum(css string) string { + v := []byte(css) + hash := sha1.Sum(v) + return hex.EncodeToString(hash[:]) +} + +func remoteFileContent(v string) ([]byte, error) { + match, _ := regexp.MatchString(`^https?:\/\/`, v) + + if match { + resp, err := http.Get(v) + + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + return ioutil.ReadAll(resp.Body) + } else { + return loadFileContent(v) + } +} + +func remoteFileHashSum(v string) string { + content, err := remoteFileContent(v) + + if err != nil { + return "" + } + + return byteHashSum(content) +} diff --git a/website/docs/r/cognito_user_pool.markdown b/website/docs/r/cognito_user_pool.markdown index db80695b8ec..b3db7e79b5d 100644 --- a/website/docs/r/cognito_user_pool.markdown +++ b/website/docs/r/cognito_user_pool.markdown @@ -89,6 +89,7 @@ The following arguments are supported: * `user_pool_add_ons` - (Optional) Configuration block for [user pool add-ons](#user-pool-add-ons) to enable user pool advanced security mode features. * `verification_message_template` (Optional) - The [verification message templates](#verification-message-template) configuration. * `account_recovery_setting` (Optional) - The [account_recovery_setting](#account-recovery-setting) configuration. +* `ui_customization` (Optional) - The uncustomized clients [UI Customization](#ui-customization). #### Admin Create User Config @@ -213,6 +214,11 @@ The following arguments are required in the `software_token_mfa_configuration` c * `name` (Required) - Specifies the recovery method for a user. Can be of the following: `verified_email`, `verified_phone_number`, and `admin_only`. * `priority` (Required) - A positive integer specifying priority of a method with 1 being the highest priority. +#### UI Customization + + * `css` (Optional) - The customized CSS. + * `image_file` (Optional) - Le local image file path. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: diff --git a/website/docs/r/cognito_user_pool_client.markdown b/website/docs/r/cognito_user_pool_client.markdown index 5bf5f3347c9..e8505307f55 100644 --- a/website/docs/r/cognito_user_pool_client.markdown +++ b/website/docs/r/cognito_user_pool_client.markdown @@ -110,6 +110,28 @@ resource "aws_cognito_user_pool_client" "test" { } ``` +### Create a user pool client with UI customization +```hcl +resource "aws_cognito_user_pool" "pool" { + name = "pool" +} + +data "template_file" "custom_css" { + template = "${file("files/custom.css")}" +} + +resource "aws_cognito_user_pool_client" "client" { + name = "client" + + user_pool_id = "${aws_cognito_user_pool.pool.id}" + + ui_customization { + css = "${data.template_file.custom_css.rendered}" + image_file = "files/logo.png" + } +} +``` + ## Argument Reference The following arguments are supported: @@ -130,6 +152,7 @@ The following arguments are supported: * `user_pool_id` - (Required) The user pool the client belongs to. * `write_attributes` - (Optional) List of user pool attributes the application client can write to. * `analytics_configuration` - (Optional) The Amazon Pinpoint analytics configuration for collecting metrics for this user pool. +* `ui_customization` (Optional) - The [UI Customization](#ui-customization). ### Analytics Configuration @@ -141,6 +164,11 @@ Either `application_arn` or `application_id` is required. * `role_arn` - (Optional) The ARN of an IAM role that authorizes Amazon Cognito to publish events to Amazon Pinpoint analytics. Conflicts with `application_arn`. * `user_data_shared` (Optional) If set to `true`, Amazon Cognito will include user data in the events it publishes to Amazon Pinpoint analytics. +#### UI Customization + + * `css` (Optional) - The customized CSS. + * `image_file` (Optional) - Le local image file path. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: From 01c012449068ecf5ec4954e963fc820474e4ed91 Mon Sep 17 00:00:00 2001 From: Gauthier Delacroix <1533042+gdlx@users.noreply.github.com> Date: Wed, 18 Sep 2019 15:40:34 +0200 Subject: [PATCH 02/11] Update website/docs/r/cognito_user_pool_client.markdown Co-Authored-By: Timm Drevensek --- website/docs/r/cognito_user_pool_client.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/cognito_user_pool_client.markdown b/website/docs/r/cognito_user_pool_client.markdown index e8505307f55..ed86e866401 100644 --- a/website/docs/r/cognito_user_pool_client.markdown +++ b/website/docs/r/cognito_user_pool_client.markdown @@ -166,8 +166,8 @@ Either `application_arn` or `application_id` is required. #### UI Customization - * `css` (Optional) - The customized CSS. - * `image_file` (Optional) - Le local image file path. +* `css` (Optional) - The customized CSS. +* `image_file` (Optional) - The local image file path. ## Attributes Reference From 2a2be681f77cabda3e93cf72edee5c6f5a798fdc Mon Sep 17 00:00:00 2001 From: Gauthier Delacroix <1533042+gdlx@users.noreply.github.com> Date: Wed, 18 Sep 2019 15:41:11 +0200 Subject: [PATCH 03/11] Update aws/resource_aws_cognito_user_pool.go Co-Authored-By: Timm Drevensek --- aws/resource_aws_cognito_user_pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_cognito_user_pool.go b/aws/resource_aws_cognito_user_pool.go index 411b8ab8c07..0aa974238a6 100644 --- a/aws/resource_aws_cognito_user_pool.go +++ b/aws/resource_aws_cognito_user_pool.go @@ -1211,7 +1211,7 @@ func resourceAwsCognitoUserPoolUpdate(d *schema.ResourceData, meta interface{}) } if err := cognitoUserPoolUICustomizationSet(d, conn); err != nil { - return fmt.Errorf("Error updating User Pool UI Customization: %s", err) + return fmt.Errorf("Error updating Cognito User Pool UI Customization: %s", err) } return resourceAwsCognitoUserPoolRead(d, meta) From 1733684249d32bb6bd8a077f13d972cf230d2437 Mon Sep 17 00:00:00 2001 From: Gauthier Delacroix <1533042+gdlx@users.noreply.github.com> Date: Wed, 18 Sep 2019 15:41:27 +0200 Subject: [PATCH 04/11] Update aws/resource_aws_cognito_user_pool.go Co-Authored-By: Timm Drevensek --- aws/resource_aws_cognito_user_pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_cognito_user_pool.go b/aws/resource_aws_cognito_user_pool.go index 0aa974238a6..1886fe1b9eb 100644 --- a/aws/resource_aws_cognito_user_pool.go +++ b/aws/resource_aws_cognito_user_pool.go @@ -810,7 +810,7 @@ func resourceAwsCognitoUserPoolCreate(d *schema.ResourceData, meta interface{}) } if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { - return fmt.Errorf("Error setting User Pool UI Customization: %s", err) + return fmt.Errorf("Error setting Cognito User Pool UI Customization: %s", err) } return resourceAwsCognitoUserPoolRead(d, meta) From c0d69ba93c6454a84605d3213f9a545f65413f1e Mon Sep 17 00:00:00 2001 From: Gauthier Delacroix <1533042+gdlx@users.noreply.github.com> Date: Wed, 18 Sep 2019 15:41:38 +0200 Subject: [PATCH 05/11] Update aws/resource_aws_cognito_user_pool_client.go Co-Authored-By: Timm Drevensek --- aws/resource_aws_cognito_user_pool_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_cognito_user_pool_client.go b/aws/resource_aws_cognito_user_pool_client.go index 982e8a956cf..27799bc6f8a 100644 --- a/aws/resource_aws_cognito_user_pool_client.go +++ b/aws/resource_aws_cognito_user_pool_client.go @@ -264,7 +264,7 @@ func resourceAwsCognitoUserPoolClientCreate(d *schema.ResourceData, meta interfa d.SetId(aws.StringValue(resp.UserPoolClient.ClientId)) if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { - return fmt.Errorf("Error setting User Pool Client UI Customization: %s", err) + return fmt.Errorf("Error setting Cognito User Pool Client UI Customization: %s", err) } return resourceAwsCognitoUserPoolClientRead(d, meta) From f7a6f8f4784dfea1b97d0b09047df9a349520686 Mon Sep 17 00:00:00 2001 From: Gauthier Delacroix <1533042+gdlx@users.noreply.github.com> Date: Wed, 18 Sep 2019 15:41:49 +0200 Subject: [PATCH 06/11] Update website/docs/r/cognito_user_pool.markdown Co-Authored-By: Timm Drevensek --- website/docs/r/cognito_user_pool.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/cognito_user_pool.markdown b/website/docs/r/cognito_user_pool.markdown index b3db7e79b5d..e35c48ee325 100644 --- a/website/docs/r/cognito_user_pool.markdown +++ b/website/docs/r/cognito_user_pool.markdown @@ -217,7 +217,7 @@ The following arguments are required in the `software_token_mfa_configuration` c #### UI Customization * `css` (Optional) - The customized CSS. - * `image_file` (Optional) - Le local image file path. + * `image_file` (Optional) - The local image file path. ## Attributes Reference From 34f9f7e5eedb64274cd2818d9e08eefd155be847 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Wed, 17 Feb 2021 23:10:08 -0500 Subject: [PATCH 07/11] fix-up linter related errors --- aws/cognito_user_pool_ui_customization.go | 10 +++++----- aws/resource_aws_cognito_user_pool_client_test.go | 10 +++++----- aws/resource_aws_cognito_user_pool_test.go | 8 ++++---- aws/resource_aws_lambda_function.go | 2 -- website/docs/r/cognito_user_pool.markdown | 4 ++-- website/docs/r/cognito_user_pool_client.markdown | 7 ++++--- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/aws/cognito_user_pool_ui_customization.go b/aws/cognito_user_pool_ui_customization.go index c6a527e42f7..fa0edfe310e 100644 --- a/aws/cognito_user_pool_ui_customization.go +++ b/aws/cognito_user_pool_ui_customization.go @@ -5,7 +5,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" - "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func cognitoUserPoolUICustomizationSchema() *schema.Schema { @@ -22,9 +22,9 @@ func cognitoUserPoolUICustomizationSchema() *schema.Schema { return new == stringHashSum(old) }, StateFunc: func(v interface{}) string { - switch v.(type) { + switch t := v.(type) { case string: - return stringHashSum(v.(string)) + return stringHashSum(t) default: return "" } @@ -37,9 +37,9 @@ func cognitoUserPoolUICustomizationSchema() *schema.Schema { return new == remoteFileHashSum(old) }, StateFunc: func(v interface{}) string { - switch v.(type) { + switch t := v.(type) { case string: - return remoteFileHashSum(v.(string)) + return remoteFileHashSum(t) default: return "" } diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index 3785a586566..4f46c1e5a42 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -168,7 +168,7 @@ func TestAccAWSCognitoUserPoolClient_allFields(t *testing.T) { func TestAccAWSCognitoUserPoolClient_withUICustomization(t *testing.T) { userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + clientName := acctest.RandString(10) css := ".label-customizable {font-weight: 400;}" resource.ParallelTest(t, resource.TestCase{ @@ -636,11 +636,11 @@ resource "aws_cognito_user_pool" "pool" { resource "aws_cognito_user_pool_client" "client" { name = "%s" - user_pool_id = "${aws_cognito_user_pool.pool.id}" + user_pool_id = aws_cognito_user_pool.pool.id ui_customization { - css = "%s" - image_file = "test-fixtures/logo.png" - } + css = "%s" + image_file = "test-fixtures/logo.png" + } } `, userPoolName, clientName, css) } diff --git a/aws/resource_aws_cognito_user_pool_test.go b/aws/resource_aws_cognito_user_pool_test.go index a67f2ecfb89..abc9f31dbcc 100644 --- a/aws/resource_aws_cognito_user_pool_test.go +++ b/aws/resource_aws_cognito_user_pool_test.go @@ -2044,10 +2044,10 @@ func testAccAWSCognitoUserPoolConfig_withUICustomization(userPoolName, css strin resource "aws_cognito_user_pool" "pool" { name = "%s" - ui_customization { - css = "%s" - image_file = "test-fixtures/logo.png" - } + ui_customization { + css = "%s" + image_file = "test-fixtures/logo.png" + } } `, userPoolName, css) } diff --git a/aws/resource_aws_lambda_function.go b/aws/resource_aws_lambda_function.go index 8a9331de10e..8db75e001fc 100644 --- a/aws/resource_aws_lambda_function.go +++ b/aws/resource_aws_lambda_function.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "log" "regexp" "time" @@ -19,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - homedir "github.com/mitchellh/go-homedir" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/lambda/waiter" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" diff --git a/website/docs/r/cognito_user_pool.markdown b/website/docs/r/cognito_user_pool.markdown index e35c48ee325..f74e5683200 100644 --- a/website/docs/r/cognito_user_pool.markdown +++ b/website/docs/r/cognito_user_pool.markdown @@ -216,8 +216,8 @@ The following arguments are required in the `software_token_mfa_configuration` c #### UI Customization - * `css` (Optional) - The customized CSS. - * `image_file` (Optional) - The local image file path. +* `css` (Optional) - The customized CSS. +* `image_file` (Optional) - The local image file path. ## Attributes Reference diff --git a/website/docs/r/cognito_user_pool_client.markdown b/website/docs/r/cognito_user_pool_client.markdown index ed86e866401..c9dc7186fe4 100644 --- a/website/docs/r/cognito_user_pool_client.markdown +++ b/website/docs/r/cognito_user_pool_client.markdown @@ -111,22 +111,23 @@ resource "aws_cognito_user_pool_client" "test" { ``` ### Create a user pool client with UI customization + ```hcl resource "aws_cognito_user_pool" "pool" { name = "pool" } data "template_file" "custom_css" { - template = "${file("files/custom.css")}" + template = file("files/custom.css") } resource "aws_cognito_user_pool_client" "client" { name = "client" - user_pool_id = "${aws_cognito_user_pool.pool.id}" + user_pool_id = aws_cognito_user_pool.pool.id ui_customization { - css = "${data.template_file.custom_css.rendered}" + css = data.template_file.custom_css.rendered image_file = "files/logo.png" } } From 6437ec54f4418d5f3150a85bec2db3f8ac751d00 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Tue, 23 Feb 2021 13:41:00 -0500 Subject: [PATCH 08/11] update test coverage/documentation --- .changelog/8114.txt | 7 + aws/cognito_user_pool_ui_customization.go | 119 ----- .../cognitoidentityprovider/finder/finder.go | 38 ++ aws/resource_aws_cognito_user_pool.go | 138 +++++- aws/resource_aws_cognito_user_pool_client.go | 99 +++- ...ource_aws_cognito_user_pool_client_test.go | 433 ++++++++++++++++-- aws/resource_aws_cognito_user_pool_test.go | 262 +++++++++-- aws/resource_aws_lambda_function.go | 15 + aws/structure.go | 22 - .../service/cognitoidentityprovider}/logo.png | Bin .../cognitoidentityprovider/logo_modified.png | Bin 0 -> 5101 bytes aws/utils.go | 58 --- website/docs/r/cognito_user_pool.markdown | 38 +- .../docs/r/cognito_user_pool_client.markdown | 30 +- 14 files changed, 960 insertions(+), 299 deletions(-) create mode 100644 .changelog/8114.txt delete mode 100644 aws/cognito_user_pool_ui_customization.go create mode 100644 aws/internal/service/cognitoidentityprovider/finder/finder.go rename aws/{test-fixtures => testdata/service/cognitoidentityprovider}/logo.png (100%) create mode 100644 aws/testdata/service/cognitoidentityprovider/logo_modified.png diff --git a/.changelog/8114.txt b/.changelog/8114.txt new file mode 100644 index 00000000000..adce5755345 --- /dev/null +++ b/.changelog/8114.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_cognito_user_pool: Add `ui_customization` argument +``` + +```release-note:enhancement +resource/aws_cognito_user_pool_client: Add `ui_customization` argument +``` diff --git a/aws/cognito_user_pool_ui_customization.go b/aws/cognito_user_pool_ui_customization.go deleted file mode 100644 index fa0edfe310e..00000000000 --- a/aws/cognito_user_pool_ui_customization.go +++ /dev/null @@ -1,119 +0,0 @@ -package aws - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func cognitoUserPoolUICustomizationSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "css": { - Type: schema.TypeString, - Optional: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return new == stringHashSum(old) - }, - StateFunc: func(v interface{}) string { - switch t := v.(type) { - case string: - return stringHashSum(t) - default: - return "" - } - }, - }, - "image_file": { - Type: schema.TypeString, - Optional: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return new == remoteFileHashSum(old) - }, - StateFunc: func(v interface{}) string { - switch t := v.(type) { - case string: - return remoteFileHashSum(t) - default: - return "" - } - }, - }, - }, - }, - } -} - -func cognitoUserPoolUICustomizationSet(d *schema.ResourceData, conn *cognitoidentityprovider.CognitoIdentityProvider) error { - var params *cognitoidentityprovider.SetUICustomizationInput - - // UI customization is applied to a single client or to all uncustomized pool - // clients, depending on the provided ResourceData. - // See https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SetUICustomization.html - - if d.Get("user_pool_id") == nil { - params = &cognitoidentityprovider.SetUICustomizationInput{ - UserPoolId: aws.String(d.Id()), - } - } else { - params = &cognitoidentityprovider.SetUICustomizationInput{ - ClientId: aws.String(d.Id()), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), - } - } - - if v, ok := d.GetOk("ui_customization"); ok { - configs := v.([]interface{}) - config, ok := configs[0].(map[string]interface{}) - - if ok && config != nil { - if v, ok := config["css"]; ok && v.(string) != "" { - params.CSS = aws.String(v.(string)) - } - - if v, ok := config["image_file"]; ok && v.(string) != "" { - body, err := remoteFileContent(v.(string)) - - if err != nil { - return fmt.Errorf("Error reading image file: %s", err.Error()) - } - - params.ImageFile = body - } - } - } else { - params.CSS = nil - params.ImageFile = nil - } - - _, err := conn.SetUICustomization(params) - - return err -} - -func cognitoUserPoolUICustomizationGet(d *schema.ResourceData, conn *cognitoidentityprovider.CognitoIdentityProvider) (*cognitoidentityprovider.GetUICustomizationOutput, error) { - var params *cognitoidentityprovider.GetUICustomizationInput - - // UI customization is read from a single client or from the user pool, - // depending on the provided ResourceData. - // See https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_GetUICustomization.html - - if d.Get("user_pool_id") == nil { - params = &cognitoidentityprovider.GetUICustomizationInput{ - UserPoolId: aws.String(d.Id()), - } - } else { - params = &cognitoidentityprovider.GetUICustomizationInput{ - ClientId: aws.String(d.Id()), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), - } - } - - return conn.GetUICustomization(params) -} diff --git a/aws/internal/service/cognitoidentityprovider/finder/finder.go b/aws/internal/service/cognitoidentityprovider/finder/finder.go new file mode 100644 index 00000000000..8c8f27b69f7 --- /dev/null +++ b/aws/internal/service/cognitoidentityprovider/finder/finder.go @@ -0,0 +1,38 @@ +package finder + +import ( + "reflect" + + "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" +) + +// CognitoUserPoolUICustomization returns the UI Customization corresponding to the UserPoolId and ClientId, if provided. +// Returns nil if no UI Customization is found. +func CognitoUserPoolUICustomization(conn *cognitoidentityprovider.CognitoIdentityProvider, userPoolId, clientId *string) (*cognitoidentityprovider.UICustomizationType, error) { + input := &cognitoidentityprovider.GetUICustomizationInput{ + UserPoolId: userPoolId, + } + + if clientId != nil { + input.ClientId = clientId + } + + output, err := conn.GetUICustomization(input) + + if err != nil { + return nil, err + } + + if output == nil || output.UICustomization == nil { + return nil, nil + } + + // The GetUICustomization API operation will return an empty struct + // if nothing is present rather than nil or an error, so we equate that with nil + // to prevent non-empty plans of an empty ui_customization block + if reflect.DeepEqual(output.UICustomization, &cognitoidentityprovider.UICustomizationType{}) { + return nil, nil + } + + return output.UICustomization, nil +} diff --git a/aws/resource_aws_cognito_user_pool.go b/aws/resource_aws_cognito_user_pool.go index 1886fe1b9eb..8bd562a3d6f 100644 --- a/aws/resource_aws_cognito_user_pool.go +++ b/aws/resource_aws_cognito_user_pool.go @@ -1,6 +1,8 @@ package aws import ( + "context" + "encoding/base64" "fmt" "log" "regexp" @@ -8,10 +10,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cognitoidentityprovider/finder" ) func resourceAwsCognitoUserPool() *schema.Resource { @@ -563,8 +568,40 @@ func resourceAwsCognitoUserPool() *schema.Resource { }, }, - "ui_customization": cognitoUserPoolUICustomizationSchema(), + "ui_customization": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "css": { + Type: schema.TypeString, + Optional: true, + }, + "css_version": { + Type: schema.TypeString, + Computed: true, + }, + "image_file": { + Type: schema.TypeString, + Optional: true, + }, + "image_url": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, }, + + CustomizeDiff: customdiff.Sequence( + // A "ui_customization" cannot be removed from a Cognito User Pool resource; + // thus, resource recreation is triggered on configuration block removal + customdiff.ForceNewIfChange("ui_customization", func(_ context.Context, old, new, meta interface{}) bool { + return len(old.([]interface{})) == 1 && len(new.([]interface{})) == 0 + }), + ), } } @@ -809,10 +846,6 @@ func resourceAwsCognitoUserPoolCreate(d *schema.ResourceData, meta interface{}) } } - if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { - return fmt.Errorf("Error setting Cognito User Pool UI Customization: %s", err) - } - return resourceAwsCognitoUserPoolRead(d, meta) } @@ -940,14 +973,23 @@ func resourceAwsCognitoUserPoolRead(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("error setting software_token_mfa_configuration: %w", err) } - getUICustomizationOutput, err := cognitoUserPoolUICustomizationGet(d, conn) + // Retrieve UICustomization iff the User Pool is associated with a Domain + if resp.UserPool.Domain != nil { + uiCustomization, err := finder.CognitoUserPoolUICustomization(conn, resp.UserPool.Id, nil) - if err != nil { - return fmt.Errorf("Error reading Cognito User Pool UI Customization: %s", err) - } + if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Cognito User Pool (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting Cognito User Pool (%s) UI customization: %w", d.Id(), err) + } - if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(getUICustomizationOutput.UICustomization)); err != nil { - return fmt.Errorf("Failed setting ui_customization: %s", err) + if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(d, uiCustomization)); err != nil { + return fmt.Errorf("error setting ui_customization: %w", err) + } } return nil @@ -1210,8 +1252,24 @@ func resourceAwsCognitoUserPoolUpdate(d *schema.ResourceData, meta interface{}) } } - if err := cognitoUserPoolUICustomizationSet(d, conn); err != nil { - return fmt.Errorf("Error updating Cognito User Pool UI Customization: %s", err) + if d.HasChange("ui_customization") { + if v, ok := d.GetOk("ui_customization"); ok { + input, err := expandCognitoUserPoolUICustomizationInput(v.([]interface{})) + + if err != nil { + return fmt.Errorf("error updating Cognito User pool (%s) UI customization: %w", d.Id(), err) + } + + if input != nil { + input.UserPoolId = aws.String(d.Id()) + + _, err := conn.SetUICustomization(input) + + if err != nil { + return fmt.Errorf("error updating Cognito User pool (%s) UI customization: %w", d.Id(), err) + } + } + } } return resourceAwsCognitoUserPoolRead(d, meta) @@ -1332,6 +1390,33 @@ func expandCognitoUserPoolAccountRecoverySettingConfig(config map[string]interfa return configs } +func expandCognitoUserPoolUICustomizationInput(l []interface{}) (*cognitoidentityprovider.SetUICustomizationInput, error) { + if len(l) == 0 || l[0] == nil { + return nil, nil + } + + tfMap, ok := l[0].(map[string]interface{}) + if !ok { + return nil, nil + } + + input := &cognitoidentityprovider.SetUICustomizationInput{} + + if v, ok := tfMap["css"].(string); ok && v != "" { + input.CSS = aws.String(v) + } + + if v, ok := tfMap["image_file"].(string); ok && v != "" { + imgFile, err := base64.StdEncoding.DecodeString(v) + if err != nil { + return nil, err + } + input.ImageFile = imgFile + } + + return input, nil +} + func flattenCognitoUserPoolAccountRecoverySettingConfig(config *cognitoidentityprovider.AccountRecoverySettingType) []interface{} { if config == nil { return nil @@ -1353,3 +1438,30 @@ func flattenCognitoUserPoolAccountRecoverySettingConfig(config *cognitoidentityp return []interface{}{settings} } + +func flattenCognitoUserPoolUICustomization(d *schema.ResourceData, ui *cognitoidentityprovider.UICustomizationType) []interface{} { + if ui == nil { + return nil + } + + m := map[string]interface{}{ + "css": aws.StringValue(ui.CSS), + "css_version": aws.StringValue(ui.CSSVersion), + "image_url": aws.StringValue(ui.ImageUrl), + } + + if ui.ImageUrl != nil { + // repopulate image_file content from state, if available, + // else, the value will be overwritten + if v, ok := d.GetOk("ui_customization"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + tfMap, ok := v.([]interface{})[0].(map[string]interface{}) + if ok { + if imgFile, ok := tfMap["image_file"].(string); ok { + m["image_file"] = imgFile + } + } + } + } + + return []interface{}{m} +} diff --git a/aws/resource_aws_cognito_user_pool_client.go b/aws/resource_aws_cognito_user_pool_client.go index 27799bc6f8a..37f0ec3edb7 100644 --- a/aws/resource_aws_cognito_user_pool_client.go +++ b/aws/resource_aws_cognito_user_pool_client.go @@ -1,14 +1,18 @@ package aws import ( + "context" "fmt" "log" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cognitoidentityprovider/finder" ) func resourceAwsCognitoUserPoolClient() *schema.Resource { @@ -145,6 +149,7 @@ func resourceAwsCognitoUserPoolClient() *schema.Resource { Type: schema.TypeString, }, }, + "analytics_configuration": { Type: schema.TypeList, Optional: true, @@ -184,8 +189,40 @@ func resourceAwsCognitoUserPoolClient() *schema.Resource { }, }, - "ui_customization": cognitoUserPoolUICustomizationSchema(), + "ui_customization": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "css": { + Type: schema.TypeString, + Optional: true, + }, + "css_version": { + Type: schema.TypeString, + Computed: true, + }, + "image_file": { + Type: schema.TypeString, + Optional: true, + }, + "image_url": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, }, + + CustomizeDiff: customdiff.Sequence( + // A "ui_customization" cannot be removed from a Cognito User Pool Client resource; + // thus, resource recreation is triggered on configuration block removal. + customdiff.ForceNewIfChange("ui_customization", func(_ context.Context, old, new, meta interface{}) bool { + return len(old.([]interface{})) == 1 && len(new.([]interface{})) == 0 + }), + ), } } @@ -263,8 +300,23 @@ func resourceAwsCognitoUserPoolClientCreate(d *schema.ResourceData, meta interfa d.SetId(aws.StringValue(resp.UserPoolClient.ClientId)) - if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { - return fmt.Errorf("Error setting Cognito User Pool Client UI Customization: %s", err) + if v, ok := d.GetOk("ui_customization"); ok { + input, err := expandCognitoUserPoolUICustomizationInput(v.([]interface{})) + + if err != nil { + return fmt.Errorf("error setting Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) + } + + if input != nil { + input.ClientId = aws.String(d.Id()) + input.UserPoolId = aws.String(d.Get("user_pool_id").(string)) + + _, err := conn.SetUICustomization(input) + + if err != nil { + return fmt.Errorf("error setting Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) + } + } } return resourceAwsCognitoUserPoolClientRead(d, meta) @@ -312,14 +364,26 @@ func resourceAwsCognitoUserPoolClientRead(d *schema.ResourceData, meta interface return fmt.Errorf("error setting analytics_configuration: %s", err) } - getUICustomizationOutput, err := cognitoUserPoolUICustomizationGet(d, conn) + // Retrieve UICustomization of the User Pool the Client belongs to; + // expect to receive an InvalidParameterException if there is no associated Domain + uiCustomization, err := finder.CognitoUserPoolUICustomization(conn, resp.UserPoolClient.UserPoolId, aws.String(d.Id())) + + if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Cognito User Pool Client (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if tfawserr.ErrMessageContains(err, cognitoidentityprovider.ErrCodeInvalidParameterException, "There has to be an existing domain associated with this user pool") { + return nil + } if err != nil { - return fmt.Errorf("Error reading Cognito User Pool Client UI Customization: %s", err) + return fmt.Errorf("error getting Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) } - if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(getUICustomizationOutput.UICustomization)); err != nil { - return fmt.Errorf("Failed setting ui_customization: %s", err) + if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(d, uiCustomization)); err != nil { + return fmt.Errorf("error setting ui_customization: %w", err) } return nil @@ -396,8 +460,25 @@ func resourceAwsCognitoUserPoolClientUpdate(d *schema.ResourceData, meta interfa return fmt.Errorf("Error updating Cognito User Pool Client: %s", err) } - if err = cognitoUserPoolUICustomizationSet(d, conn); err != nil { - return fmt.Errorf("Error updating User Pool Client UI Customization: %s", err) + if d.HasChange("ui_customization") { + if v, ok := d.GetOk("ui_customization"); ok { + input, err := expandCognitoUserPoolUICustomizationInput(v.([]interface{})) + + if err != nil { + return fmt.Errorf("error updating Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) + } + + if input != nil { + input.ClientId = aws.String(d.Id()) + input.UserPoolId = aws.String(d.Get("user_pool_id").(string)) + + _, err := conn.SetUICustomization(input) + + if err != nil { + return fmt.Errorf("error updating Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) + } + } + } } return resourceAwsCognitoUserPoolClientRead(d, meta) diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index 4f46c1e5a42..317495822a2 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -30,6 +30,7 @@ func TestAccAWSCognitoUserPoolClient_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", clientName), resource.TestCheckResourceAttr(resourceName, "explicit_auth_flows.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "explicit_auth_flows.*", "ADMIN_NO_SRP_AUTH"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), ), }, { @@ -166,28 +167,6 @@ func TestAccAWSCognitoUserPoolClient_allFields(t *testing.T) { }) } -func TestAccAWSCognitoUserPoolClient_withUICustomization(t *testing.T) { - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - clientName := acctest.RandString(10) - css := ".label-customizable {font-weight: 400;}" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolClientConfig_withUICustomization(userPoolName, clientName, css), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("aws_cognito_user_pool_client.client", "ui_customization.#", "1"), - resource.TestCheckResourceAttr("aws_cognito_user_pool_client.client", "ui_customization.0.css", css), - resource.TestCheckResourceAttrSet("aws_cognito_user_pool_client.client", "ui_customization.0.image_file"), - ), - }, - }, - }) -} - func TestAccAWSCognitoUserPoolClient_allFieldsUpdatingOneField(t *testing.T) { var client cognitoidentityprovider.UserPoolClientType userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) @@ -355,6 +334,225 @@ func TestAccAWSCognitoUserPoolClient_disappears(t *testing.T) { }) } +func TestAccAWSCognitoUserPoolClient_UICustomization_CSS(t *testing.T) { + var before, after cognitoidentityprovider.UserPoolClientType + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_client.test" + + css := ".label-customizable {font-weight: 400;}" + cssUpdated := ".label-customizable {font-weight: 100;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), + ImportStateVerify: true, + }, + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, cssUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", cssUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), + ImportStateVerify: true, + }, + { + // Test removing the UICustomization settings, forcing new resource + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationRemoved(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &after), + testAccCheckAWSCognitoUserPoolClientRecreated(&before, &after), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolClient_UICustomization_ImageFile(t *testing.T) { + var before, after cognitoidentityprovider.UserPoolClientType + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_client.test" + + filename := "testdata/service/cognitoidentityprovider/logo.png" + updatedFilename := "testdata/service/cognitoidentityprovider/logo_modified.png" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, updatedFilename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + { + // Test removing the UICustomization settings, forcing new resource + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationRemoved(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), + testAccCheckAWSCognitoUserPoolClientRecreated(&before, &after), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolClient_UICustomization_CSSandImageFile(t *testing.T) { + var client cognitoidentityprovider.UserPoolClientType + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_client.test" + + css := ".label-customizable {font-weight: 400;}" + filename := "testdata/service/cognitoidentityprovider/logo.png" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSSAndImage(rName, css, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), + ), + }, + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolClient_UICustomization_InheritedFromUserPool(t *testing.T) { + var client cognitoidentityprovider.UserPoolClientType + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_client.test" + poolName := "aws_cognito_user_pool.test" + + css := ".label-customizable {font-weight: 400;}" + cssUpdated := ".label-customizable {font-weight: 100;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(poolName), + ), + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(poolName), + ), + }, + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationInherited(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(poolName), + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndConfigured(rName, css), + PlanOnly: true, + }, + { + Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndOverridden(rName, css, cssUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(poolName), + testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", cssUpdated), + ), + }, + }, + }) +} + func testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[resourceName] @@ -455,6 +653,16 @@ func testAccCheckAWSCognitoUserPoolClientDisappears(client *cognitoidentityprovi } } +func testAccCheckAWSCognitoUserPoolClientRecreated(before, after *cognitoidentityprovider.UserPoolClientType) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(before.ClientId) == aws.StringValue(after.ClientId) { + return fmt.Errorf("Cognito User Pool Client not recreated") + } + + return nil + } +} + func testAccAWSCognitoUserPoolClientConfig_basic(userPoolName, clientName string) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { @@ -628,19 +836,182 @@ resource "aws_cognito_user_pool_client" "test" { `, clientName) } -func testAccAWSCognitoUserPoolClientConfig_withUICustomization(userPoolName, clientName string, css string) string { +func testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, css string) string { return fmt.Sprintf(` -resource "aws_cognito_user_pool" "pool" { - name = "%s" +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + + ui_customization { + css = %q + } + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, css) +} + +func testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, filename string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + + ui_customization { + image_file = filebase64(%q) + } + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, filename) +} + +func testAccAWSCognitoUserPoolClientConfig_UICustomizationCSSAndImage(rName, css, filename string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id } -resource "aws_cognito_user_pool_client" "client" { - name = "%s" - user_pool_id = aws_cognito_user_pool.pool.id +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + + ui_customization { + css = %q + image_file = filebase64(%q) + } + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, css, filename) +} + +func testAccAWSCognitoUserPoolClientConfig_UICustomizationRemoved(rName string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName) +} + +func testAccAWSCognitoUserPoolClientConfig_UICustomizationInherited(rName, css string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q + + ui_customization { + css = %q + } +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, css) +} + +func testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndConfigured(rName, css string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q + + ui_customization { + css = %q + } +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + + ui_customization { + css = aws_cognito_user_pool.test.ui_customization.0.css + } + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, css) +} + +func testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndOverridden(rName, css, cssUpdated string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q + + ui_customization { + css = %[2]q + } +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + ui_customization { - css = "%s" - image_file = "test-fixtures/logo.png" + css = %[3]q } + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id } -`, userPoolName, clientName, css) +`, rName, css, cssUpdated) } diff --git a/aws/resource_aws_cognito_user_pool_test.go b/aws/resource_aws_cognito_user_pool_test.go index abc9f31dbcc..d636cac2598 100644 --- a/aws/resource_aws_cognito_user_pool_test.go +++ b/aws/resource_aws_cognito_user_pool_test.go @@ -89,6 +89,7 @@ func TestAccAWSCognitoUserPool_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "sms_configuration.#", "0"), resource.TestCheckResourceAttr(resourceName, "software_token_mfa_configuration.#", "0"), resource.TestCheckResourceAttr(resourceName, "account_recovery_setting.#", "0"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), ), }, { @@ -1057,6 +1058,182 @@ func TestAccAWSCognitoUserPool_withSchemaAttributes(t *testing.T) { }) } +func TestAccAWSCognitoUserPool_withUICustomization_CSS(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool.test" + + css := ".label-customizable {font-weight: 400;}" + cssUpdated := ".label-customizable {font-weight: 100;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), + ), + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, cssUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", cssUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test removing the UICustomization settings, forcing new resource + Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSCognitoUserPool_withUICustomization_ImageFile(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool.test" + + filename := "testdata/service/cognitoidentityprovider/logo.png" + updatedFilename := "testdata/service/cognitoidentityprovider/logo_modified.png" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), + ), + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, updatedFilename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + { + // Test removing the UICustomization settings, forcing new resource + Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSCognitoUserPool_withUICustomization_CSSandImageFile(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool.test" + + css := ".label-customizable {font-weight: 400;}" + filename := "testdata/service/cognitoidentityprovider/logo.png" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), + ), + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSSAndImage(rName, css, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), + ), + }, + { + Config: testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, + }, + }, + }) +} + func TestAccAWSCognitoUserPool_withVerificationMessageTemplate(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool.test" @@ -1110,27 +1287,6 @@ func TestAccAWSCognitoUserPool_withVerificationMessageTemplate(t *testing.T) { }) } -func TestAccAWSCognitoUserPool_withUICustomization(t *testing.T) { - userPoolName := fmt.Sprintf("tf-acc-cognito-user-pool-%s", acctest.RandString(7)) - css := ".label-customizable {font-weight: 400;}" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomization(userPoolName, css), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("aws_cognito_user_pool_pool.pool", "ui_customization.#", "1"), - resource.TestCheckResourceAttr("aws_cognito_user_pool_pool.pool", "ui_customization.0.css", css), - resource.TestCheckResourceAttrSet("aws_cognito_user_pool_pool.pool", "ui_customization.0.image_file"), - ), - }, - }, - }) -} - func TestAccAWSCognitoUserPool_update(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") optionalMfa := "OPTIONAL" @@ -2039,15 +2195,67 @@ resource "aws_cognito_user_pool" "test" { `, name, mfaconfig, smsAuthMsg) } -func testAccAWSCognitoUserPoolConfig_withUICustomization(userPoolName, css string) string { +func testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} +`, rName) +} + +func testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool" "test" { + name = %[1]q + + ui_customization { + css = %q + } +} +`, rName, css) +} + +func testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, filename string) string { return fmt.Sprintf(` -resource "aws_cognito_user_pool" "pool" { - name = "%s" +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool" "test" { + name = %[1]q + + ui_customization { + image_file = filebase64(%q) + } +} +`, rName, filename) +} + +func testAccAWSCognitoUserPoolConfig_withUICustomizationCSSAndImage(rName, css, filename string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool" "test" { + name = %[1]q ui_customization { - css = "%s" - image_file = "test-fixtures/logo.png" + css = %q + image_file = filebase64(%q) } } -`, userPoolName, css) +`, rName, css, filename) } diff --git a/aws/resource_aws_lambda_function.go b/aws/resource_aws_lambda_function.go index 8db75e001fc..0519e185f52 100644 --- a/aws/resource_aws_lambda_function.go +++ b/aws/resource_aws_lambda_function.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io/ioutil" "log" "regexp" "time" @@ -18,6 +19,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + homedir "github.com/mitchellh/go-homedir" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/lambda/waiter" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" @@ -1204,6 +1206,19 @@ func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) e return resourceAwsLambdaFunctionRead(d, meta) } +// loadFileContent returns contents of a file in a given path +func loadFileContent(v string) ([]byte, error) { + filename, err := homedir.Expand(v) + if err != nil { + return nil, err + } + fileContent, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return fileContent, nil +} + func readEnvironmentVariables(ev map[string]interface{}) map[string]string { variables := make(map[string]string) for k, v := range ev { diff --git a/aws/structure.go b/aws/structure.go index fb7c2a19d21..1781aa7bb81 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -3158,28 +3158,6 @@ func flattenCognitoUserPoolVerificationMessageTemplate(s *cognitoidentityprovide return []map[string]interface{}{} } -func flattenCognitoUserPoolUICustomization(s *cognitoidentityprovider.UICustomizationType) []map[string]interface{} { - m := map[string]interface{}{} - - if s == nil { - return nil - } - - if s.CSS != nil { - m["css"] = *s.CSS - } - - if s.ImageUrl != nil { - m["image_file"] = *s.ImageUrl - } - - if len(m) > 0 { - return []map[string]interface{}{m} - } - - return []map[string]interface{}{} -} - func sliceContainsMap(l []interface{}, m map[string]interface{}) (int, bool) { for i, t := range l { if reflect.DeepEqual(m, t.(map[string]interface{})) { diff --git a/aws/test-fixtures/logo.png b/aws/testdata/service/cognitoidentityprovider/logo.png similarity index 100% rename from aws/test-fixtures/logo.png rename to aws/testdata/service/cognitoidentityprovider/logo.png diff --git a/aws/testdata/service/cognitoidentityprovider/logo_modified.png b/aws/testdata/service/cognitoidentityprovider/logo_modified.png new file mode 100644 index 0000000000000000000000000000000000000000..71db86115cb225dfd8ea1b4709b72e51daec4ed2 GIT binary patch literal 5101 zcmbuCcT`i$*1%5)9VH?H3PLW@RGNr@1&P81DI#{-r3g|2hKLdf0tq67UPPrSpaP;I za_Jh70HN3;9TVvYQUW;wA)4gmi@vw+TkHGlTi=^mYi6HyX7-ui>^*zW5ey5)flc-{ zb~XSE1^~9g3J4~EG;2KC9{?O3f!zQANCIN8%>Y~|!GslnDFb4^WdN{;sr)0KhVA_K z7!d$SL<6G#9^)qLe-)wTU$g(+!|%iXqw&7TKckP_hyNox{F)|U0{c$}V#Bak1F@I? zFgDl&?6NbVbJuFRQpDS@v(uzu3w<&9B>+IaMd!LD^+5Q6ut^TyO zIc95Tf9CAD^Uf}=ZoU`&`~w2fL07|Y;Su=AYd7K(NQp_wDcQGf-^t0%%P)9XQd;(? z{PB~gHMMp14UI2elG~{ron76p-@NVZ>mL{#8vab5n4FsaIy3uiZXW!;vbx4z=luBj zi%Ynl|H2Zs|04TeTyjD#5m8aNsKhTWm`KDga5+&ibtCc3M^8yy2$kQl_qya3%bO3X z+oUv%PqP$!uJlMNYVMoR0)L_XCi}mE#r?m?{sH?B7Xz>oI_7^@1VSbMod)4-!HCc; zMFn(V0~{uFCb%4c0{D+X1aG?`cQv&*DDKE zQ1<%d2e=K0N-%ka>}XK}#qXBQ7pZ|tq%{?NUn+g{W@z$i%rUEhOWH4M`u9Cih;LCX zpwC?JUY=dpyifKn@r^DdyRRtNkfVoSmW2lBx;bINns!_5+PIj#wwg|{6d?a%Lc}xn zQsrwuTujGXmCYh8)&fA3A!HdO&;j z^awUSskj;McT2)O#dtR8Dl^x*1NP*{l^<#m`LpVx=Ey*GOXV=h*uU~{z6AfymXM#V#V_<+u9U5QUQ z!S+LINYTL21C0Ybg(-&aV^lp)k$j>?qZc=(@XbO7l!#9+8)y{n_(io*G|}0n7wGVF zwUuEz;|sP)Z1Z$kJ7Vagw!F2)FA7_F z<$XnV7)72IQQqZP-H5)rKk%rMj+2@Bs2hTl_hsItsaE5-_`OI`?@i}V9!)OWdV2;J zczfxE-LQB|-I*CY`6&CHYITWp6)q~+j~&t6+*oUt%cjTN9ho;B%#5t~t3>P0okHI7 z7H8#Vu%z2BGDI`@(8XJS?lB)H#}Wmfpm5^&)Qjm`kn{o<^JT}6tYcuUV~X8FayGOj z8yic6(Ix3~t(X=4S8d4`>5KZa>1Hx^($MB1uBp((mc;QaWHo}frP!wF)>JoDautvqnl}Ma>W;#V?U%HJyi?Yu{X|v$k8z4DH=#gOR2Mp({h}4;Gdr> z@fxmq_dd%UtJRz%cKLhI`RwMKfA+ld`L6f$15Y7{2!cxd8>>}(6MC2cFjteX2LwO> zf}_*TQz&O^3r}AT9`_s-;FO%K)KO^Y)X)qjwqjN3ZusCSWPG-}uES@8?s)5gkGWG` z8?4yF-kBX^vN)^)Ui`qI$9b9u)I*B0rP$~9ocd~zs%$;mOu~Sg`}Nr*a?Dfq($lWP zHm6f3KPeA#s9W4Ccv>ynd3N9s6(Ro(+QX7a;aQCf00q4NMhN@s77PFxUYq)UxfOL>(nU17>5)- zox!&+ObUR^Gp_un1O+_eH2+~$7*^gZkjQ#5J177y9;#9aKJSUTOAR=h>sr$veM)`M zWK8DAWxV~*x#qitYD*oj3StRm#5O15_zZD)fxR6X<6u~d9!Pcr|NiQ`ib~|kYF2S_ zLHBqfwKAytWL@8yJ5nwBTo2PEXOMTUME`xCS!~O;VSHwX#kS_^TXYSpd-?}Z;E^3)IZ z4>=vqPi#yU?)1_dIc32%D6zn6 z4P`if`BTodE`6WAvz!X9G3U{K6Pcti8p-p(r)F!`f}KsBvQ6fM>JGnw``kV@C{%1q z7#eFp{fe`a6s{u2sf~=h5lv(nwxMKsW?cKAQBzPh|H6b}Tqr(@?S|aQI~P&am{_&e zBmlO}heNt|>Kt`WaKdpVQ-#(i=L!bX@0%>A*2N-Lj@0&58ODFW%I#t3+{Inh&@|1d zz84>S_eqL-+wA`Dm4mLQmY#12Z+NHwzS>d%FqC*IcZ6i7{KjY`_Y_Z@-vsT&TTYD| zwxdXDI_7I?T}bLHG<^%P8p)GS--l59@UV)h%C$$|Y{MyaQBss;3&_~( z^*OzZVcZ`UlF1!fAt;J2#q&JuhNgRgV z=b-ZEi8%!(V)<)BBr!6yl*IKc!wCR+yfpp!ARy?4=3BdzBs8WOY>#*aj~U|&*>YI~ zu+Ffl%#i2Q7T~?}wK@3Os&ED~8rt4iI5IyN^nGzRK7F}z`AI@>;8&4-J7XTChpB%5Svy)XF2(>Aql zVLL-jGr5Bojv#diqhBB@s=pmJ4?e=`ohRRUl~HmyA+2$p4Ozc;!-4&mHVpkNEM4DJst*~DH5^-1;+`XP zAmvLru3YjZh%1NC_O8eF~PuSAG zrt)grl_I@9bE7CIzEus!H6;unktjc>6i+0llO;b{o?)TLvtd;}W7AQoJozfSl7%d^ zgIZlSok#c24P0@X3-_YCrFT@LO?w@% zoxCmpawCZ!qi_mPABAOinK&_6%yQ&&+XMhfiGRCBGh_c> zQWgmOaX0zx#2wFSM96=|Vu#8{&=r{L3VHBV#J2H>FGi%h5%-SDOlMhq@`vw23k?)c zMF#sc8J~E~Wh9D~j<6f7+tW!_1aaT9`_mR;Lwa2VfOmp-=j}t6uN+YckuvzYNe`P! z)qJwjxxp!6U`eB6RP8RNA)`uWbH$4IA5!KtBc;F;jnLVxjen*F*MWWy z92f{`rN9E=f<9;g6*;s02ZX)%;|m7rV;9##0wC0D)r*O!C3Y|9@EkG<6RHJ3a>l$5 ze}J(b?uz*gJ=4h}P`@t!;AQ=QI#y7GTT2ILmn18+wmO(d3jRD0?I3(HHcm-N)CfgL*U? z>t51p_VS)(1=o}SFxPz*38{`a4bj7C$KM3C&WP?$%eWJ0B}aDI5O@J&)BC>T?A8xu zAMdgmsXq7M8`kkJb5P_J0(jd(ITl4LhomC}fcQ;3qiw3gONlamuSjEnV898oGg0;7 zvSUD~$`{f3VRPU4{m_?`~s?YT`$&)goV)ecpHhlM{HBjFICRU+Me zC3a89YX&8J!K1~qO((yOf8(v;ZSIjw)UZV$_n>=-+8^TM1~aBKeab+j=h^(%xr<}| zE5w)=#O@(>Bw3glJH0shrsXTH#EvSH-8*V+o;|oT^Jx;dUwv|599?fa?0dUkTg0LB zfRX{+UJB@!UD6-*Ues2`jeN`1d9M^sPpWQwi6|V6HVs&;wp;H-jO>zX7ygZX{vW$s BvA+NS literal 0 HcmV?d00001 diff --git a/aws/utils.go b/aws/utils.go index 1db3a469780..49c56f14d73 100644 --- a/aws/utils.go +++ b/aws/utils.go @@ -1,17 +1,12 @@ package aws import ( - "crypto/sha1" "encoding/base64" - "encoding/hex" "encoding/json" - "io/ioutil" - "net/http" "reflect" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - homedir "github.com/mitchellh/go-homedir" ) // Base64Encode encodes data if the input isn't already encoded using base64.StdEncoding.EncodeToString. @@ -66,56 +61,3 @@ func appendUniqueString(slice []string, elem string) []string { } return append(slice, elem) } - -// loadFileContent returns contents of a file in a given path -func loadFileContent(v string) ([]byte, error) { - filename, err := homedir.Expand(v) - if err != nil { - return nil, err - } - fileContent, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return fileContent, nil -} - -func byteHashSum(content []byte) string { - hash := sha1.Sum(content) - - return hex.EncodeToString(hash[:]) -} - -func stringHashSum(css string) string { - v := []byte(css) - hash := sha1.Sum(v) - return hex.EncodeToString(hash[:]) -} - -func remoteFileContent(v string) ([]byte, error) { - match, _ := regexp.MatchString(`^https?:\/\/`, v) - - if match { - resp, err := http.Get(v) - - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - return ioutil.ReadAll(resp.Body) - } else { - return loadFileContent(v) - } -} - -func remoteFileHashSum(v string) string { - content, err := remoteFileContent(v) - - if err != nil { - return "" - } - - return byteHashSum(content) -} diff --git a/website/docs/r/cognito_user_pool.markdown b/website/docs/r/cognito_user_pool.markdown index f74e5683200..08bb7a32094 100644 --- a/website/docs/r/cognito_user_pool.markdown +++ b/website/docs/r/cognito_user_pool.markdown @@ -43,7 +43,7 @@ resource "aws_cognito_user_pool" "example" { ### Using Account Recovery Setting ```hcl -resource "aws_cognito_user_pool" "test" { +resource "aws_cognito_user_pool" "example" { name = "mypool" account_recovery_setting { @@ -60,6 +60,25 @@ resource "aws_cognito_user_pool" "test" { } ``` +### Specifying UI Customization Settings for all clients (only applicable on resource updates) + +```hcl +resource "aws_cognito_user_pool" "example" { + name = "mypool" + + # ... other configuration ... + ui_customization { + css = ".label-customizable {font-weight: 400;}" + image_file = filebase64("logo.png") + } +} + +resource "aws_cognito_user_pool_domain" "example" { + domain = "example" + user_pool_id = aws_cognito_user_pool.example.id +} +``` + ## Argument Reference The following arguments are supported: @@ -89,7 +108,7 @@ The following arguments are supported: * `user_pool_add_ons` - (Optional) Configuration block for [user pool add-ons](#user-pool-add-ons) to enable user pool advanced security mode features. * `verification_message_template` (Optional) - The [verification message templates](#verification-message-template) configuration. * `account_recovery_setting` (Optional) - The [account_recovery_setting](#account-recovery-setting) configuration. -* `ui_customization` (Optional) - The uncustomized clients [UI Customization](#ui-customization). +* `ui_customization` (Optional) - Configuration block for [UI customization](#ui-customization) information for a user pool's built-in app UI. To use this feature, the user pool must have a domain associated with it. Once configured, the UI customization will be used for every client that has no UI customization set previously. Removing this configuration forces new resource. #### Admin Create User Config @@ -199,6 +218,11 @@ The following arguments are required in the `software_token_mfa_configuration` c * `advanced_security_mode` (Required) - The mode for advanced security, must be one of `OFF`, `AUDIT` or `ENFORCED`. +#### UI Customization + +* `css` (Optional) - The CSS values in the UI customization, provided as a String. +* `image_file` (Optional) - The uploaded logo image in the UI customization, provided as a base64-encoded String. Drift detection is not possible for this argument. + #### Verification Message Template * `default_email_option` (Optional) - The default email option. Must be either `CONFIRM_WITH_CODE` or `CONFIRM_WITH_LINK`. Defaults to `CONFIRM_WITH_CODE`. @@ -214,11 +238,6 @@ The following arguments are required in the `software_token_mfa_configuration` c * `name` (Required) - Specifies the recovery method for a user. Can be of the following: `verified_email`, `verified_phone_number`, and `admin_only`. * `priority` (Required) - A positive integer specifying priority of a method with 1 being the highest priority. -#### UI Customization - -* `css` (Optional) - The customized CSS. -* `image_file` (Optional) - The local image file path. - ## Attributes Reference In addition to all arguments above, the following attributes are exported: @@ -228,7 +247,10 @@ In addition to all arguments above, the following attributes are exported: * `endpoint` - The endpoint name of the user pool. Example format: cognito-idp.REGION.amazonaws.com/xxxx_yyyyy * `creation_date` - The date the user pool was created. * `last_modified_date` - The date the user pool was last modified. - +* `ui_customization` - The UI customization information for a user pool's built-in app UI. + * `css_version` - The CSS version number. + * `image_url` - The logo image URL for the UI customization. + ## Import Cognito User Pools can be imported using the `id`, e.g. diff --git a/website/docs/r/cognito_user_pool_client.markdown b/website/docs/r/cognito_user_pool_client.markdown index c9dc7186fe4..e8ef33f516a 100644 --- a/website/docs/r/cognito_user_pool_client.markdown +++ b/website/docs/r/cognito_user_pool_client.markdown @@ -110,26 +110,29 @@ resource "aws_cognito_user_pool_client" "test" { } ``` -### Create a user pool client with UI customization +### Create a user pool client with UI customization settings ```hcl resource "aws_cognito_user_pool" "pool" { name = "pool" } -data "template_file" "custom_css" { - template = file("files/custom.css") +resource "aws_cognito_user_pool_domain" "domain" { + domain = "example" + user_pool_id = aws_cognito_user_pool.pool.id } resource "aws_cognito_user_pool_client" "client" { name = "client" - user_pool_id = aws_cognito_user_pool.pool.id - ui_customization { - css = data.template_file.custom_css.rendered - image_file = "files/logo.png" + css = ".label-customizable {font-weight: 400;}" + image_file = filebase64("logo.png") } + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure the domain is 'Active' + user_pool_id = aws_cognito_user_pool_domain.domain.user_pool_id } ``` @@ -153,7 +156,7 @@ The following arguments are supported: * `user_pool_id` - (Required) The user pool the client belongs to. * `write_attributes` - (Optional) List of user pool attributes the application client can write to. * `analytics_configuration` - (Optional) The Amazon Pinpoint analytics configuration for collecting metrics for this user pool. -* `ui_customization` (Optional) - The [UI Customization](#ui-customization). +* `ui_customization` (Optional) - Configuration block for [UI customization](#ui-customization) information for the user pool's built-in app UI. To use this feature, the user pool the client belongs to must have a domain associated with it. If UI customization settings are configured within the [`aws_cognito_user pool` resource](cognito_user_pool.markdown), this must be configured to prevent plan-time differences. Removing this configuration forces new resource. ### Analytics Configuration @@ -167,16 +170,19 @@ Either `application_arn` or `application_id` is required. #### UI Customization -* `css` (Optional) - The customized CSS. -* `image_file` (Optional) - The local image file path. - +* `css` (Optional) - The CSS values in the UI customization, provided as a String. +* `image_file` (Optional) - The uploaded logo image in the UI customization, provided as a base64-encoded String. Drift detection is not possible for this argument. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: * `id` - The id of the user pool client. * `client_secret` - The client secret of the user pool client. - +* `ui_customization` - The UI customization information for the user pool's built-in app UI. + * `css_version` - The CSS version number. + * `image_url` - The logo image URL for the UI customization. + ## Import Cognito User Pool Clients can be imported using the `id` of the Cognito User Pool, and the `id` of the Cognito User Pool Client, e.g. From 658eb93d854e5e21d2e216bf715bb5522132b7d2 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Tue, 23 Feb 2021 14:09:56 -0500 Subject: [PATCH 09/11] fix tflint errors --- aws/resource_aws_cognito_user_pool_client_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index 317495822a2..a3bd0628c1f 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -937,7 +937,7 @@ func testAccAWSCognitoUserPoolClientConfig_UICustomizationInherited(rName, css s return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { name = %[1]q - + ui_customization { css = %q } @@ -962,7 +962,7 @@ func testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndConfigured return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { name = %[1]q - + ui_customization { css = %q } @@ -977,7 +977,7 @@ resource "aws_cognito_user_pool_client" "test" { name = %[1]q ui_customization { - css = aws_cognito_user_pool.test.ui_customization.0.css + css = aws_cognito_user_pool.test.ui_customization[0].css } # Refer to the aws_cognito_user_pool_domain resource's @@ -991,7 +991,7 @@ func testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndOverridden return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { name = %[1]q - + ui_customization { css = %[2]q } From dc397fbd838a418ebc1cb1adf3c550207bffc921 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Wed, 24 Feb 2021 22:33:32 -0500 Subject: [PATCH 10/11] refactor UI customization settings; add test coverage and documentation --- .changelog/8114.txt | 8 +- .../cognitoidentityprovider/finder/finder.go | 13 +- aws/provider.go | 3 +- aws/resource_aws_cognito_user_pool.go | 132 ---- aws/resource_aws_cognito_user_pool_client.go | 101 --- ...ource_aws_cognito_user_pool_client_test.go | 410 ---------- aws/resource_aws_cognito_user_pool_test.go | 242 ------ ..._aws_cognito_user_pool_ui_customization.go | 223 ++++++ ...cognito_user_pool_ui_customization_test.go | 706 ++++++++++++++++++ website/docs/r/cognito_user_pool.markdown | 32 +- .../docs/r/cognito_user_pool_client.markdown | 37 +- ...o_user_pool_ui_customization.html.markdown | 92 +++ 12 files changed, 1033 insertions(+), 966 deletions(-) create mode 100644 aws/resource_aws_cognito_user_pool_ui_customization.go create mode 100644 aws/resource_aws_cognito_user_pool_ui_customization_test.go create mode 100644 website/docs/r/cognito_user_pool_ui_customization.html.markdown diff --git a/.changelog/8114.txt b/.changelog/8114.txt index adce5755345..4538eb4af9a 100644 --- a/.changelog/8114.txt +++ b/.changelog/8114.txt @@ -1,7 +1,3 @@ -```release-note:enhancement -resource/aws_cognito_user_pool: Add `ui_customization` argument -``` - -```release-note:enhancement -resource/aws_cognito_user_pool_client: Add `ui_customization` argument +```release-note:new-resource +aws_cognito_user_pool_ui_customization ``` diff --git a/aws/internal/service/cognitoidentityprovider/finder/finder.go b/aws/internal/service/cognitoidentityprovider/finder/finder.go index 8c8f27b69f7..692a7bad474 100644 --- a/aws/internal/service/cognitoidentityprovider/finder/finder.go +++ b/aws/internal/service/cognitoidentityprovider/finder/finder.go @@ -3,18 +3,16 @@ package finder import ( "reflect" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" ) -// CognitoUserPoolUICustomization returns the UI Customization corresponding to the UserPoolId and ClientId, if provided. +// CognitoUserPoolUICustomization returns the UI Customization corresponding to the UserPoolId and ClientId. // Returns nil if no UI Customization is found. -func CognitoUserPoolUICustomization(conn *cognitoidentityprovider.CognitoIdentityProvider, userPoolId, clientId *string) (*cognitoidentityprovider.UICustomizationType, error) { +func CognitoUserPoolUICustomization(conn *cognitoidentityprovider.CognitoIdentityProvider, userPoolId, clientId string) (*cognitoidentityprovider.UICustomizationType, error) { input := &cognitoidentityprovider.GetUICustomizationInput{ - UserPoolId: userPoolId, - } - - if clientId != nil { - input.ClientId = clientId + ClientId: aws.String(clientId), + UserPoolId: aws.String(userPoolId), } output, err := conn.GetUICustomization(input) @@ -29,7 +27,6 @@ func CognitoUserPoolUICustomization(conn *cognitoidentityprovider.CognitoIdentit // The GetUICustomization API operation will return an empty struct // if nothing is present rather than nil or an error, so we equate that with nil - // to prevent non-empty plans of an empty ui_customization block if reflect.DeepEqual(output.UICustomization, &cognitoidentityprovider.UICustomizationType{}) { return nil, nil } diff --git a/aws/provider.go b/aws/provider.go index 29273c51f04..00ba0af2e9b 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -519,13 +519,14 @@ func Provider() *schema.Provider { "aws_cognito_identity_pool": resourceAwsCognitoIdentityPool(), "aws_cognito_identity_pool_roles_attachment": resourceAwsCognitoIdentityPoolRolesAttachment(), "aws_cognito_identity_provider": resourceAwsCognitoIdentityProvider(), + "aws_cognito_resource_server": resourceAwsCognitoResourceServer(), "aws_cognito_user_group": resourceAwsCognitoUserGroup(), "aws_cognito_user_pool": resourceAwsCognitoUserPool(), "aws_cognito_user_pool_client": resourceAwsCognitoUserPoolClient(), "aws_cognito_user_pool_domain": resourceAwsCognitoUserPoolDomain(), + "aws_cognito_user_pool_ui_customization": resourceAwsCognitoUserPoolUICustomization(), "aws_cloudhsm_v2_cluster": resourceAwsCloudHsmV2Cluster(), "aws_cloudhsm_v2_hsm": resourceAwsCloudHsmV2Hsm(), - "aws_cognito_resource_server": resourceAwsCognitoResourceServer(), "aws_cloudwatch_composite_alarm": resourceAwsCloudWatchCompositeAlarm(), "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), "aws_cloudwatch_dashboard": resourceAwsCloudWatchDashboard(), diff --git a/aws/resource_aws_cognito_user_pool.go b/aws/resource_aws_cognito_user_pool.go index 8bd562a3d6f..341ee3043f1 100644 --- a/aws/resource_aws_cognito_user_pool.go +++ b/aws/resource_aws_cognito_user_pool.go @@ -1,8 +1,6 @@ package aws import ( - "context" - "encoding/base64" "fmt" "log" "regexp" @@ -10,13 +8,10 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cognitoidentityprovider/finder" ) func resourceAwsCognitoUserPool() *schema.Resource { @@ -567,41 +562,7 @@ func resourceAwsCognitoUserPool() *schema.Resource { }, }, }, - - "ui_customization": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "css": { - Type: schema.TypeString, - Optional: true, - }, - "css_version": { - Type: schema.TypeString, - Computed: true, - }, - "image_file": { - Type: schema.TypeString, - Optional: true, - }, - "image_url": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, }, - - CustomizeDiff: customdiff.Sequence( - // A "ui_customization" cannot be removed from a Cognito User Pool resource; - // thus, resource recreation is triggered on configuration block removal - customdiff.ForceNewIfChange("ui_customization", func(_ context.Context, old, new, meta interface{}) bool { - return len(old.([]interface{})) == 1 && len(new.([]interface{})) == 0 - }), - ), } } @@ -973,25 +934,6 @@ func resourceAwsCognitoUserPoolRead(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("error setting software_token_mfa_configuration: %w", err) } - // Retrieve UICustomization iff the User Pool is associated with a Domain - if resp.UserPool.Domain != nil { - uiCustomization, err := finder.CognitoUserPoolUICustomization(conn, resp.UserPool.Id, nil) - - if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { - log.Printf("[WARN] Cognito User Pool (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if err != nil { - return fmt.Errorf("error getting Cognito User Pool (%s) UI customization: %w", d.Id(), err) - } - - if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(d, uiCustomization)); err != nil { - return fmt.Errorf("error setting ui_customization: %w", err) - } - } - return nil } @@ -1252,26 +1194,6 @@ func resourceAwsCognitoUserPoolUpdate(d *schema.ResourceData, meta interface{}) } } - if d.HasChange("ui_customization") { - if v, ok := d.GetOk("ui_customization"); ok { - input, err := expandCognitoUserPoolUICustomizationInput(v.([]interface{})) - - if err != nil { - return fmt.Errorf("error updating Cognito User pool (%s) UI customization: %w", d.Id(), err) - } - - if input != nil { - input.UserPoolId = aws.String(d.Id()) - - _, err := conn.SetUICustomization(input) - - if err != nil { - return fmt.Errorf("error updating Cognito User pool (%s) UI customization: %w", d.Id(), err) - } - } - } - } - return resourceAwsCognitoUserPoolRead(d, meta) } @@ -1390,33 +1312,6 @@ func expandCognitoUserPoolAccountRecoverySettingConfig(config map[string]interfa return configs } -func expandCognitoUserPoolUICustomizationInput(l []interface{}) (*cognitoidentityprovider.SetUICustomizationInput, error) { - if len(l) == 0 || l[0] == nil { - return nil, nil - } - - tfMap, ok := l[0].(map[string]interface{}) - if !ok { - return nil, nil - } - - input := &cognitoidentityprovider.SetUICustomizationInput{} - - if v, ok := tfMap["css"].(string); ok && v != "" { - input.CSS = aws.String(v) - } - - if v, ok := tfMap["image_file"].(string); ok && v != "" { - imgFile, err := base64.StdEncoding.DecodeString(v) - if err != nil { - return nil, err - } - input.ImageFile = imgFile - } - - return input, nil -} - func flattenCognitoUserPoolAccountRecoverySettingConfig(config *cognitoidentityprovider.AccountRecoverySettingType) []interface{} { if config == nil { return nil @@ -1438,30 +1333,3 @@ func flattenCognitoUserPoolAccountRecoverySettingConfig(config *cognitoidentityp return []interface{}{settings} } - -func flattenCognitoUserPoolUICustomization(d *schema.ResourceData, ui *cognitoidentityprovider.UICustomizationType) []interface{} { - if ui == nil { - return nil - } - - m := map[string]interface{}{ - "css": aws.StringValue(ui.CSS), - "css_version": aws.StringValue(ui.CSSVersion), - "image_url": aws.StringValue(ui.ImageUrl), - } - - if ui.ImageUrl != nil { - // repopulate image_file content from state, if available, - // else, the value will be overwritten - if v, ok := d.GetOk("ui_customization"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - tfMap, ok := v.([]interface{})[0].(map[string]interface{}) - if ok { - if imgFile, ok := tfMap["image_file"].(string); ok { - m["image_file"] = imgFile - } - } - } - } - - return []interface{}{m} -} diff --git a/aws/resource_aws_cognito_user_pool_client.go b/aws/resource_aws_cognito_user_pool_client.go index 37f0ec3edb7..e2132390e5a 100644 --- a/aws/resource_aws_cognito_user_pool_client.go +++ b/aws/resource_aws_cognito_user_pool_client.go @@ -1,18 +1,14 @@ package aws import ( - "context" "fmt" "log" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cognitoidentityprovider/finder" ) func resourceAwsCognitoUserPoolClient() *schema.Resource { @@ -149,7 +145,6 @@ func resourceAwsCognitoUserPoolClient() *schema.Resource { Type: schema.TypeString, }, }, - "analytics_configuration": { Type: schema.TypeList, Optional: true, @@ -188,41 +183,7 @@ func resourceAwsCognitoUserPoolClient() *schema.Resource { }, }, }, - - "ui_customization": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "css": { - Type: schema.TypeString, - Optional: true, - }, - "css_version": { - Type: schema.TypeString, - Computed: true, - }, - "image_file": { - Type: schema.TypeString, - Optional: true, - }, - "image_url": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, }, - - CustomizeDiff: customdiff.Sequence( - // A "ui_customization" cannot be removed from a Cognito User Pool Client resource; - // thus, resource recreation is triggered on configuration block removal. - customdiff.ForceNewIfChange("ui_customization", func(_ context.Context, old, new, meta interface{}) bool { - return len(old.([]interface{})) == 1 && len(new.([]interface{})) == 0 - }), - ), } } @@ -300,25 +261,6 @@ func resourceAwsCognitoUserPoolClientCreate(d *schema.ResourceData, meta interfa d.SetId(aws.StringValue(resp.UserPoolClient.ClientId)) - if v, ok := d.GetOk("ui_customization"); ok { - input, err := expandCognitoUserPoolUICustomizationInput(v.([]interface{})) - - if err != nil { - return fmt.Errorf("error setting Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) - } - - if input != nil { - input.ClientId = aws.String(d.Id()) - input.UserPoolId = aws.String(d.Get("user_pool_id").(string)) - - _, err := conn.SetUICustomization(input) - - if err != nil { - return fmt.Errorf("error setting Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) - } - } - } - return resourceAwsCognitoUserPoolClientRead(d, meta) } @@ -364,28 +306,6 @@ func resourceAwsCognitoUserPoolClientRead(d *schema.ResourceData, meta interface return fmt.Errorf("error setting analytics_configuration: %s", err) } - // Retrieve UICustomization of the User Pool the Client belongs to; - // expect to receive an InvalidParameterException if there is no associated Domain - uiCustomization, err := finder.CognitoUserPoolUICustomization(conn, resp.UserPoolClient.UserPoolId, aws.String(d.Id())) - - if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { - log.Printf("[WARN] Cognito User Pool Client (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if tfawserr.ErrMessageContains(err, cognitoidentityprovider.ErrCodeInvalidParameterException, "There has to be an existing domain associated with this user pool") { - return nil - } - - if err != nil { - return fmt.Errorf("error getting Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) - } - - if err := d.Set("ui_customization", flattenCognitoUserPoolUICustomization(d, uiCustomization)); err != nil { - return fmt.Errorf("error setting ui_customization: %w", err) - } - return nil } @@ -460,27 +380,6 @@ func resourceAwsCognitoUserPoolClientUpdate(d *schema.ResourceData, meta interfa return fmt.Errorf("Error updating Cognito User Pool Client: %s", err) } - if d.HasChange("ui_customization") { - if v, ok := d.GetOk("ui_customization"); ok { - input, err := expandCognitoUserPoolUICustomizationInput(v.([]interface{})) - - if err != nil { - return fmt.Errorf("error updating Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) - } - - if input != nil { - input.ClientId = aws.String(d.Id()) - input.UserPoolId = aws.String(d.Get("user_pool_id").(string)) - - _, err := conn.SetUICustomization(input) - - if err != nil { - return fmt.Errorf("error updating Cognito User Pool Client (%s) UI customization: %w", d.Id(), err) - } - } - } - } - return resourceAwsCognitoUserPoolClientRead(d, meta) } diff --git a/aws/resource_aws_cognito_user_pool_client_test.go b/aws/resource_aws_cognito_user_pool_client_test.go index a3bd0628c1f..d1fff425c1b 100644 --- a/aws/resource_aws_cognito_user_pool_client_test.go +++ b/aws/resource_aws_cognito_user_pool_client_test.go @@ -30,7 +30,6 @@ func TestAccAWSCognitoUserPoolClient_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", clientName), resource.TestCheckResourceAttr(resourceName, "explicit_auth_flows.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "explicit_auth_flows.*", "ADMIN_NO_SRP_AUTH"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), ), }, { @@ -334,225 +333,6 @@ func TestAccAWSCognitoUserPoolClient_disappears(t *testing.T) { }) } -func TestAccAWSCognitoUserPoolClient_UICustomization_CSS(t *testing.T) { - var before, after cognitoidentityprovider.UserPoolClientType - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_cognito_user_pool_client.test" - - css := ".label-customizable {font-weight: 400;}" - cssUpdated := ".label-customizable {font-weight: 100;}" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, css), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), - ImportStateVerify: true, - }, - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, cssUpdated), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", cssUpdated), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), - ImportStateVerify: true, - }, - { - // Test removing the UICustomization settings, forcing new resource - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationRemoved(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &after), - testAccCheckAWSCognitoUserPoolClientRecreated(&before, &after), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), - ), - }, - }, - }) -} - -func TestAccAWSCognitoUserPoolClient_UICustomization_ImageFile(t *testing.T) { - var before, after cognitoidentityprovider.UserPoolClientType - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_cognito_user_pool_client.test" - - filename := "testdata/service/cognitoidentityprovider/logo.png" - updatedFilename := "testdata/service/cognitoidentityprovider/logo_modified.png" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, filename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, updatedFilename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - { - // Test removing the UICustomization settings, forcing new resource - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationRemoved(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &before), - testAccCheckAWSCognitoUserPoolClientRecreated(&before, &after), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), - ), - }, - }, - }) -} - -func TestAccAWSCognitoUserPoolClient_UICustomization_CSSandImageFile(t *testing.T) { - var client cognitoidentityprovider.UserPoolClientType - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_cognito_user_pool_client.test" - - css := ".label-customizable {font-weight: 400;}" - filename := "testdata/service/cognitoidentityprovider/logo.png" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSSAndImage(rName, css, filename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, css), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), - ), - }, - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, filename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName), - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - }, - }) -} - -func TestAccAWSCognitoUserPoolClient_UICustomization_InheritedFromUserPool(t *testing.T) { - var client cognitoidentityprovider.UserPoolClientType - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_cognito_user_pool_client.test" - poolName := "aws_cognito_user_pool.test" - - css := ".label-customizable {font-weight: 400;}" - cssUpdated := ".label-customizable {font-weight: 100;}" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolClientDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(poolName), - ), - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(poolName), - ), - }, - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationInherited(rName, css), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(poolName), - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndConfigured(rName, css), - PlanOnly: true, - }, - { - Config: testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndOverridden(rName, css, cssUpdated), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(poolName), - testAccCheckAWSCognitoUserPoolClientExists(resourceName, &client), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", cssUpdated), - ), - }, - }, - }) -} - func testAccAWSCognitoUserPoolClientImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { rs, ok := s.RootModule().Resources[resourceName] @@ -653,16 +433,6 @@ func testAccCheckAWSCognitoUserPoolClientDisappears(client *cognitoidentityprovi } } -func testAccCheckAWSCognitoUserPoolClientRecreated(before, after *cognitoidentityprovider.UserPoolClientType) resource.TestCheckFunc { - return func(s *terraform.State) error { - if aws.StringValue(before.ClientId) == aws.StringValue(after.ClientId) { - return fmt.Errorf("Cognito User Pool Client not recreated") - } - - return nil - } -} - func testAccAWSCognitoUserPoolClientConfig_basic(userPoolName, clientName string) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { @@ -835,183 +605,3 @@ resource "aws_cognito_user_pool_client" "test" { } `, clientName) } - -func testAccAWSCognitoUserPoolClientConfig_UICustomizationCSS(rName, css string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - - ui_customization { - css = %q - } - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state - user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id -} -`, rName, css) -} - -func testAccAWSCognitoUserPoolClientConfig_UICustomizationImage(rName, filename string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - - ui_customization { - image_file = filebase64(%q) - } - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state - user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id -} -`, rName, filename) -} - -func testAccAWSCognitoUserPoolClientConfig_UICustomizationCSSAndImage(rName, css, filename string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - - ui_customization { - css = %q - image_file = filebase64(%q) - } - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state - user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id -} -`, rName, css, filename) -} - -func testAccAWSCognitoUserPoolClientConfig_UICustomizationRemoved(rName string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} - -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state - user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id -} -`, rName) -} - -func testAccAWSCognitoUserPoolClientConfig_UICustomizationInherited(rName, css string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q - - ui_customization { - css = %q - } -} - -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state - user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id -} -`, rName, css) -} - -func testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndConfigured(rName, css string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q - - ui_customization { - css = %q - } -} - -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - - ui_customization { - css = aws_cognito_user_pool.test.ui_customization[0].css - } - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state - user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id -} -`, rName, css) -} - -func testAccAWSCognitoUserPoolClientConfig_UICustomizationInheritedAndOverridden(rName, css, cssUpdated string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool" "test" { - name = %[1]q - - ui_customization { - css = %[2]q - } -} - -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - - ui_customization { - css = %[3]q - } - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state - user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id -} -`, rName, css, cssUpdated) -} diff --git a/aws/resource_aws_cognito_user_pool_test.go b/aws/resource_aws_cognito_user_pool_test.go index d636cac2598..4f4b941ff8b 100644 --- a/aws/resource_aws_cognito_user_pool_test.go +++ b/aws/resource_aws_cognito_user_pool_test.go @@ -89,7 +89,6 @@ func TestAccAWSCognitoUserPool_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "sms_configuration.#", "0"), resource.TestCheckResourceAttr(resourceName, "software_token_mfa_configuration.#", "0"), resource.TestCheckResourceAttr(resourceName, "account_recovery_setting.#", "0"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), ), }, { @@ -1058,182 +1057,6 @@ func TestAccAWSCognitoUserPool_withSchemaAttributes(t *testing.T) { }) } -func TestAccAWSCognitoUserPool_withUICustomization_CSS(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_cognito_user_pool.test" - - css := ".label-customizable {font-weight: 400;}" - cssUpdated := ".label-customizable {font-weight: 100;}" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), - ), - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, cssUpdated), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", cssUpdated), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - { - // Test removing the UICustomization settings, forcing new resource - Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), - ), - }, - }, - }) -} - -func TestAccAWSCognitoUserPool_withUICustomization_ImageFile(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_cognito_user_pool.test" - - filename := "testdata/service/cognitoidentityprovider/logo.png" - updatedFilename := "testdata/service/cognitoidentityprovider/logo_modified.png" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), - ), - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, filename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, updatedFilename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - { - // Test removing the UICustomization settings, forcing new resource - Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), - ), - }, - }, - }) -} - -func TestAccAWSCognitoUserPool_withUICustomization_CSSandImageFile(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_cognito_user_pool.test" - - css := ".label-customizable {font-weight: 400;}" - filename := "testdata/service/cognitoidentityprovider/logo.png" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSCognitoUserPoolDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "0"), - ), - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSSAndImage(rName, css, filename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttr(resourceName, "ui_customization.0.css", css), - ), - }, - { - Config: testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, filename), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckAWSCognitoUserPoolExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "ui_customization.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "ui_customization.0.image_url"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"ui_customization.0.image_file"}, - }, - }, - }) -} - func TestAccAWSCognitoUserPool_withVerificationMessageTemplate(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_cognito_user_pool.test" @@ -2194,68 +2017,3 @@ resource "aws_cognito_user_pool" "test" { } `, name, mfaconfig, smsAuthMsg) } - -func testAccAWSCognitoUserPoolConfig_withUserPoolDomain(rName string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool" "test" { - name = %[1]q -} -`, rName) -} - -func testAccAWSCognitoUserPoolConfig_withUICustomizationCSS(rName, css string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool" "test" { - name = %[1]q - - ui_customization { - css = %q - } -} -`, rName, css) -} - -func testAccAWSCognitoUserPoolConfig_withUICustomizationImage(rName, filename string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool" "test" { - name = %[1]q - - ui_customization { - image_file = filebase64(%q) - } -} -`, rName, filename) -} - -func testAccAWSCognitoUserPoolConfig_withUICustomizationCSSAndImage(rName, css, filename string) string { - return fmt.Sprintf(` -resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id -} - -resource "aws_cognito_user_pool" "test" { - name = %[1]q - - ui_customization { - css = %q - image_file = filebase64(%q) - } -} -`, rName, css, filename) -} diff --git a/aws/resource_aws_cognito_user_pool_ui_customization.go b/aws/resource_aws_cognito_user_pool_ui_customization.go new file mode 100644 index 00000000000..e536d5e3950 --- /dev/null +++ b/aws/resource_aws_cognito_user_pool_ui_customization.go @@ -0,0 +1,223 @@ +package aws + +import ( + "encoding/base64" + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cognitoidentityprovider/finder" +) + +func resourceAwsCognitoUserPoolUICustomization() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCognitoUserPoolUICustomizationPut, + Read: resourceAwsCognitoUserPoolUICustomizationRead, + Update: resourceAwsCognitoUserPoolUICustomizationPut, + Delete: resourceAwsCognitoUserPoolUICustomizationDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "client_id": { + Type: schema.TypeString, + Optional: true, + Default: "ALL", + }, + + "creation_date": { + Type: schema.TypeString, + Computed: true, + }, + + "css": { + Type: schema.TypeString, + Optional: true, + AtLeastOneOf: []string{"css", "image_file"}, + }, + + "css_version": { + Type: schema.TypeString, + Computed: true, + }, + + "image_file": { + Type: schema.TypeString, + Optional: true, + AtLeastOneOf: []string{"image_file", "css"}, + }, + + "image_url": { + Type: schema.TypeString, + Computed: true, + }, + + "last_modified_date": { + Type: schema.TypeString, + Computed: true, + }, + + "user_pool_id": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsCognitoUserPoolUICustomizationPut(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cognitoidpconn + + clientId := d.Get("client_id").(string) + userPoolId := d.Get("user_pool_id").(string) + + input := &cognitoidentityprovider.SetUICustomizationInput{ + ClientId: aws.String(clientId), + UserPoolId: aws.String(userPoolId), + } + + if v, ok := d.GetOk("css"); ok { + input.CSS = aws.String(v.(string)) + } + + if v, ok := d.GetOk("image_file"); ok { + imgFile, err := base64.StdEncoding.DecodeString(v.(string)) + if err != nil { + return fmt.Errorf("error Base64 decoding image file for Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): %w", userPoolId, clientId, err) + } + + input.ImageFile = imgFile + } + + _, err := conn.SetUICustomization(input) + + if err != nil { + return fmt.Errorf("error setting Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): %w", userPoolId, clientId, err) + } + + d.SetId(fmt.Sprintf("%s,%s", userPoolId, clientId)) + + return resourceAwsCognitoUserPoolUICustomizationRead(d, meta) +} + +func resourceAwsCognitoUserPoolUICustomizationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cognitoidpconn + + userPoolId, clientId, err := parseCognitoUserPoolUICustomizationID(d.Id()) + + if err != nil { + return fmt.Errorf("error parsing Cognito User Pool UI customization ID (%s): %w", d.Id(), err) + } + + uiCustomization, err := finder.CognitoUserPoolUICustomization(conn, userPoolId, clientId) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s) not found, removing from state", userPoolId, clientId) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): %w", userPoolId, clientId, err) + } + + if uiCustomization == nil { + if d.IsNewResource() { + return fmt.Errorf("error getting Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): not found", userPoolId, clientId) + } + + log.Printf("[WARN] Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s) not found, removing from state", userPoolId, clientId) + d.SetId("") + return nil + } + + d.Set("client_id", uiCustomization.ClientId) + d.Set("creation_date", aws.TimeValue(uiCustomization.CreationDate).Format(time.RFC3339)) + d.Set("css", uiCustomization.CSS) + d.Set("css_version", uiCustomization.CSSVersion) + d.Set("image_url", uiCustomization.ImageUrl) + d.Set("last_modified_date", aws.TimeValue(uiCustomization.LastModifiedDate).Format(time.RFC3339)) + d.Set("user_pool_id", uiCustomization.UserPoolId) + + return nil +} + +func resourceAwsCognitoUserPoolUICustomizationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cognitoidpconn + + userPoolId, clientId, err := parseCognitoUserPoolUICustomizationID(d.Id()) + + if err != nil { + return fmt.Errorf("error parsing Cognito User Pool UI customization ID (%s): %w", d.Id(), err) + } + + input := &cognitoidentityprovider.SetUICustomizationInput{ + ClientId: aws.String(clientId), + UserPoolId: aws.String(userPoolId), + } + + output, err := conn.SetUICustomization(input) + + if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): %w", userPoolId, clientId, err) + } + + if output == nil || output.UICustomization == nil { + return nil + } + + if cognitoUserPoolUICustomizationExists(output.UICustomization) { + return fmt.Errorf("error deleting Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): still exists", userPoolId, clientId) + } + + return nil +} + +// cognitoUserPoolUICustomizationExists validates the API object such that +// we define resource existence when the object is non-nil and +// at least one of the object's fields are non-nil with the exception of CSSVersion +// which remains as an artifact even after UI customization removal +func cognitoUserPoolUICustomizationExists(ui *cognitoidentityprovider.UICustomizationType) bool { + if ui == nil { + return false + } + + if ui.CSS != nil { + return true + } + + if ui.CreationDate != nil { + return true + } + + if ui.ImageUrl != nil { + return true + } + + if ui.LastModifiedDate != nil { + return true + } + + return false +} + +func parseCognitoUserPoolUICustomizationID(id string) (string, string, error) { + idParts := strings.SplitN(id, ",", 2) + + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + return "", "", fmt.Errorf("please make sure ID is in format USER_POOL_ID,CLIENT_ID") + } + + return idParts[0], idParts[1], nil +} diff --git a/aws/resource_aws_cognito_user_pool_ui_customization_test.go b/aws/resource_aws_cognito_user_pool_ui_customization_test.go new file mode 100644 index 00000000000..de5b7ec41d6 --- /dev/null +++ b/aws/resource_aws_cognito_user_pool_ui_customization_test.go @@ -0,0 +1,706 @@ +package aws + +import ( + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cognitoidentityprovider/finder" +) + +func TestAccAWSCognitoUserPoolUICustomization_AllClients_CSS(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + userPoolResourceName := "aws_cognito_user_pool.test" + + css := ".label-customizable {font-weight: 400;}" + cssUpdated := ".label-customizable {font-weight: 100;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", css), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "css_version"), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSS(rName, cssUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", cssUpdated), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "css_version"), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_AllClients_Disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + + css := ".label-customizable {font-weight: 400;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSCognitoIdentityProvider(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsCognitoUserPoolUICustomization(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_AllClients_ImageFile(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + userPoolResourceName := "aws_cognito_user_pool.test" + + filename := "testdata/service/cognitoidentityprovider/logo.png" + updatedFilename := "testdata/service/cognitoidentityprovider/logo_modified.png" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_Image(rName, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + resource.TestCheckResourceAttrSet(resourceName, "image_url"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"image_file"}, + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_Image(rName, updatedFilename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + resource.TestCheckResourceAttrSet(resourceName, "image_url"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"image_file"}, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_AllClients_CSSAndImageFile(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + userPoolResourceName := "aws_cognito_user_pool.test" + + css := ".label-customizable {font-weight: 400;}" + filename := "testdata/service/cognitoidentityprovider/logo.png" + updatedFilename := "testdata/service/cognitoidentityprovider/logo_modified.png" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSSAndImage(rName, css, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + resource.TestCheckResourceAttr(resourceName, "css", css), + resource.TestCheckResourceAttrSet(resourceName, "css_version"), + resource.TestCheckResourceAttrSet(resourceName, "image_url"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"image_file"}, + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", css), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "css_version"), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_Image(rName, updatedFilename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + resource.TestCheckResourceAttrSet(resourceName, "image_url"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"image_file"}, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_Client_CSS(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + clientResourceName := "aws_cognito_user_pool_client.test" + userPoolResourceName := "aws_cognito_user_pool.test" + + css := ".label-customizable {font-weight: 400;}" + cssUpdated := ".label-customizable {font-weight: 100;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_Client_CSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", css), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "css_version"), + resource.TestCheckResourceAttrPair(resourceName, "client_id", clientResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_Client_CSS(rName, cssUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", cssUpdated), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrSet(resourceName, "css_version"), + resource.TestCheckResourceAttrPair(resourceName, "client_id", clientResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_Client_Disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + + css := ".label-customizable {font-weight: 400;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSCognitoIdentityProvider(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_Client_CSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsCognitoUserPoolUICustomization(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_Client_Image(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + clientResourceName := "aws_cognito_user_pool_client.test" + userPoolResourceName := "aws_cognito_user_pool.test" + + filename := "testdata/service/cognitoidentityprovider/logo.png" + updatedFilename := "testdata/service/cognitoidentityprovider/logo_modified.png" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_Client_Image(rName, filename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrPair(resourceName, "client_id", clientResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "image_url"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"image_file"}, + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_Client_Image(rName, updatedFilename), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttrPair(resourceName, "client_id", clientResourceName, "id"), + resource.TestCheckResourceAttrSet(resourceName, "image_url"), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"image_file"}, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_ClientAndAll_CSS(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.ui_all" + clientUIResourceName := "aws_cognito_user_pool_ui_customization.ui_client" + + clientResourceName := "aws_cognito_user_pool_client.test" + userPoolResourceName := "aws_cognito_user_pool.test" + + allCSS := ".label-customizable {font-weight: 400;}" + clientCSS := ".label-customizable {font-weight: 100;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + // Test UI Customization settings shared by ALL and a specific client + Config: testAccAWSCognitoUserPoolUICustomizationConfig_ClientAndAllCustomizations_CSS(rName, allCSS, allCSS), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + testAccCheckAWSCognitoUserPoolUICustomizationExists(clientUIResourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttr(resourceName, "css", allCSS), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + resource.TestCheckResourceAttrPair(clientUIResourceName, "client_id", clientResourceName, "id"), + resource.TestCheckResourceAttrSet(clientUIResourceName, "creation_date"), + resource.TestCheckResourceAttr(clientUIResourceName, "css", allCSS), + resource.TestCheckResourceAttrSet(clientUIResourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(clientUIResourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: clientUIResourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test UI Customization settings overridden for the client + Config: testAccAWSCognitoUserPoolUICustomizationConfig_ClientAndAllCustomizations_CSS(rName, allCSS, clientCSS), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + testAccCheckAWSCognitoUserPoolUICustomizationExists(clientUIResourceName), + resource.TestCheckResourceAttrSet(resourceName, "creation_date"), + resource.TestCheckResourceAttr(resourceName, "css", allCSS), + resource.TestCheckResourceAttrSet(resourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(resourceName, "user_pool_id", userPoolResourceName, "id"), + resource.TestCheckResourceAttrPair(clientUIResourceName, "client_id", clientResourceName, "id"), + resource.TestCheckResourceAttrSet(clientUIResourceName, "creation_date"), + resource.TestCheckResourceAttr(clientUIResourceName, "css", clientCSS), + resource.TestCheckResourceAttrSet(clientUIResourceName, "last_modified_date"), + resource.TestCheckResourceAttrPair(clientUIResourceName, "user_pool_id", userPoolResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: clientUIResourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_UpdateClientToAll_CSS(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + clientResourceName := "aws_cognito_user_pool_client.test" + + css := ".label-customizable {font-weight: 100;}" + cssUpdated := ".label-customizable {font-weight: 400;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_Client_CSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", css), + resource.TestCheckResourceAttrPair(resourceName, "client_id", clientResourceName, "id"), + ), + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSS(rName, cssUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", cssUpdated), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCognitoUserPoolUICustomization_UpdateAllToClient_CSS(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_cognito_user_pool_ui_customization.test" + clientResourceName := "aws_cognito_user_pool_client.test" + + css := ".label-customizable {font-weight: 100;}" + cssUpdated := ".label-customizable {font-weight: 400;}" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCognitoUserPoolUICustomizationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSS(rName, css), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", css), + resource.TestCheckResourceAttr(resourceName, "client_id", "ALL"), + ), + }, + { + Config: testAccAWSCognitoUserPoolUICustomizationConfig_Client_CSS(rName, cssUpdated), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSCognitoUserPoolUICustomizationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "css", cssUpdated), + resource.TestCheckResourceAttrPair(resourceName, "client_id", clientResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAWSCognitoUserPoolUICustomizationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).cognitoidpconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_cognito_user_pool_ui_customization" { + continue + } + + userPoolId, clientId, err := parseCognitoUserPoolUICustomizationID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error parsing Cognito User Pool UI customization ID (%s): %w", rs.Primary.ID, err) + } + + output, err := finder.CognitoUserPoolUICustomization(conn, userPoolId, clientId) + + if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + continue + } + + // Catch cases where the User Pool Domain has been destroyed, effectively eliminating + // a UI customization; calls to GetUICustomization will fail + if tfawserr.ErrMessageContains(err, cognitoidentityprovider.ErrCodeInvalidParameterException, "There has to be an existing domain associated with this user pool") { + continue + } + + if err != nil { + return err + } + + if cognitoUserPoolUICustomizationExists(output) { + return fmt.Errorf("Cognito User Pool UI Customization (UserPoolId: %s, ClientId: %s) still exists", userPoolId, clientId) + } + } + + return nil +} + +func testAccCheckAWSCognitoUserPoolUICustomizationExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return errors.New("No Cognito User Pool Client ID set") + } + + userPoolId, clientId, err := parseCognitoUserPoolUICustomizationID(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error parsing Cognito User Pool UI customization ID (%s): %w", rs.Primary.ID, err) + } + + conn := testAccProvider.Meta().(*AWSClient).cognitoidpconn + + output, err := finder.CognitoUserPoolUICustomization(conn, userPoolId, clientId) + + if err != nil { + return err + } + + if output == nil { + return fmt.Errorf("Cognito User Pool UI customization (%s) not found", rs.Primary.ID) + } + + return nil + } +} + +func testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSS(rName, css string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_ui_customization" "test" { + css = %q + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, css) +} + +func testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_Image(rName, filename string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_ui_customization" "test" { + image_file = filebase64(%q) + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, filename) +} + +func testAccAWSCognitoUserPoolUICustomizationConfig_AllClients_CSSAndImage(rName, css, filename string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_ui_customization" "test" { + css = %q + image_file = filebase64(%q) + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, css, filename) +} + +func testAccAWSCognitoUserPoolUICustomizationConfig_Client_CSS(rName, css string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_ui_customization" "test" { + client_id = aws_cognito_user_pool_client.test.id + css = %q + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, css) +} + +func testAccAWSCognitoUserPoolUICustomizationConfig_Client_Image(rName, filename string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_ui_customization" "test" { + client_id = aws_cognito_user_pool_client.test.id + image_file = filebase64(%q) + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, filename) +} + +func testAccAWSCognitoUserPoolUICustomizationConfig_ClientAndAllCustomizations_CSS(rName, allCSS, clientCSS string) string { + return fmt.Sprintf(` +resource "aws_cognito_user_pool" "test" { + name = %[1]q +} + +resource "aws_cognito_user_pool_domain" "test" { + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_client" "test" { + name = %[1]q + user_pool_id = aws_cognito_user_pool.test.id +} + +resource "aws_cognito_user_pool_ui_customization" "ui_all" { + css = %q + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} + +resource "aws_cognito_user_pool_ui_customization" "ui_client" { + client_id = aws_cognito_user_pool_client.test.id + css = %q + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id +} +`, rName, allCSS, clientCSS) +} diff --git a/website/docs/r/cognito_user_pool.markdown b/website/docs/r/cognito_user_pool.markdown index 08bb7a32094..db80695b8ec 100644 --- a/website/docs/r/cognito_user_pool.markdown +++ b/website/docs/r/cognito_user_pool.markdown @@ -43,7 +43,7 @@ resource "aws_cognito_user_pool" "example" { ### Using Account Recovery Setting ```hcl -resource "aws_cognito_user_pool" "example" { +resource "aws_cognito_user_pool" "test" { name = "mypool" account_recovery_setting { @@ -60,25 +60,6 @@ resource "aws_cognito_user_pool" "example" { } ``` -### Specifying UI Customization Settings for all clients (only applicable on resource updates) - -```hcl -resource "aws_cognito_user_pool" "example" { - name = "mypool" - - # ... other configuration ... - ui_customization { - css = ".label-customizable {font-weight: 400;}" - image_file = filebase64("logo.png") - } -} - -resource "aws_cognito_user_pool_domain" "example" { - domain = "example" - user_pool_id = aws_cognito_user_pool.example.id -} -``` - ## Argument Reference The following arguments are supported: @@ -108,7 +89,6 @@ The following arguments are supported: * `user_pool_add_ons` - (Optional) Configuration block for [user pool add-ons](#user-pool-add-ons) to enable user pool advanced security mode features. * `verification_message_template` (Optional) - The [verification message templates](#verification-message-template) configuration. * `account_recovery_setting` (Optional) - The [account_recovery_setting](#account-recovery-setting) configuration. -* `ui_customization` (Optional) - Configuration block for [UI customization](#ui-customization) information for a user pool's built-in app UI. To use this feature, the user pool must have a domain associated with it. Once configured, the UI customization will be used for every client that has no UI customization set previously. Removing this configuration forces new resource. #### Admin Create User Config @@ -218,11 +198,6 @@ The following arguments are required in the `software_token_mfa_configuration` c * `advanced_security_mode` (Required) - The mode for advanced security, must be one of `OFF`, `AUDIT` or `ENFORCED`. -#### UI Customization - -* `css` (Optional) - The CSS values in the UI customization, provided as a String. -* `image_file` (Optional) - The uploaded logo image in the UI customization, provided as a base64-encoded String. Drift detection is not possible for this argument. - #### Verification Message Template * `default_email_option` (Optional) - The default email option. Must be either `CONFIRM_WITH_CODE` or `CONFIRM_WITH_LINK`. Defaults to `CONFIRM_WITH_CODE`. @@ -247,10 +222,7 @@ In addition to all arguments above, the following attributes are exported: * `endpoint` - The endpoint name of the user pool. Example format: cognito-idp.REGION.amazonaws.com/xxxx_yyyyy * `creation_date` - The date the user pool was created. * `last_modified_date` - The date the user pool was last modified. -* `ui_customization` - The UI customization information for a user pool's built-in app UI. - * `css_version` - The CSS version number. - * `image_url` - The logo image URL for the UI customization. - + ## Import Cognito User Pools can be imported using the `id`, e.g. diff --git a/website/docs/r/cognito_user_pool_client.markdown b/website/docs/r/cognito_user_pool_client.markdown index e8ef33f516a..5bf5f3347c9 100644 --- a/website/docs/r/cognito_user_pool_client.markdown +++ b/website/docs/r/cognito_user_pool_client.markdown @@ -110,32 +110,6 @@ resource "aws_cognito_user_pool_client" "test" { } ``` -### Create a user pool client with UI customization settings - -```hcl -resource "aws_cognito_user_pool" "pool" { - name = "pool" -} - -resource "aws_cognito_user_pool_domain" "domain" { - domain = "example" - user_pool_id = aws_cognito_user_pool.pool.id -} - -resource "aws_cognito_user_pool_client" "client" { - name = "client" - - ui_customization { - css = ".label-customizable {font-weight: 400;}" - image_file = filebase64("logo.png") - } - - # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure the domain is 'Active' - user_pool_id = aws_cognito_user_pool_domain.domain.user_pool_id -} -``` - ## Argument Reference The following arguments are supported: @@ -156,7 +130,6 @@ The following arguments are supported: * `user_pool_id` - (Required) The user pool the client belongs to. * `write_attributes` - (Optional) List of user pool attributes the application client can write to. * `analytics_configuration` - (Optional) The Amazon Pinpoint analytics configuration for collecting metrics for this user pool. -* `ui_customization` (Optional) - Configuration block for [UI customization](#ui-customization) information for the user pool's built-in app UI. To use this feature, the user pool the client belongs to must have a domain associated with it. If UI customization settings are configured within the [`aws_cognito_user pool` resource](cognito_user_pool.markdown), this must be configured to prevent plan-time differences. Removing this configuration forces new resource. ### Analytics Configuration @@ -168,21 +141,13 @@ Either `application_arn` or `application_id` is required. * `role_arn` - (Optional) The ARN of an IAM role that authorizes Amazon Cognito to publish events to Amazon Pinpoint analytics. Conflicts with `application_arn`. * `user_data_shared` (Optional) If set to `true`, Amazon Cognito will include user data in the events it publishes to Amazon Pinpoint analytics. -#### UI Customization - -* `css` (Optional) - The CSS values in the UI customization, provided as a String. -* `image_file` (Optional) - The uploaded logo image in the UI customization, provided as a base64-encoded String. Drift detection is not possible for this argument. - ## Attributes Reference In addition to all arguments above, the following attributes are exported: * `id` - The id of the user pool client. * `client_secret` - The client secret of the user pool client. -* `ui_customization` - The UI customization information for the user pool's built-in app UI. - * `css_version` - The CSS version number. - * `image_url` - The logo image URL for the UI customization. - + ## Import Cognito User Pool Clients can be imported using the `id` of the Cognito User Pool, and the `id` of the Cognito User Pool Client, e.g. diff --git a/website/docs/r/cognito_user_pool_ui_customization.html.markdown b/website/docs/r/cognito_user_pool_ui_customization.html.markdown new file mode 100644 index 00000000000..cca7ca7da7d --- /dev/null +++ b/website/docs/r/cognito_user_pool_ui_customization.html.markdown @@ -0,0 +1,92 @@ +--- +subcategory: "Cognito" +layout: "aws" +page_title: "AWS: aws_cognito_user_pool_ui_customization" +description: |- + Provides a Cognito User Pool UI Customization resource. +--- + +# Resource: aws_cognito_user_pool_ui_customization + +Provides a Cognito User Pool UI Customization resource. + +~> **Note:** To use this resource, the user pool must have a domain associated with it. For more information, see the Amazon Cognito Developer Guide on [Customizing the Built-in Sign-In and Sign-up Webpages](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-ui-customization.html). + +### Example Usage + +### UI customization settings for a single client + +```hcl +resource "aws_cognito_user_pool" "example" { + name = "example" +} + +resource "aws_cognito_user_pool_domain" "example" { + domain = "example" + user_pool_id = aws_cognito_user_pool.example.id +} + +resource "aws_cognito_user_pool_client" "example" { + name = "example" + user_pool_id = aws_cognito_user_pool.example.id +} + +resource "aws_cognito_user_pool_ui_customization" "example" { + client_id = aws_cognito_user_pool_client.example.id + + css = ".label-customizable {font-weight: 400;}" + image_file = filebase64("logo.png") + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.example.user_pool_id +} +``` + +### UI customization settings for all clients + +```hcl +resource "aws_cognito_user_pool" "example" { + name = "example" +} + +resource "aws_cognito_user_pool_domain" "example" { + domain = "example" + user_pool_id = aws_cognito_user_pool.example.id +} + +resource "aws_cognito_user_pool_ui_customization" "example" { + css = ".label-customizable {font-weight: 400;}" + image_file = filebase64("logo.png") + + # Refer to the aws_cognito_user_pool_domain resource's + # user_pool_id attribute to ensure it is in an 'Active' state + user_pool_id = aws_cognito_user_pool_domain.example.user_pool_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `client_id` (Optional) The client ID for the client app. Defaults to `ALL`. If `ALL` is specified, the `css` and/or `image_file` settings will be used for every client that has no UI customization set previously. +* `css` (Optional) - The CSS values in the UI customization, provided as a String. At least one of `css` or `image_file` is required. +* `image_file` (Optional) - The uploaded logo image for the UI customization, provided as a base64-encoded String. Drift detection is not possible for this argument. At least one of `css` or `image_file` is required. +* `user_pool_id` (Required) - The user pool ID for the user pool. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `creation_date` - The creation date in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) for the UI customization. +* `css_version` - The CSS version number. +* `image_url` - The logo image URL for the UI customization. +* `last_modified_date` - The last-modified date in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) for the UI customization. + +## Import + +Cognito User Pool UI Customizations can be imported using the `user_pool_id` and `client_id` separated by `,`, e.g. + +``` +$ terraform import aws_cognito_user_pool_ui_customization.example us-west-2_ZCTarbt5C,12bu4fuk3mlgqa2rtrujgp6egq +``` From c18646dd4495988b61b1ce02f5bb8c9b78beafab Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Wed, 24 Feb 2021 22:56:24 -0500 Subject: [PATCH 11/11] linting, update method used in testcheckdestroy --- ..._aws_cognito_user_pool_ui_customization.go | 38 +--------- ...cognito_user_pool_ui_customization_test.go | 74 +++++++++++++------ 2 files changed, 52 insertions(+), 60 deletions(-) diff --git a/aws/resource_aws_cognito_user_pool_ui_customization.go b/aws/resource_aws_cognito_user_pool_ui_customization.go index e536d5e3950..6d80f256875 100644 --- a/aws/resource_aws_cognito_user_pool_ui_customization.go +++ b/aws/resource_aws_cognito_user_pool_ui_customization.go @@ -163,7 +163,7 @@ func resourceAwsCognitoUserPoolUICustomizationDelete(d *schema.ResourceData, met UserPoolId: aws.String(userPoolId), } - output, err := conn.SetUICustomization(input) + _, err = conn.SetUICustomization(input) if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeResourceNotFoundException) { return nil @@ -173,45 +173,9 @@ func resourceAwsCognitoUserPoolUICustomizationDelete(d *schema.ResourceData, met return fmt.Errorf("error deleting Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): %w", userPoolId, clientId, err) } - if output == nil || output.UICustomization == nil { - return nil - } - - if cognitoUserPoolUICustomizationExists(output.UICustomization) { - return fmt.Errorf("error deleting Cognito User Pool UI customization (UserPoolId: %s, ClientId: %s): still exists", userPoolId, clientId) - } - return nil } -// cognitoUserPoolUICustomizationExists validates the API object such that -// we define resource existence when the object is non-nil and -// at least one of the object's fields are non-nil with the exception of CSSVersion -// which remains as an artifact even after UI customization removal -func cognitoUserPoolUICustomizationExists(ui *cognitoidentityprovider.UICustomizationType) bool { - if ui == nil { - return false - } - - if ui.CSS != nil { - return true - } - - if ui.CreationDate != nil { - return true - } - - if ui.ImageUrl != nil { - return true - } - - if ui.LastModifiedDate != nil { - return true - } - - return false -} - func parseCognitoUserPoolUICustomizationID(id string) (string, string, error) { idParts := strings.SplitN(id, ",", 2) diff --git a/aws/resource_aws_cognito_user_pool_ui_customization_test.go b/aws/resource_aws_cognito_user_pool_ui_customization_test.go index de5b7ec41d6..03c5611633a 100644 --- a/aws/resource_aws_cognito_user_pool_ui_customization_test.go +++ b/aws/resource_aws_cognito_user_pool_ui_customization_test.go @@ -511,7 +511,7 @@ func testAccCheckAWSCognitoUserPoolUICustomizationDestroy(s *terraform.State) er return err } - if cognitoUserPoolUICustomizationExists(output) { + if testAccAWSCognitoUserPoolUICustomizationExists(output) { return fmt.Errorf("Cognito User Pool UI Customization (UserPoolId: %s, ClientId: %s) still exists", userPoolId, clientId) } } @@ -619,21 +619,21 @@ resource "aws_cognito_user_pool_ui_customization" "test" { func testAccAWSCognitoUserPoolUICustomizationConfig_Client_CSS(rName, css string) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { - name = %[1]q + name = %[1]q } resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id } resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - user_pool_id = aws_cognito_user_pool.test.id + name = %[1]q + user_pool_id = aws_cognito_user_pool.test.id } resource "aws_cognito_user_pool_ui_customization" "test" { - client_id = aws_cognito_user_pool_client.test.id + client_id = aws_cognito_user_pool_client.test.id css = %q # Refer to the aws_cognito_user_pool_domain resource's @@ -646,25 +646,25 @@ resource "aws_cognito_user_pool_ui_customization" "test" { func testAccAWSCognitoUserPoolUICustomizationConfig_Client_Image(rName, filename string) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { - name = %[1]q + name = %[1]q } resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id } resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - user_pool_id = aws_cognito_user_pool.test.id + name = %[1]q + user_pool_id = aws_cognito_user_pool.test.id } resource "aws_cognito_user_pool_ui_customization" "test" { - client_id = aws_cognito_user_pool_client.test.id + client_id = aws_cognito_user_pool_client.test.id image_file = filebase64(%q) # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state + # user_pool_id attribute to ensure it is in an 'Active' state user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id } `, rName, filename) @@ -673,34 +673,62 @@ resource "aws_cognito_user_pool_ui_customization" "test" { func testAccAWSCognitoUserPoolUICustomizationConfig_ClientAndAllCustomizations_CSS(rName, allCSS, clientCSS string) string { return fmt.Sprintf(` resource "aws_cognito_user_pool" "test" { - name = %[1]q + name = %[1]q } resource "aws_cognito_user_pool_domain" "test" { - domain = %[1]q - user_pool_id = aws_cognito_user_pool.test.id + domain = %[1]q + user_pool_id = aws_cognito_user_pool.test.id } resource "aws_cognito_user_pool_client" "test" { - name = %[1]q - user_pool_id = aws_cognito_user_pool.test.id + name = %[1]q + user_pool_id = aws_cognito_user_pool.test.id } resource "aws_cognito_user_pool_ui_customization" "ui_all" { css = %q # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state + # user_pool_id attribute to ensure it is in an 'Active' state user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id } resource "aws_cognito_user_pool_ui_customization" "ui_client" { - client_id = aws_cognito_user_pool_client.test.id - css = %q + client_id = aws_cognito_user_pool_client.test.id + css = %q # Refer to the aws_cognito_user_pool_domain resource's - # user_pool_id attribute to ensure it is in an 'Active' state + # user_pool_id attribute to ensure it is in an 'Active' state user_pool_id = aws_cognito_user_pool_domain.test.user_pool_id } `, rName, allCSS, clientCSS) } + +// testAccAWSCognitoUserPoolUICustomizationExists validates the API object such that +// we define resource existence when the object is non-nil and +// at least one of the object's fields are non-nil with the exception of CSSVersion +// which remains as an artifact even after UI customization removal +func testAccAWSCognitoUserPoolUICustomizationExists(ui *cognitoidentityprovider.UICustomizationType) bool { + if ui == nil { + return false + } + + if ui.CSS != nil { + return true + } + + if ui.CreationDate != nil { + return true + } + + if ui.ImageUrl != nil { + return true + } + + if ui.LastModifiedDate != nil { + return true + } + + return false +}