From ed9044585dcf249ebf3586c054bb7cf263055bed Mon Sep 17 00:00:00 2001 From: Keisuke Nishida Date: Fri, 7 Feb 2020 01:25:47 +0900 Subject: [PATCH 1/9] Add aws_amplify_domain_association resource --- aws/provider.go | 1 + ...resource_aws_amplify_domain_association.go | 287 ++++++++++++++++++ ...rce_aws_amplify_domain_association_test.go | 132 ++++++++ .../amplify_domain_association.html.markdown | 77 +++++ 4 files changed, 497 insertions(+) create mode 100644 aws/resource_aws_amplify_domain_association.go create mode 100644 aws/resource_aws_amplify_domain_association_test.go create mode 100644 website/docs/r/amplify_domain_association.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 74fd8c21cb9..20a1170763a 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -456,6 +456,7 @@ func Provider() *schema.Provider { "aws_amplify_app": resourceAwsAmplifyApp(), "aws_amplify_backend_environment": resourceAwsAmplifyBackendEnvironment(), "aws_amplify_branch": resourceAwsAmplifyBranch(), + "aws_amplify_domain_association": resourceAwsAmplifyDomainAssociation(), "aws_amplify_webhook": resourceAwsAmplifyWebhook(), "aws_api_gateway_account": resourceAwsApiGatewayAccount(), "aws_api_gateway_api_key": resourceAwsApiGatewayApiKey(), diff --git a/aws/resource_aws_amplify_domain_association.go b/aws/resource_aws_amplify_domain_association.go new file mode 100644 index 00000000000..51d675f05df --- /dev/null +++ b/aws/resource_aws_amplify_domain_association.go @@ -0,0 +1,287 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/amplify" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceAwsAmplifyDomainAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsAmplifyDomainAssociationCreate, + Read: resourceAwsAmplifyDomainAssociationRead, + Update: resourceAwsAmplifyDomainAssociationUpdate, + Delete: resourceAwsAmplifyDomainAssociationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "app_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "enable_auto_sub_domain": { + Type: schema.TypeBool, + Optional: true, + }, + "sub_domain_settings": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "branch_name": { + Type: schema.TypeString, + Required: true, + }, + "prefix": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + // non-API + "wait_for_verification": { + Type: schema.TypeBool, + Optional: true, + Default: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return true + }, + }, + }, + } +} + +func resourceAwsAmplifyDomainAssociationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).amplifyconn + log.Print("[DEBUG] Creating Amplify DomainAssociation") + + params := &lify.CreateDomainAssociationInput{ + AppId: aws.String(d.Get("app_id").(string)), + DomainName: aws.String(d.Get("domain_name").(string)), + } + + if v, ok := d.GetOk("sub_domain_settings"); ok { + params.SubDomainSettings = expandAmplifySubDomainSettings(v.([]interface{})) + } + + if v, ok := d.GetOk("enable_auto_sub_domain"); ok { + params.EnableAutoSubDomain = aws.Bool(v.(bool)) + } + + resp, err := conn.CreateDomainAssociation(params) + if err != nil { + return fmt.Errorf("Error creating Amplify DomainAssociation: %s", err) + } + + arn := *resp.DomainAssociation.DomainAssociationArn + d.SetId(arn[strings.Index(arn, ":apps/")+len(":apps/"):]) + + if d.Get("wait_for_verification").(bool) { + log.Printf("[DEBUG] Waiting until Amplify DomainAssociation (%s) is deployed", d.Id()) + if err := resourceAwsAmplifyDomainAssociationWaitUntilVerified(d.Id(), meta); err != nil { + return fmt.Errorf("error waiting until Amplify DomainAssociation (%s) is deployed: %s", d.Id(), err) + } + } + + return resourceAwsAmplifyDomainAssociationRead(d, meta) +} + +func resourceAwsAmplifyDomainAssociationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).amplifyconn + log.Printf("[DEBUG] Reading Amplify DomainAssociation: %s", d.Id()) + + s := strings.Split(d.Id(), "/") + app_id := s[0] + domain_name := s[2] + + resp, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ + AppId: aws.String(app_id), + DomainName: aws.String(domain_name), + }) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == amplify.ErrCodeNotFoundException { + log.Printf("[WARN] Amplify DomainAssociation (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + d.Set("app_id", app_id) + d.Set("arn", resp.DomainAssociation.DomainAssociationArn) + d.Set("domain_name", resp.DomainAssociation.DomainName) + if err := d.Set("sub_domain_settings", flattenAmplifySubDomainSettings(resp.DomainAssociation.SubDomains)); err != nil { + return fmt.Errorf("error setting sub_domain_settings: %s", err) + } + d.Set("enable_auto_sub_domain", resp.DomainAssociation.EnableAutoSubDomain) + + return nil +} + +func resourceAwsAmplifyDomainAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).amplifyconn + log.Printf("[DEBUG] Updating Amplify DomainAssociation: %s", d.Id()) + + s := strings.Split(d.Id(), "/") + app_id := s[0] + domain_name := s[2] + + params := &lify.UpdateDomainAssociationInput{ + AppId: aws.String(app_id), + DomainName: aws.String(domain_name), + } + + if d.HasChange("sub_domain_settings") { + params.SubDomainSettings = expandAmplifySubDomainSettings(d.Get("sub_domain_settings").([]interface{})) + } + + if d.HasChange("enable_auto_sub_domain") { + params.EnableAutoSubDomain = aws.Bool(d.Get("enable_auto_sub_domain").(bool)) + } + + _, err := conn.UpdateDomainAssociation(params) + if err != nil { + return fmt.Errorf("Error updating Amplify DomainAssociation: %s", err) + } + + if d.Get("wait_for_verification").(bool) { + log.Printf("[DEBUG] Waiting until Amplify DomainAssociation (%s) is deployed", d.Id()) + if err := resourceAwsAmplifyDomainAssociationWaitUntilVerified(d.Id(), meta); err != nil { + return fmt.Errorf("error waiting until Amplify DomainAssociation (%s) is deployed: %s", d.Id(), err) + } + } + + return resourceAwsAmplifyDomainAssociationRead(d, meta) +} + +func resourceAwsAmplifyDomainAssociationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).amplifyconn + log.Printf("[DEBUG] Deleting Amplify DomainAssociation: %s", d.Id()) + + s := strings.Split(d.Id(), "/") + app_id := s[0] + domain_name := s[2] + + params := &lify.DeleteDomainAssociationInput{ + AppId: aws.String(app_id), + DomainName: aws.String(domain_name), + } + + _, err := conn.DeleteDomainAssociation(params) + if err != nil { + return fmt.Errorf("Error deleting Amplify DomainAssociation: %s", err) + } + + return nil +} + +func resourceAwsAmplifyDomainAssociationWaitUntilVerified(id string, meta interface{}) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + amplify.DomainStatusPendingVerification, + amplify.DomainStatusInProgress, + amplify.DomainStatusCreating, + amplify.DomainStatusRequestingCertificate, + amplify.DomainStatusUpdating, + }, + Target: []string{ + // It takes up to 30 minutes, so skip waiting for deployment. + amplify.DomainStatusPendingDeployment, + amplify.DomainStatusAvailable, + }, + Refresh: resourceAwsAmplifyDomainAssociationStateRefreshFunc(id, meta), + Timeout: 15 * time.Minute, + MinTimeout: 15 * time.Second, + Delay: 10 * time.Second, + } + + _, err := stateConf.WaitForState() + return err +} + +func resourceAwsAmplifyDomainAssociationStateRefreshFunc(id string, meta interface{}) resource.StateRefreshFunc { + s := strings.Split(id, "/") + app_id := s[0] + domain_name := s[2] + + return func() (interface{}, string, error) { + conn := meta.(*AWSClient).amplifyconn + + resp, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ + AppId: aws.String(app_id), + DomainName: aws.String(domain_name), + }) + if err != nil { + log.Printf("[WARN] Error retrieving Amplify DomainAssociation %q details: %s", id, err) + return nil, "", err + } + + if *resp.DomainAssociation.DomainStatus == amplify.DomainStatusFailed { + return nil, "", fmt.Errorf("%s", *resp.DomainAssociation.StatusReason) + } + + return resp.DomainAssociation, *resp.DomainAssociation.DomainStatus, nil + } +} + +func expandAmplifySubDomainSettings(values []interface{}) []*amplify.SubDomainSetting { + settings := make([]*amplify.SubDomainSetting, 0) + + for _, v := range values { + e := v.(map[string]interface{}) + + setting := &lify.SubDomainSetting{} + + if ev, ok := e["branch_name"].(string); ok && ev != "" { + setting.BranchName = aws.String(ev) + } + + if ev, ok := e["prefix"].(string); ok { + setting.Prefix = aws.String(ev) + } + + settings = append(settings, setting) + } + + return settings +} + +func flattenAmplifySubDomainSettings(sub_domains []*amplify.SubDomain) []map[string]interface{} { + values := make([]map[string]interface{}, 0) + + for _, v := range sub_domains { + kv := make(map[string]interface{}) + + if v.SubDomainSetting.BranchName != nil { + kv["branch_name"] = *v.SubDomainSetting.BranchName + } + + if v.SubDomainSetting.Prefix != nil { + kv["prefix"] = *v.SubDomainSetting.Prefix + } + + values = append(values, kv) + } + + return values +} diff --git a/aws/resource_aws_amplify_domain_association_test.go b/aws/resource_aws_amplify_domain_association_test.go new file mode 100644 index 00000000000..f655bba7950 --- /dev/null +++ b/aws/resource_aws_amplify_domain_association_test.go @@ -0,0 +1,132 @@ +package aws + +import ( + "fmt" + "regexp" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/amplify" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccAWSAmplifyDomainAssociation_basic(t *testing.T) { + var domain amplify.DomainAssociation + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_amplify_domain_association.test" + + domainName := "example.com" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAmplifyDomainAssociationConfig_Required(rName, domainName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), + resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile("^arn:[^:]+:amplify:[^:]+:[^:]+:apps/[^/]+/domains/[^/]+$")), + resource.TestCheckResourceAttr(resourceName, "domain_name", domainName), + resource.TestCheckResourceAttr(resourceName, "sub_domain_settings.#", "1"), + resource.TestCheckResourceAttr(resourceName, "sub_domain_settings.0.branch_name", "master"), + resource.TestCheckResourceAttr(resourceName, "sub_domain_settings.0.prefix", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_verification"}, + }, + }, + }) +} + +func testAccCheckAWSAmplifyDomainAssociationExists(resourceName string, v *amplify.DomainAssociation) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).amplifyconn + + id := strings.Split(rs.Primary.ID, "/") + app_id := id[0] + domain_name := id[2] + + output, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ + AppId: aws.String(app_id), + DomainName: aws.String(domain_name), + }) + if err != nil { + return err + } + + if output == nil || output.DomainAssociation == nil { + return fmt.Errorf("Amplify DomainAssociation (%s) not found", rs.Primary.ID) + } + + *v = *output.DomainAssociation + + return nil + } +} + +func testAccCheckAWSAmplifyDomainAssociationDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_amplify_domain_association" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).amplifyconn + + s := strings.Split(rs.Primary.ID, "/") + app_id := s[0] + domain_name := s[2] + + _, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ + AppId: aws.String(app_id), + DomainName: aws.String(domain_name), + }) + + if isAWSErr(err, amplify.ErrCodeNotFoundException, "") { + continue + } + + if err != nil { + return err + } + } + + return nil +} + +func testAccAWSAmplifyDomainAssociationConfig_Required(rName string, domainName string) string { + return fmt.Sprintf(` +resource "aws_amplify_app" "test" { + name = "%s" +} + +resource "aws_amplify_branch" "test" { + app_id = aws_amplify_app.test.id + branch_name = "master" +} + +resource "aws_amplify_domain_association" "test" { + app_id = aws_amplify_app.test.id + domain_name = "%s" + + sub_domain_settings { + branch_name = aws_amplify_branch.test.branch_name + prefix = "" + } + + wait_for_verification = false +} +`, rName, domainName) +} diff --git a/website/docs/r/amplify_domain_association.html.markdown b/website/docs/r/amplify_domain_association.html.markdown new file mode 100644 index 00000000000..67aec0bf3f1 --- /dev/null +++ b/website/docs/r/amplify_domain_association.html.markdown @@ -0,0 +1,77 @@ +--- +subcategory: "Amplify" +layout: "aws" +page_title: "AWS: aws_amplify_domain_association" +description: |- + Provides an Amplify domain association resource. +--- + +# Resource: aws_amplify_domain_association + +Provides an Amplify domain association resource. + +## Example Usage + +```hcl +resource "aws_amplify_app" "app" { + name = "app" + + // Setup redirect from https://example.com to https://www.example.com + custom_rules { + source = "https://example.com" + status = "302" + target = "https://www.example.com" + } +} + +resource "aws_amplify_branch" "master" { + app_id = aws_amplify_app.app.id + branch_name = "master" +} + +resource "aws_amplify_domain_association" "app" { + app_id = aws_amplify_app.app.id + domain_name = "example.com" + + // https://example.com + sub_domain_settings { + branch_name = aws_amplify_branch.master.branch_name + prefix = "" + } + + // https://www.example.com + sub_domain_settings { + branch_name = aws_amplify_branch.master.branch_name + prefix = "www" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `app_id` - (Required) Unique Id for an Amplify App. +* `domain_name` - (Required) Domain name for the Domain Association. +* `sub_domain_settings` - (Required) Setting structure for the Subdomain. A `sub_domain_settings` block is documented below. +* `enable_auto_sub_domain` - (Optional) Enables automated creation of Subdomains for branches. (Currently not supported) +* `wait_for_verification` - (Optional) If enabled, the resource will wait for the domain association status to change to PENDING_DEPLOYMENT or AVAILABLE. Setting this to false will skip the process. Default: true. + +A `sub_domain_settings` block supports the following arguments: + +* `branch_name` - (Required) Branch name setting for the Subdomain. +* `prefix` - (Required) Prefix setting for the Subdomain. + +## Attribute Reference + +The following attributes are exported: + +* `arn` - ARN for the Domain Association. + +## Import + +Amplify domain association can be imported using `app_id` and `domain_name`, e.g. + +``` +$ terraform import aws_amplify_domain_association.app d2ypk4k47z8u6/domains/example.com +``` From 49a5ea0fc460db86b20c8c72b18c510f55a87fb8 Mon Sep 17 00:00:00 2001 From: Keisuke Nishida Date: Sat, 15 Feb 2020 02:58:31 +0900 Subject: [PATCH 2/9] use "${}" in docs --- website/docs/r/amplify_domain_association.html.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/docs/r/amplify_domain_association.html.markdown b/website/docs/r/amplify_domain_association.html.markdown index 67aec0bf3f1..123c2f6d669 100644 --- a/website/docs/r/amplify_domain_association.html.markdown +++ b/website/docs/r/amplify_domain_association.html.markdown @@ -25,23 +25,23 @@ resource "aws_amplify_app" "app" { } resource "aws_amplify_branch" "master" { - app_id = aws_amplify_app.app.id + app_id = "${aws_amplify_app.app.id}" branch_name = "master" } resource "aws_amplify_domain_association" "app" { - app_id = aws_amplify_app.app.id + app_id = "${aws_amplify_app.app.id}" domain_name = "example.com" // https://example.com sub_domain_settings { - branch_name = aws_amplify_branch.master.branch_name + branch_name = "${aws_amplify_branch.master.branch_name}" prefix = "" } // https://www.example.com sub_domain_settings { - branch_name = aws_amplify_branch.master.branch_name + branch_name = "${aws_amplify_branch.master.branch_name}" prefix = "www" } } From c2d6be3bed3a27f9e24853d03b0825d4d78f377f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Jun 2021 09:20:00 -0400 Subject: [PATCH 3/9] r/aws_amplify_domain_association: Building with Plugin SDK v2. --- ...resource_aws_amplify_domain_association.go | 44 +++++++-------- ...rce_aws_amplify_domain_association_test.go | 14 ++--- .../amplify_domain_association.html.markdown | 53 +++++++++---------- 3 files changed, 51 insertions(+), 60 deletions(-) diff --git a/aws/resource_aws_amplify_domain_association.go b/aws/resource_aws_amplify_domain_association.go index 51d675f05df..8453aed28e6 100644 --- a/aws/resource_aws_amplify_domain_association.go +++ b/aws/resource_aws_amplify_domain_association.go @@ -9,8 +9,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/amplify" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func resourceAwsAmplifyDomainAssociation() *schema.Resource { @@ -24,27 +24,27 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, "app_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, + + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "domain_name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "enable_auto_sub_domain": { - Type: schema.TypeBool, - Optional: true, - }, - "sub_domain_settings": { - Type: schema.TypeList, + + "sub_domain_setting": { + Type: schema.TypeSet, Required: true, + MaxItems: 255, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "branch_name": { @@ -58,6 +58,7 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { }, }, }, + // non-API "wait_for_verification": { Type: schema.TypeBool, @@ -80,14 +81,10 @@ func resourceAwsAmplifyDomainAssociationCreate(d *schema.ResourceData, meta inte DomainName: aws.String(d.Get("domain_name").(string)), } - if v, ok := d.GetOk("sub_domain_settings"); ok { + if v, ok := d.GetOk("sub_domain_setting"); ok { params.SubDomainSettings = expandAmplifySubDomainSettings(v.([]interface{})) } - if v, ok := d.GetOk("enable_auto_sub_domain"); ok { - params.EnableAutoSubDomain = aws.Bool(v.(bool)) - } - resp, err := conn.CreateDomainAssociation(params) if err != nil { return fmt.Errorf("Error creating Amplify DomainAssociation: %s", err) @@ -130,10 +127,9 @@ func resourceAwsAmplifyDomainAssociationRead(d *schema.ResourceData, meta interf d.Set("app_id", app_id) d.Set("arn", resp.DomainAssociation.DomainAssociationArn) d.Set("domain_name", resp.DomainAssociation.DomainName) - if err := d.Set("sub_domain_settings", flattenAmplifySubDomainSettings(resp.DomainAssociation.SubDomains)); err != nil { - return fmt.Errorf("error setting sub_domain_settings: %s", err) + if err := d.Set("sub_domain_setting", flattenAmplifySubDomainSettings(resp.DomainAssociation.SubDomains)); err != nil { + return fmt.Errorf("error setting sub_domain_setting: %s", err) } - d.Set("enable_auto_sub_domain", resp.DomainAssociation.EnableAutoSubDomain) return nil } @@ -151,12 +147,8 @@ func resourceAwsAmplifyDomainAssociationUpdate(d *schema.ResourceData, meta inte DomainName: aws.String(domain_name), } - if d.HasChange("sub_domain_settings") { - params.SubDomainSettings = expandAmplifySubDomainSettings(d.Get("sub_domain_settings").([]interface{})) - } - - if d.HasChange("enable_auto_sub_domain") { - params.EnableAutoSubDomain = aws.Bool(d.Get("enable_auto_sub_domain").(bool)) + if d.HasChange("sub_domain_setting") { + params.SubDomainSettings = expandAmplifySubDomainSettings(d.Get("sub_domain_setting").([]interface{})) } _, err := conn.UpdateDomainAssociation(params) diff --git a/aws/resource_aws_amplify_domain_association_test.go b/aws/resource_aws_amplify_domain_association_test.go index f655bba7950..b1d1f6b43df 100644 --- a/aws/resource_aws_amplify_domain_association_test.go +++ b/aws/resource_aws_amplify_domain_association_test.go @@ -8,9 +8,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/amplify" - "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "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" ) func TestAccAWSAmplifyDomainAssociation_basic(t *testing.T) { @@ -31,9 +31,9 @@ func TestAccAWSAmplifyDomainAssociation_basic(t *testing.T) { testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile("^arn:[^:]+:amplify:[^:]+:[^:]+:apps/[^/]+/domains/[^/]+$")), resource.TestCheckResourceAttr(resourceName, "domain_name", domainName), - resource.TestCheckResourceAttr(resourceName, "sub_domain_settings.#", "1"), - resource.TestCheckResourceAttr(resourceName, "sub_domain_settings.0.branch_name", "master"), - resource.TestCheckResourceAttr(resourceName, "sub_domain_settings.0.prefix", ""), + resource.TestCheckResourceAttr(resourceName, "sub_domain_setting.#", "1"), + resource.TestCheckResourceAttr(resourceName, "sub_domain_setting.0.branch_name", "master"), + resource.TestCheckResourceAttr(resourceName, "sub_domain_setting.0.prefix", ""), ), }, { @@ -121,7 +121,7 @@ resource "aws_amplify_domain_association" "test" { app_id = aws_amplify_app.test.id domain_name = "%s" - sub_domain_settings { + sub_domain_setting { branch_name = aws_amplify_branch.test.branch_name prefix = "" } diff --git a/website/docs/r/amplify_domain_association.html.markdown b/website/docs/r/amplify_domain_association.html.markdown index 123c2f6d669..9134f6112b8 100644 --- a/website/docs/r/amplify_domain_association.html.markdown +++ b/website/docs/r/amplify_domain_association.html.markdown @@ -1,23 +1,23 @@ --- -subcategory: "Amplify" +subcategory: "Amplify Console" layout: "aws" page_title: "AWS: aws_amplify_domain_association" description: |- - Provides an Amplify domain association resource. + Provides an Amplify Domain Association resource. --- # Resource: aws_amplify_domain_association -Provides an Amplify domain association resource. +Provides an Amplify Domain Association resource. ## Example Usage -```hcl -resource "aws_amplify_app" "app" { +```terraform +resource "aws_amplify_app" "example" { name = "app" - // Setup redirect from https://example.com to https://www.example.com - custom_rules { + # Setup redirect from https://example.com to https://www.example.com + custom_rule { source = "https://example.com" status = "302" target = "https://www.example.com" @@ -25,23 +25,23 @@ resource "aws_amplify_app" "app" { } resource "aws_amplify_branch" "master" { - app_id = "${aws_amplify_app.app.id}" + app_id = aws_amplify_app.example.id branch_name = "master" } -resource "aws_amplify_domain_association" "app" { - app_id = "${aws_amplify_app.app.id}" +resource "aws_amplify_domain_association" "example" { + app_id = aws_amplify_app.example.id domain_name = "example.com" - // https://example.com - sub_domain_settings { - branch_name = "${aws_amplify_branch.master.branch_name}" + # https://example.com + sub_domain_setting { + branch_name = aws_amplify_branch.master.branch_name prefix = "" } - // https://www.example.com - sub_domain_settings { - branch_name = "${aws_amplify_branch.master.branch_name}" + # https://www.example.com + sub_domain_setting { + branch_name = aws_amplify_branch.master.branch_name prefix = "www" } } @@ -51,27 +51,26 @@ resource "aws_amplify_domain_association" "app" { The following arguments are supported: -* `app_id` - (Required) Unique Id for an Amplify App. -* `domain_name` - (Required) Domain name for the Domain Association. -* `sub_domain_settings` - (Required) Setting structure for the Subdomain. A `sub_domain_settings` block is documented below. -* `enable_auto_sub_domain` - (Optional) Enables automated creation of Subdomains for branches. (Currently not supported) +* `app_id` - (Required) The unique ID for an Amplify app. +* `domain_name` - (Required) The domain name for the domain association. +* `sub_domain_setting` - (Required) The setting for the subdomain. Documented below. * `wait_for_verification` - (Optional) If enabled, the resource will wait for the domain association status to change to PENDING_DEPLOYMENT or AVAILABLE. Setting this to false will skip the process. Default: true. -A `sub_domain_settings` block supports the following arguments: +The `sub_domain_setting` configuration block supports the following arguments: -* `branch_name` - (Required) Branch name setting for the Subdomain. -* `prefix` - (Required) Prefix setting for the Subdomain. +* `branch_name` - (Required) The branch name setting for the subdomain. +* `prefix` - (Required) The prefix setting for the subdomain. -## Attribute Reference +## Attributes Reference -The following attributes are exported: +In addition to all arguments above, the following attributes are exported: -* `arn` - ARN for the Domain Association. +* `arn` - The Amazon Resource Name (ARN) for the domain association. ## Import Amplify domain association can be imported using `app_id` and `domain_name`, e.g. ``` -$ terraform import aws_amplify_domain_association.app d2ypk4k47z8u6/domains/example.com +$ terraform import aws_amplify_domain_association.app d2ypk4k47z8u6/example.com ``` From 8ba927abf3acd0c57926a5cd7daf07d29bc6dd0a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Jun 2021 12:08:00 -0400 Subject: [PATCH 4/9] r/aws_amplify_domain_association: Add and use internal waiter package. --- aws/internal/service/amplify/finder/finder.go | 29 ++ aws/internal/service/amplify/id.go | 19 ++ aws/internal/service/amplify/waiter/status.go | 25 ++ aws/internal/service/amplify/waiter/waiter.go | 42 +++ aws/internal/tfresource/errors.go | 12 + ...resource_aws_amplify_domain_association.go | 277 ++++++++++-------- ...rce_aws_amplify_domain_association_test.go | 51 ++-- .../amplify_domain_association.html.markdown | 16 +- 8 files changed, 315 insertions(+), 156 deletions(-) create mode 100644 aws/internal/service/amplify/waiter/status.go create mode 100644 aws/internal/service/amplify/waiter/waiter.go diff --git a/aws/internal/service/amplify/finder/finder.go b/aws/internal/service/amplify/finder/finder.go index ec468523b9f..9440c53a1f0 100644 --- a/aws/internal/service/amplify/finder/finder.go +++ b/aws/internal/service/amplify/finder/finder.go @@ -93,6 +93,35 @@ func BranchByAppIDAndBranchName(conn *amplify.Amplify, appID, branchName string) return output.Branch, nil } +func DomainAssociationByAppIDAndDomainName(conn *amplify.Amplify, appID, domainName string) (*amplify.DomainAssociation, error) { + input := &lify.GetDomainAssociationInput{ + AppId: aws.String(appID), + DomainName: aws.String(domainName), + } + + output, err := conn.GetDomainAssociation(input) + + if tfawserr.ErrCodeEquals(err, amplify.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.DomainAssociation == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.DomainAssociation, nil +} + func WebhookByID(conn *amplify.Amplify, id string) (*amplify.Webhook, error) { input := &lify.GetWebhookInput{ WebhookId: aws.String(id), diff --git a/aws/internal/service/amplify/id.go b/aws/internal/service/amplify/id.go index 0306a36111d..20bb749cbb0 100644 --- a/aws/internal/service/amplify/id.go +++ b/aws/internal/service/amplify/id.go @@ -42,3 +42,22 @@ func BranchParseResourceID(id string) (string, string, error) { return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected APPID%[2]sBRANCHNAME", id, branchResourceIDSeparator) } + +const domainAssociationResourceIDSeparator = "/" + +func DomainAssociationCreateResourceID(appID, domainName string) string { + parts := []string{appID, domainName} + id := strings.Join(parts, domainAssociationResourceIDSeparator) + + return id +} + +func DomainAssociationParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, domainAssociationResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected APPID%[2]sDOMAINNAME", id, domainAssociationResourceIDSeparator) +} diff --git a/aws/internal/service/amplify/waiter/status.go b/aws/internal/service/amplify/waiter/status.go new file mode 100644 index 00000000000..49be5ec89c8 --- /dev/null +++ b/aws/internal/service/amplify/waiter/status.go @@ -0,0 +1,25 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/amplify" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +func DomainAssociationStatus(conn *amplify.Amplify, appID, domainName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + domainAssociation, err := finder.DomainAssociationByAppIDAndDomainName(conn, appID, domainName) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return domainAssociation, aws.StringValue(domainAssociation.DomainStatus), nil + } +} diff --git a/aws/internal/service/amplify/waiter/waiter.go b/aws/internal/service/amplify/waiter/waiter.go new file mode 100644 index 00000000000..d9b007c8450 --- /dev/null +++ b/aws/internal/service/amplify/waiter/waiter.go @@ -0,0 +1,42 @@ +package waiter + +import ( + "errors" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/amplify" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" +) + +const ( + DomainAssociationVerifiedTimeout = 15 * time.Minute +) + +func DomainAssociationVerified(conn *amplify.Amplify, appID, domainName string) (*amplify.DomainAssociation, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + amplify.DomainStatusPendingVerification, + amplify.DomainStatusInProgress, + amplify.DomainStatusCreating, + amplify.DomainStatusRequestingCertificate, + amplify.DomainStatusUpdating, + }, + Target: []string{amplify.DomainStatusPendingDeployment, amplify.DomainStatusAvailable}, + Refresh: DomainAssociationStatus(conn, appID, domainName), + Timeout: DomainAssociationVerifiedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*amplify.DomainAssociation); ok { + if v != nil && aws.StringValue(v.DomainStatus) == amplify.DomainStatusFailed { + tfresource.SetLastError(err, errors.New(aws.StringValue(v.StatusReason))) + } + + return v, err + } + + return nil, err +} diff --git a/aws/internal/tfresource/errors.go b/aws/internal/tfresource/errors.go index baa733d20a5..703eb55aa7d 100644 --- a/aws/internal/tfresource/errors.go +++ b/aws/internal/tfresource/errors.go @@ -23,3 +23,15 @@ func TimedOut(err error) bool { timeoutErr, ok := err.(*resource.TimeoutError) // nolint:errorlint return ok && timeoutErr.LastError == nil } + +// SetLastError sets the LastError field on the error if supported. +func SetLastError(err, lastErr error) { + var te *resource.TimeoutError + var use *resource.UnexpectedStateError + + if ok := errors.As(err, &te); ok && te.LastError == nil { + te.LastError = lastErr + } else if ok := errors.As(err, &use); ok && use.LastError == nil { + use.LastError = lastErr + } +} diff --git a/aws/resource_aws_amplify_domain_association.go b/aws/resource_aws_amplify_domain_association.go index 8453aed28e6..4e36c21ac40 100644 --- a/aws/resource_aws_amplify_domain_association.go +++ b/aws/resource_aws_amplify_domain_association.go @@ -1,16 +1,18 @@ package aws import ( + "context" "fmt" "log" - "strings" - "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/amplify" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + tfamplify "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsAmplifyDomainAssociation() *schema.Resource { @@ -20,7 +22,11 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { Update: resourceAwsAmplifyDomainAssociationUpdate, Delete: resourceAwsAmplifyDomainAssociationDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + d.Set("wait_for_verification", true) + + return []*schema.ResourceData{d}, nil + }, }, Schema: map[string]*schema.Schema{ @@ -35,13 +41,18 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { Computed: true, }, + "certificate_verification_dns_record": { + Type: schema.TypeString, + Computed: true, + }, + "domain_name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "sub_domain_setting": { + "sub_domain": { Type: schema.TypeSet, Required: true, MaxItems: 255, @@ -51,22 +62,26 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { Type: schema.TypeString, Required: true, }, + "dns_record": { + Type: schema.TypeString, + Computed: true, + }, "prefix": { Type: schema.TypeString, Required: true, }, + "verified": { + Type: schema.TypeBool, + Computed: true, + }, }, }, }, - // non-API "wait_for_verification": { Type: schema.TypeBool, Optional: true, Default: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return true - }, }, }, } @@ -74,29 +89,29 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { func resourceAwsAmplifyDomainAssociationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).amplifyconn - log.Print("[DEBUG] Creating Amplify DomainAssociation") - params := &lify.CreateDomainAssociationInput{ - AppId: aws.String(d.Get("app_id").(string)), - DomainName: aws.String(d.Get("domain_name").(string)), - } + appID := d.Get("app_id").(string) + domainName := d.Get("domain_name").(string) + id := tfamplify.DomainAssociationCreateResourceID(appID, domainName) - if v, ok := d.GetOk("sub_domain_setting"); ok { - params.SubDomainSettings = expandAmplifySubDomainSettings(v.([]interface{})) + input := &lify.CreateDomainAssociationInput{ + AppId: aws.String(appID), + DomainName: aws.String(domainName), + SubDomainSettings: expandAmplifySubDomainSettings(d.Get("sub_domain").(*schema.Set).List()), } - resp, err := conn.CreateDomainAssociation(params) + log.Printf("[DEBUG] Creating Amplify Domain Association: %s", input) + _, err := conn.CreateDomainAssociation(input) + if err != nil { - return fmt.Errorf("Error creating Amplify DomainAssociation: %s", err) + return fmt.Errorf("error creating Amplify Domain Association (%s): %w", id, err) } - arn := *resp.DomainAssociation.DomainAssociationArn - d.SetId(arn[strings.Index(arn, ":apps/")+len(":apps/"):]) + d.SetId(id) if d.Get("wait_for_verification").(bool) { - log.Printf("[DEBUG] Waiting until Amplify DomainAssociation (%s) is deployed", d.Id()) - if err := resourceAwsAmplifyDomainAssociationWaitUntilVerified(d.Id(), meta); err != nil { - return fmt.Errorf("error waiting until Amplify DomainAssociation (%s) is deployed: %s", d.Id(), err) + if _, err := waiter.DomainAssociationVerified(conn, appID, domainName); err != nil { + return fmt.Errorf("error waiting for Amplify Domain Association (%s) to verify: %w", d.Id(), err) } } @@ -105,30 +120,31 @@ func resourceAwsAmplifyDomainAssociationCreate(d *schema.ResourceData, meta inte func resourceAwsAmplifyDomainAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).amplifyconn - log.Printf("[DEBUG] Reading Amplify DomainAssociation: %s", d.Id()) - s := strings.Split(d.Id(), "/") - app_id := s[0] - domain_name := s[2] + appID, domainName, err := tfamplify.DomainAssociationParseResourceID(d.Id()) - resp, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ - AppId: aws.String(app_id), - DomainName: aws.String(domain_name), - }) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == amplify.ErrCodeNotFoundException { - log.Printf("[WARN] Amplify DomainAssociation (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + return fmt.Errorf("error parsing Amplify Domain Association ID: %w", err) + } + + domainAssociation, err := finder.DomainAssociationByAppIDAndDomainName(conn, appID, domainName) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Amplify Domain Association (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading Amplify Domain Association (%s): %w", d.Id(), err) } - d.Set("app_id", app_id) - d.Set("arn", resp.DomainAssociation.DomainAssociationArn) - d.Set("domain_name", resp.DomainAssociation.DomainName) - if err := d.Set("sub_domain_setting", flattenAmplifySubDomainSettings(resp.DomainAssociation.SubDomains)); err != nil { - return fmt.Errorf("error setting sub_domain_setting: %s", err) + d.Set("app_id", appID) + d.Set("arn", domainAssociation.DomainAssociationArn) + d.Set("certificate_verification_dns_record", domainAssociation.CertificateVerificationDNSRecord) + d.Set("domain_name", domainAssociation.DomainName) + if err := d.Set("sub_domain", flattenAmplifySubDomains(domainAssociation.SubDomains)); err != nil { + return fmt.Errorf("error setting sub_domain: %w", err) } return nil @@ -136,30 +152,31 @@ func resourceAwsAmplifyDomainAssociationRead(d *schema.ResourceData, meta interf func resourceAwsAmplifyDomainAssociationUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).amplifyconn - log.Printf("[DEBUG] Updating Amplify DomainAssociation: %s", d.Id()) - s := strings.Split(d.Id(), "/") - app_id := s[0] - domain_name := s[2] + appID, domainName, err := tfamplify.DomainAssociationParseResourceID(d.Id()) - params := &lify.UpdateDomainAssociationInput{ - AppId: aws.String(app_id), - DomainName: aws.String(domain_name), + if err != nil { + return fmt.Errorf("error parsing Amplify Domain Association ID: %w", err) } - if d.HasChange("sub_domain_setting") { - params.SubDomainSettings = expandAmplifySubDomainSettings(d.Get("sub_domain_setting").([]interface{})) - } + if d.HasChange("sub_domain") { + input := &lify.UpdateDomainAssociationInput{ + AppId: aws.String(appID), + DomainName: aws.String(domainName), + SubDomainSettings: expandAmplifySubDomainSettings(d.Get("sub_domain").(*schema.Set).List()), + } - _, err := conn.UpdateDomainAssociation(params) - if err != nil { - return fmt.Errorf("Error updating Amplify DomainAssociation: %s", err) + log.Printf("[DEBUG] Creating Amplify Domain Association: %s", input) + _, err := conn.UpdateDomainAssociation(input) + + if err != nil { + return fmt.Errorf("error updating Amplify Domain Association (%s): %w", d.Id(), err) + } } if d.Get("wait_for_verification").(bool) { - log.Printf("[DEBUG] Waiting until Amplify DomainAssociation (%s) is deployed", d.Id()) - if err := resourceAwsAmplifyDomainAssociationWaitUntilVerified(d.Id(), meta); err != nil { - return fmt.Errorf("error waiting until Amplify DomainAssociation (%s) is deployed: %s", d.Id(), err) + if _, err := waiter.DomainAssociationVerified(conn, appID, domainName); err != nil { + return fmt.Errorf("error waiting for Amplify Domain Association (%s) to verify: %w", d.Id(), err) } } @@ -168,112 +185,118 @@ func resourceAwsAmplifyDomainAssociationUpdate(d *schema.ResourceData, meta inte func resourceAwsAmplifyDomainAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).amplifyconn - log.Printf("[DEBUG] Deleting Amplify DomainAssociation: %s", d.Id()) - s := strings.Split(d.Id(), "/") - app_id := s[0] - domain_name := s[2] + appID, domainName, err := tfamplify.DomainAssociationParseResourceID(d.Id()) - params := &lify.DeleteDomainAssociationInput{ - AppId: aws.String(app_id), - DomainName: aws.String(domain_name), + if err != nil { + return fmt.Errorf("error parsing Amplify Domain Association ID: %w", err) + } + + log.Printf("[DEBUG] Deleting Amplify Domain Association: %s", d.Id()) + _, err = conn.DeleteDomainAssociation(&lify.DeleteDomainAssociationInput{ + AppId: aws.String(appID), + DomainName: aws.String(domainName), + }) + + if tfawserr.ErrCodeEquals(err, amplify.ErrCodeNotFoundException) { + return nil } - _, err := conn.DeleteDomainAssociation(params) if err != nil { - return fmt.Errorf("Error deleting Amplify DomainAssociation: %s", err) + return fmt.Errorf("error deleting Amplify Domain Association (%s): %w", d.Id(), err) } return nil } -func resourceAwsAmplifyDomainAssociationWaitUntilVerified(id string, meta interface{}) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{ - amplify.DomainStatusPendingVerification, - amplify.DomainStatusInProgress, - amplify.DomainStatusCreating, - amplify.DomainStatusRequestingCertificate, - amplify.DomainStatusUpdating, - }, - Target: []string{ - // It takes up to 30 minutes, so skip waiting for deployment. - amplify.DomainStatusPendingDeployment, - amplify.DomainStatusAvailable, - }, - Refresh: resourceAwsAmplifyDomainAssociationStateRefreshFunc(id, meta), - Timeout: 15 * time.Minute, - MinTimeout: 15 * time.Second, - Delay: 10 * time.Second, +func expandAmplifySubDomainSetting(tfMap map[string]interface{}) *amplify.SubDomainSetting { + if tfMap == nil { + return nil + } + + apiObject := &lify.SubDomainSetting{} + + if v, ok := tfMap["branch_name"].(string); ok && v != "" { + apiObject.BranchName = aws.String(v) + } + + if v, ok := tfMap["prefix"].(string); ok && v != "" { + apiObject.Prefix = aws.String(v) } - _, err := stateConf.WaitForState() - return err + return apiObject } -func resourceAwsAmplifyDomainAssociationStateRefreshFunc(id string, meta interface{}) resource.StateRefreshFunc { - s := strings.Split(id, "/") - app_id := s[0] - domain_name := s[2] +func expandAmplifySubDomainSettings(tfList []interface{}) []*amplify.SubDomainSetting { + if len(tfList) == 0 { + return nil + } - return func() (interface{}, string, error) { - conn := meta.(*AWSClient).amplifyconn + var apiObjects []*amplify.SubDomainSetting - resp, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ - AppId: aws.String(app_id), - DomainName: aws.String(domain_name), - }) - if err != nil { - log.Printf("[WARN] Error retrieving Amplify DomainAssociation %q details: %s", id, err) - return nil, "", err + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue } - if *resp.DomainAssociation.DomainStatus == amplify.DomainStatusFailed { - return nil, "", fmt.Errorf("%s", *resp.DomainAssociation.StatusReason) + apiObject := expandAmplifySubDomainSetting(tfMap) + + if apiObject == nil { + continue } - return resp.DomainAssociation, *resp.DomainAssociation.DomainStatus, nil + apiObjects = append(apiObjects, apiObject) } + + return apiObjects } -func expandAmplifySubDomainSettings(values []interface{}) []*amplify.SubDomainSetting { - settings := make([]*amplify.SubDomainSetting, 0) +func flattenAmplifySubDomain(apiObject *amplify.SubDomain) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} - for _, v := range values { - e := v.(map[string]interface{}) + if v := apiObject.DnsRecord; v != nil { + tfMap["dns_record"] = aws.StringValue(v) + } - setting := &lify.SubDomainSetting{} + if v := apiObject.SubDomainSetting; v != nil { + apiObject := v - if ev, ok := e["branch_name"].(string); ok && ev != "" { - setting.BranchName = aws.String(ev) + if v := apiObject.BranchName; v != nil { + tfMap["branch_name"] = aws.StringValue(v) } - if ev, ok := e["prefix"].(string); ok { - setting.Prefix = aws.String(ev) + if v := apiObject.Prefix; v != nil { + tfMap["prefix"] = aws.StringValue(v) } + } - settings = append(settings, setting) + if v := apiObject.Verified; v != nil { + tfMap["verified"] = aws.BoolValue(v) } - return settings + return tfMap } -func flattenAmplifySubDomainSettings(sub_domains []*amplify.SubDomain) []map[string]interface{} { - values := make([]map[string]interface{}, 0) - - for _, v := range sub_domains { - kv := make(map[string]interface{}) +func flattenAmplifySubDomains(apiObjects []*amplify.SubDomain) []interface{} { + if len(apiObjects) == 0 { + return nil + } - if v.SubDomainSetting.BranchName != nil { - kv["branch_name"] = *v.SubDomainSetting.BranchName - } + var tfList []interface{} - if v.SubDomainSetting.Prefix != nil { - kv["prefix"] = *v.SubDomainSetting.Prefix + for _, apiObject := range apiObjects { + if apiObject == nil { + continue } - values = append(values, kv) + tfList = append(tfList, flattenAmplifySubDomain(apiObject)) } - return values + return tfList } diff --git a/aws/resource_aws_amplify_domain_association_test.go b/aws/resource_aws_amplify_domain_association_test.go index b1d1f6b43df..44d9af61d56 100644 --- a/aws/resource_aws_amplify_domain_association_test.go +++ b/aws/resource_aws_amplify_domain_association_test.go @@ -3,14 +3,15 @@ package aws import ( "fmt" "regexp" - "strings" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/amplify" "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" + tfamplify "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func TestAccAWSAmplifyDomainAssociation_basic(t *testing.T) { @@ -21,7 +22,8 @@ func TestAccAWSAmplifyDomainAssociation_basic(t *testing.T) { domainName := "example.com" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSAmplify(t) }, + ErrorCheck: testAccErrorCheck(t, amplify.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, Steps: []resource.TestStep{ @@ -53,54 +55,55 @@ func testAccCheckAWSAmplifyDomainAssociationExists(resourceName string, v *ampli return fmt.Errorf("Not found: %s", resourceName) } - conn := testAccProvider.Meta().(*AWSClient).amplifyconn + if rs.Primary.ID == "" { + return fmt.Errorf("No Amplify Domain Association ID is set") + } - id := strings.Split(rs.Primary.ID, "/") - app_id := id[0] - domain_name := id[2] + appID, domainName, err := tfamplify.DomainAssociationParseResourceID(rs.Primary.ID) - output, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ - AppId: aws.String(app_id), - DomainName: aws.String(domain_name), - }) if err != nil { return err } - if output == nil || output.DomainAssociation == nil { - return fmt.Errorf("Amplify DomainAssociation (%s) not found", rs.Primary.ID) + conn := testAccProvider.Meta().(*AWSClient).amplifyconn + + domainAssociation, err := finder.DomainAssociationByAppIDAndDomainName(conn, appID, domainName) + + if err != nil { + return err } - *v = *output.DomainAssociation + *v = *domainAssociation return nil } } func testAccCheckAWSAmplifyDomainAssociationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).amplifyconn + for _, rs := range s.RootModule().Resources { if rs.Type != "aws_amplify_domain_association" { continue } - conn := testAccProvider.Meta().(*AWSClient).amplifyconn + appID, domainName, err := tfamplify.DomainAssociationParseResourceID(rs.Primary.ID) - s := strings.Split(rs.Primary.ID, "/") - app_id := s[0] - domain_name := s[2] + if err != nil { + return err + } - _, err := conn.GetDomainAssociation(&lify.GetDomainAssociationInput{ - AppId: aws.String(app_id), - DomainName: aws.String(domain_name), - }) + _, err = finder.DomainAssociationByAppIDAndDomainName(conn, appID, domainName) - if isAWSErr(err, amplify.ErrCodeNotFoundException, "") { + if tfresource.NotFound(err) { continue } if err != nil { return err } + + return fmt.Errorf("Amplify Domain Association %s still exists", rs.Primary.ID) } return nil @@ -121,7 +124,7 @@ resource "aws_amplify_domain_association" "test" { app_id = aws_amplify_app.test.id domain_name = "%s" - sub_domain_setting { + sub_domain { branch_name = aws_amplify_branch.test.branch_name prefix = "" } diff --git a/website/docs/r/amplify_domain_association.html.markdown b/website/docs/r/amplify_domain_association.html.markdown index 9134f6112b8..51c5d3cd8fb 100644 --- a/website/docs/r/amplify_domain_association.html.markdown +++ b/website/docs/r/amplify_domain_association.html.markdown @@ -34,13 +34,13 @@ resource "aws_amplify_domain_association" "example" { domain_name = "example.com" # https://example.com - sub_domain_setting { + sub_domain { branch_name = aws_amplify_branch.master.branch_name prefix = "" } # https://www.example.com - sub_domain_setting { + sub_domain { branch_name = aws_amplify_branch.master.branch_name prefix = "www" } @@ -53,10 +53,10 @@ The following arguments are supported: * `app_id` - (Required) The unique ID for an Amplify app. * `domain_name` - (Required) The domain name for the domain association. -* `sub_domain_setting` - (Required) The setting for the subdomain. Documented below. -* `wait_for_verification` - (Optional) If enabled, the resource will wait for the domain association status to change to PENDING_DEPLOYMENT or AVAILABLE. Setting this to false will skip the process. Default: true. +* `sub_domain` - (Required) The setting for the subdomain. Documented below. +* `wait_for_verification` - (Optional) If enabled, the resource will wait for the domain association status to change to `PENDING_DEPLOYMENT` or `AVAILABLE`. Setting this to `false` will skip the process. Default: `true`. -The `sub_domain_setting` configuration block supports the following arguments: +The `sub_domain` configuration block supports the following arguments: * `branch_name` - (Required) The branch name setting for the subdomain. * `prefix` - (Required) The prefix setting for the subdomain. @@ -66,6 +66,12 @@ The `sub_domain_setting` configuration block supports the following arguments: In addition to all arguments above, the following attributes are exported: * `arn` - The Amazon Resource Name (ARN) for the domain association. +* `certificate_verification_dns_record` - The DNS record for certificate verification. + +The `sub_domain` configuration block exports the following attributes: + +* `dns_record` - The DNS record for the subdomain. +* `verified` - The verified status of the subdomain. ## Import From 3d9d57b5448bb20efafe1099f015666041707fdb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Jun 2021 13:57:16 -0400 Subject: [PATCH 5/9] r/aws_amplify_domain_association: First test running. --- ...resource_aws_amplify_domain_association.go | 22 ++++++----- ...rce_aws_amplify_domain_association_test.go | 38 +++++++++---------- aws/resource_aws_amplify_test.go | 3 ++ 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/aws/resource_aws_amplify_domain_association.go b/aws/resource_aws_amplify_domain_association.go index 4e36c21ac40..64e4334e340 100644 --- a/aws/resource_aws_amplify_domain_association.go +++ b/aws/resource_aws_amplify_domain_association.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/amplify" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" tfamplify "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/amplify/waiter" @@ -47,28 +48,30 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { }, "domain_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 255), }, "sub_domain": { Type: schema.TypeSet, Required: true, - MaxItems: 255, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "branch_name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 255), }, "dns_record": { Type: schema.TypeString, Computed: true, }, "prefix": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(0, 255), }, "verified": { Type: schema.TypeBool, @@ -220,7 +223,8 @@ func expandAmplifySubDomainSetting(tfMap map[string]interface{}) *amplify.SubDom apiObject.BranchName = aws.String(v) } - if v, ok := tfMap["prefix"].(string); ok && v != "" { + // Empty prefix is allowed. + if v, ok := tfMap["prefix"].(string); ok { apiObject.Prefix = aws.String(v) } diff --git a/aws/resource_aws_amplify_domain_association_test.go b/aws/resource_aws_amplify_domain_association_test.go index 44d9af61d56..c9373860483 100644 --- a/aws/resource_aws_amplify_domain_association_test.go +++ b/aws/resource_aws_amplify_domain_association_test.go @@ -14,13 +14,11 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) -func TestAccAWSAmplifyDomainAssociation_basic(t *testing.T) { +func testAccAWSAmplifyDomainAssociation_basic(t *testing.T) { var domain amplify.DomainAssociation rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_amplify_domain_association.test" - domainName := "example.com" - resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSAmplify(t) }, ErrorCheck: testAccErrorCheck(t, amplify.EndpointsID), @@ -28,21 +26,23 @@ func TestAccAWSAmplifyDomainAssociation_basic(t *testing.T) { CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAmplifyDomainAssociationConfig_Required(rName, domainName), + Config: testAccAWSAmplifyDomainAssociationConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), - resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile("^arn:[^:]+:amplify:[^:]+:[^:]+:apps/[^/]+/domains/[^/]+$")), - resource.TestCheckResourceAttr(resourceName, "domain_name", domainName), - resource.TestCheckResourceAttr(resourceName, "sub_domain_setting.#", "1"), - resource.TestCheckResourceAttr(resourceName, "sub_domain_setting.0.branch_name", "master"), - resource.TestCheckResourceAttr(resourceName, "sub_domain_setting.0.prefix", ""), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "amplify", regexp.MustCompile(`apps/.+/domains/.+`)), + resource.TestCheckResourceAttr(resourceName, "domain_name", "example.com"), + resource.TestCheckResourceAttr(resourceName, "sub_domain.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "sub_domain.*", map[string]string{ + "branch_name": rName, + "prefix": "www", + }), + resource.TestCheckResourceAttr(resourceName, "wait_for_verification", "false"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"wait_for_verification"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -109,27 +109,27 @@ func testAccCheckAWSAmplifyDomainAssociationDestroy(s *terraform.State) error { return nil } -func testAccAWSAmplifyDomainAssociationConfig_Required(rName string, domainName string) string { +func testAccAWSAmplifyDomainAssociationConfig(rName string) string { return fmt.Sprintf(` resource "aws_amplify_app" "test" { - name = "%s" + name = %[1]q } resource "aws_amplify_branch" "test" { app_id = aws_amplify_app.test.id - branch_name = "master" + branch_name = %[1]q } resource "aws_amplify_domain_association" "test" { app_id = aws_amplify_app.test.id - domain_name = "%s" + domain_name = "example.com" sub_domain { branch_name = aws_amplify_branch.test.branch_name - prefix = "" + prefix = "www" } wait_for_verification = false } -`, rName, domainName) +`, rName) } diff --git a/aws/resource_aws_amplify_test.go b/aws/resource_aws_amplify_test.go index c238c783c87..a4a397bca94 100644 --- a/aws/resource_aws_amplify_test.go +++ b/aws/resource_aws_amplify_test.go @@ -35,6 +35,9 @@ func TestAccAWSAmplify_serial(t *testing.T) { "EnvironmentVariables": testAccAWSAmplifyBranch_EnvironmentVariables, "OptionalArguments": testAccAWSAmplifyBranch_OptionalArguments, }, + "DomainAssociation": { + "basic": testAccAWSAmplifyDomainAssociation_basic, + }, "Webhook": { "basic": testAccAWSAmplifyWebhook_basic, "disappears": testAccAWSAmplifyWebhook_disappears, From 1f0ad57e5dba22686d0fe5bf64aa61aaea3d1320 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Jun 2021 13:58:45 -0400 Subject: [PATCH 6/9] Add CHANGELOG entry. --- .changelog/11938.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/11938.txt diff --git a/.changelog/11938.txt b/.changelog/11938.txt new file mode 100644 index 00000000000..29730594689 --- /dev/null +++ b/.changelog/11938.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_amplify_domain_association +``` \ No newline at end of file From 7f6adbf84e0623bcd24500df2043d0c6853d35b1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Jun 2021 14:51:56 -0400 Subject: [PATCH 7/9] r/aws_amplify_domain_association: Correct waiters. --- aws/internal/service/amplify/waiter/waiter.go | 30 +++- ...resource_aws_amplify_domain_association.go | 11 +- ...rce_aws_amplify_domain_association_test.go | 151 ++++++++++++++++-- aws/resource_aws_amplify_test.go | 4 +- docs/MAINTAINING.md | 1 + 5 files changed, 173 insertions(+), 24 deletions(-) diff --git a/aws/internal/service/amplify/waiter/waiter.go b/aws/internal/service/amplify/waiter/waiter.go index d9b007c8450..08abc2675ac 100644 --- a/aws/internal/service/amplify/waiter/waiter.go +++ b/aws/internal/service/amplify/waiter/waiter.go @@ -11,18 +11,34 @@ import ( ) const ( + DomainAssociationCreatedTimeout = 5 * time.Minute DomainAssociationVerifiedTimeout = 15 * time.Minute ) +func DomainAssociationCreated(conn *amplify.Amplify, appID, domainName string) (*amplify.DomainAssociation, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{amplify.DomainStatusCreating, amplify.DomainStatusInProgress, amplify.DomainStatusRequestingCertificate}, + Target: []string{amplify.DomainStatusPendingVerification, amplify.DomainStatusPendingDeployment, amplify.DomainStatusAvailable}, + Refresh: DomainAssociationStatus(conn, appID, domainName), + Timeout: DomainAssociationCreatedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if v, ok := outputRaw.(*amplify.DomainAssociation); ok { + if v != nil && aws.StringValue(v.DomainStatus) == amplify.DomainStatusFailed { + tfresource.SetLastError(err, errors.New(aws.StringValue(v.StatusReason))) + } + + return v, err + } + + return nil, err +} + func DomainAssociationVerified(conn *amplify.Amplify, appID, domainName string) (*amplify.DomainAssociation, error) { stateConf := &resource.StateChangeConf{ - Pending: []string{ - amplify.DomainStatusPendingVerification, - amplify.DomainStatusInProgress, - amplify.DomainStatusCreating, - amplify.DomainStatusRequestingCertificate, - amplify.DomainStatusUpdating, - }, + Pending: []string{amplify.DomainStatusUpdating, amplify.DomainStatusInProgress, amplify.DomainStatusPendingVerification}, Target: []string{amplify.DomainStatusPendingDeployment, amplify.DomainStatusAvailable}, Refresh: DomainAssociationStatus(conn, appID, domainName), Timeout: DomainAssociationVerifiedTimeout, diff --git a/aws/resource_aws_amplify_domain_association.go b/aws/resource_aws_amplify_domain_association.go index 64e4334e340..f1d7f2a86c6 100644 --- a/aws/resource_aws_amplify_domain_association.go +++ b/aws/resource_aws_amplify_domain_association.go @@ -1,7 +1,6 @@ package aws import ( - "context" "fmt" "log" @@ -23,11 +22,7 @@ func resourceAwsAmplifyDomainAssociation() *schema.Resource { Update: resourceAwsAmplifyDomainAssociationUpdate, Delete: resourceAwsAmplifyDomainAssociationDelete, Importer: &schema.ResourceImporter{ - StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - d.Set("wait_for_verification", true) - - return []*schema.ResourceData{d}, nil - }, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ @@ -112,6 +107,10 @@ func resourceAwsAmplifyDomainAssociationCreate(d *schema.ResourceData, meta inte d.SetId(id) + if _, err := waiter.DomainAssociationCreated(conn, appID, domainName); err != nil { + return fmt.Errorf("error waiting for Amplify Domain Association (%s) to create: %w", d.Id(), err) + } + if d.Get("wait_for_verification").(bool) { if _, err := waiter.DomainAssociationVerified(conn, appID, domainName); err != nil { return fmt.Errorf("error waiting for Amplify Domain Association (%s) to verify: %w", d.Id(), err) diff --git a/aws/resource_aws_amplify_domain_association_test.go b/aws/resource_aws_amplify_domain_association_test.go index c9373860483..701e197546b 100644 --- a/aws/resource_aws_amplify_domain_association_test.go +++ b/aws/resource_aws_amplify_domain_association_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "os" "regexp" "testing" @@ -15,6 +16,12 @@ import ( ) func testAccAWSAmplifyDomainAssociation_basic(t *testing.T) { + key := "AMPLIFY_DOMAIN_NAME" + domainName := os.Getenv(key) + if domainName == "" { + t.Skipf("Environment variable %s is not set", key) + } + var domain amplify.DomainAssociation rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_amplify_domain_association.test" @@ -26,23 +33,112 @@ func testAccAWSAmplifyDomainAssociation_basic(t *testing.T) { CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAmplifyDomainAssociationConfig(rName), + Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "amplify", regexp.MustCompile(`apps/.+/domains/.+`)), - resource.TestCheckResourceAttr(resourceName, "domain_name", "example.com"), + resource.TestCheckResourceAttr(resourceName, "domain_name", domainName), resource.TestCheckResourceAttr(resourceName, "sub_domain.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "sub_domain.*", map[string]string{ "branch_name": rName, - "prefix": "www", + "prefix": "", + }), + resource.TestCheckResourceAttr(resourceName, "wait_for_verification", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_verification"}, + }, + }, + }) +} + +func testAccAWSAmplifyDomainAssociation_disappears(t *testing.T) { + key := "AMPLIFY_DOMAIN_NAME" + domainName := os.Getenv(key) + if domainName == "" { + t.Skipf("Environment variable %s is not set", key) + } + + var domain amplify.DomainAssociation + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_amplify_domain_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSAmplify(t) }, + ErrorCheck: testAccErrorCheck(t, amplify.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAmplifyDomainAssociation(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAWSAmplifyDomainAssociation_update(t *testing.T) { + key := "AMPLIFY_DOMAIN_NAME" + domainName := os.Getenv(key) + if domainName == "" { + t.Skipf("Environment variable %s is not set", key) + } + + var domain amplify.DomainAssociation + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_amplify_domain_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSAmplify(t) }, + ErrorCheck: testAccErrorCheck(t, amplify.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "amplify", regexp.MustCompile(`apps/.+/domains/.+`)), + resource.TestCheckResourceAttr(resourceName, "domain_name", domainName), + resource.TestCheckResourceAttr(resourceName, "sub_domain.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "sub_domain.*", map[string]string{ + "branch_name": rName, + "prefix": "", }), resource.TestCheckResourceAttr(resourceName, "wait_for_verification", "false"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_verification"}, + }, + { + Config: testAccAWSAmplifyDomainAssociationConfigUpdated(rName, domainName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "amplify", regexp.MustCompile(`apps/.+/domains/.+`)), + resource.TestCheckResourceAttr(resourceName, "domain_name", domainName), + resource.TestCheckResourceAttr(resourceName, "sub_domain.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "sub_domain.*", map[string]string{ + "branch_name": rName, + "prefix": "", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "sub_domain.*", map[string]string{ + "branch_name": fmt.Sprintf("%s-2", rName), + "prefix": "www", + }), + resource.TestCheckResourceAttr(resourceName, "wait_for_verification", "true"), + ), }, }, }) @@ -109,7 +205,7 @@ func testAccCheckAWSAmplifyDomainAssociationDestroy(s *terraform.State) error { return nil } -func testAccAWSAmplifyDomainAssociationConfig(rName string) string { +func testAccAWSAmplifyDomainAssociationConfig(rName, domainName string) string { return fmt.Sprintf(` resource "aws_amplify_app" "test" { name = %[1]q @@ -122,14 +218,49 @@ resource "aws_amplify_branch" "test" { resource "aws_amplify_domain_association" "test" { app_id = aws_amplify_app.test.id - domain_name = "example.com" + domain_name = %[2]q sub_domain { branch_name = aws_amplify_branch.test.branch_name - prefix = "www" + prefix = "" } wait_for_verification = false } -`, rName) +`, rName, domainName) +} + +func testAccAWSAmplifyDomainAssociationConfigUpdated(rName, domainName string) string { + return fmt.Sprintf(` +resource "aws_amplify_app" "test" { + name = %[1]q +} + +resource "aws_amplify_branch" "test" { + app_id = aws_amplify_app.test.id + branch_name = %[1]q +} + +resource "aws_amplify_branch" "test2" { + app_id = aws_amplify_app.test.id + branch_name = "%[1]s-2" + } + +resource "aws_amplify_domain_association" "test" { + app_id = aws_amplify_app.test.id + domain_name = %[2]q + + sub_domain { + branch_name = aws_amplify_branch.test.branch_name + prefix = "" + } + + sub_domain { + branch_name = aws_amplify_branch.test2.branch_name + prefix = "www" + } + + wait_for_verification = true +} +`, rName, domainName) } diff --git a/aws/resource_aws_amplify_test.go b/aws/resource_aws_amplify_test.go index a4a397bca94..d4b48560d04 100644 --- a/aws/resource_aws_amplify_test.go +++ b/aws/resource_aws_amplify_test.go @@ -36,7 +36,9 @@ func TestAccAWSAmplify_serial(t *testing.T) { "OptionalArguments": testAccAWSAmplifyBranch_OptionalArguments, }, "DomainAssociation": { - "basic": testAccAWSAmplifyDomainAssociation_basic, + "basic": testAccAWSAmplifyDomainAssociation_basic, + "disappears": testAccAWSAmplifyDomainAssociation_disappears, + "update": testAccAWSAmplifyDomainAssociation_update, }, "Webhook": { "basic": testAccAWSAmplifyWebhook_basic, diff --git a/docs/MAINTAINING.md b/docs/MAINTAINING.md index 2a8d9b3c474..bb36731efe7 100644 --- a/docs/MAINTAINING.md +++ b/docs/MAINTAINING.md @@ -342,6 +342,7 @@ Environment variables (beyond standard AWS Go SDK ones) used by acceptance testi | `ACM_CERTIFICATE_SINGLE_ISSUED_DOMAIN` | Domain name of ACM Certificate with a single issued certificate. **DEPRECATED:** Should be replaced with `aws_acm_certficate` resource usage in tests. | | `ACM_CERTIFICATE_SINGLE_ISSUED_MOST_RECENT_ARN` | Amazon Resource Name of most recent ACM Certificate with a single issued certificate. **DEPRECATED:** Should be replaced with `aws_acm_certficate` resource usage in tests. | | `ADM_CLIENT_ID` | Identifier for Amazon Device Manager Client in Pinpoint testing. | +| `AMPLIFY_DOMAIN_NAME` | Domain name to use for Amplify domain association testing. | | `AMPLIFY_GITHUB_ACCESS_TOKEN` | GitHub access token used for AWS Amplify testing. | | `AMPLIFY_GITHUB_REPOSITORY` | GitHub repository used for AWS Amplify testing. | | `ADM_CLIENT_SECRET` | Secret for Amazon Device Manager Client in Pinpoint testing. | From 6dccac660d47e0113f0be80071d73ba22a38b7f3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Jun 2021 15:01:12 -0400 Subject: [PATCH 8/9] Fix terrafmt error. --- aws/resource_aws_amplify_domain_association_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_amplify_domain_association_test.go b/aws/resource_aws_amplify_domain_association_test.go index 701e197546b..6e3a8f4095a 100644 --- a/aws/resource_aws_amplify_domain_association_test.go +++ b/aws/resource_aws_amplify_domain_association_test.go @@ -242,9 +242,9 @@ resource "aws_amplify_branch" "test" { } resource "aws_amplify_branch" "test2" { - app_id = aws_amplify_app.test.id - branch_name = "%[1]s-2" - } + app_id = aws_amplify_app.test.id + branch_name = "%[1]s-2" +} resource "aws_amplify_domain_association" "test" { app_id = aws_amplify_app.test.id From 459b930e3c1010d2a4e81b309abd8b415c688c62 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Jun 2021 15:16:33 -0400 Subject: [PATCH 9/9] r/aws_amplify_domain_association: Avoid 'BadRequestException: Cannot update domain while the domain is in PENDING_VERIFICATION status.'. --- ...rce_aws_amplify_domain_association_test.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/aws/resource_aws_amplify_domain_association_test.go b/aws/resource_aws_amplify_domain_association_test.go index 6e3a8f4095a..2ffe7d1d0a2 100644 --- a/aws/resource_aws_amplify_domain_association_test.go +++ b/aws/resource_aws_amplify_domain_association_test.go @@ -33,7 +33,7 @@ func testAccAWSAmplifyDomainAssociation_basic(t *testing.T) { CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName), + Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName, false), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "amplify", regexp.MustCompile(`apps/.+/domains/.+`)), @@ -74,7 +74,7 @@ func testAccAWSAmplifyDomainAssociation_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName), + Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName, false), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), testAccCheckResourceDisappears(testAccProvider, resourceAwsAmplifyDomainAssociation(), resourceName), @@ -103,7 +103,7 @@ func testAccAWSAmplifyDomainAssociation_update(t *testing.T) { CheckDestroy: testAccCheckAWSAmplifyDomainAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName), + Config: testAccAWSAmplifyDomainAssociationConfig(rName, domainName, true), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "amplify", regexp.MustCompile(`apps/.+/domains/.+`)), @@ -113,7 +113,7 @@ func testAccAWSAmplifyDomainAssociation_update(t *testing.T) { "branch_name": rName, "prefix": "", }), - resource.TestCheckResourceAttr(resourceName, "wait_for_verification", "false"), + resource.TestCheckResourceAttr(resourceName, "wait_for_verification", "true"), ), }, { @@ -123,7 +123,7 @@ func testAccAWSAmplifyDomainAssociation_update(t *testing.T) { ImportStateVerifyIgnore: []string{"wait_for_verification"}, }, { - Config: testAccAWSAmplifyDomainAssociationConfigUpdated(rName, domainName), + Config: testAccAWSAmplifyDomainAssociationConfigUpdated(rName, domainName, true), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAmplifyDomainAssociationExists(resourceName, &domain), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "amplify", regexp.MustCompile(`apps/.+/domains/.+`)), @@ -205,7 +205,7 @@ func testAccCheckAWSAmplifyDomainAssociationDestroy(s *terraform.State) error { return nil } -func testAccAWSAmplifyDomainAssociationConfig(rName, domainName string) string { +func testAccAWSAmplifyDomainAssociationConfig(rName, domainName string, waitForVerification bool) string { return fmt.Sprintf(` resource "aws_amplify_app" "test" { name = %[1]q @@ -225,12 +225,12 @@ resource "aws_amplify_domain_association" "test" { prefix = "" } - wait_for_verification = false + wait_for_verification = %[3]t } -`, rName, domainName) +`, rName, domainName, waitForVerification) } -func testAccAWSAmplifyDomainAssociationConfigUpdated(rName, domainName string) string { +func testAccAWSAmplifyDomainAssociationConfigUpdated(rName, domainName string, waitForVerification bool) string { return fmt.Sprintf(` resource "aws_amplify_app" "test" { name = %[1]q @@ -260,7 +260,7 @@ resource "aws_amplify_domain_association" "test" { prefix = "www" } - wait_for_verification = true + wait_for_verification = %[3]t } -`, rName, domainName) +`, rName, domainName, waitForVerification) }