From 876c4be9519463df40dbfcc3c07680bc2a87adf2 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Sun, 11 Jul 2021 21:22:56 -0400 Subject: [PATCH] * Add Default Tags Support * Update documentation * Add test coverage for resource updates * refactor expand/flatten methods --- .changelog/18032.txt | 3 - .changelog/19307.txt | 4 + aws/resource_aws_appconfig_application.go | 121 ++--- ...resource_aws_appconfig_application_test.go | 154 +++++-- aws/resource_aws_appconfig_environment.go | 308 +++++++++---- ...resource_aws_appconfig_environment_test.go | 434 +++++++++++++----- website/docs/index.html.markdown | 2 + .../r/appconfig_application.html.markdown | 24 +- .../r/appconfig_environment.html.markdown | 60 +-- 9 files changed, 753 insertions(+), 357 deletions(-) delete mode 100644 .changelog/18032.txt diff --git a/.changelog/18032.txt b/.changelog/18032.txt deleted file mode 100644 index bfbca0898be..00000000000 --- a/.changelog/18032.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:new-resource -aws_appconfig_application -``` \ No newline at end of file diff --git a/.changelog/19307.txt b/.changelog/19307.txt index 446c1c8af69..2c375c1a0d9 100644 --- a/.changelog/19307.txt +++ b/.changelog/19307.txt @@ -1,3 +1,7 @@ +```release-note:new-resource +aws_appconfig_application +``` + ```release-note:new-resource aws_appconfig_environment ``` diff --git a/aws/resource_aws_appconfig_application.go b/aws/resource_aws_appconfig_application.go index adcd5c9a8de..34a26b850d1 100644 --- a/aws/resource_aws_appconfig_application.go +++ b/aws/resource_aws_appconfig_application.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" + "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" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" @@ -23,47 +24,51 @@ func resourceAwsAppconfigApplication() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": { + "arn": { Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 64), - ), + Computed: true, }, "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1024), - ), + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 1024), }, - "tags": tagsSchema(), - "id": { - Type: schema.TypeString, - Computed: true, - }, - "arn": { - Type: schema.TypeString, - Computed: true, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), }, + CustomizeDiff: SetTagsDiff, } } func resourceAwsAppconfigApplicationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + applicationName := d.Get("name").(string) - applicationDescription := d.Get("description").(string) input := &appconfig.CreateApplicationInput{ - Name: aws.String(applicationName), - Description: aws.String(applicationDescription), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), + Name: aws.String(applicationName), + Tags: tags.IgnoreAws().AppconfigTags(), + } + + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) } app, err := conn.CreateApplication(input) + if err != nil { - return fmt.Errorf("Error creating AppConfig application: %s", err) + return fmt.Errorf("error creating AppConfig Application (%s): %w", applicationName, err) + } + + if app == nil { + return fmt.Errorf("error creating AppConfig Application (%s): empty response", applicationName) } d.SetId(aws.StringValue(app.Id)) @@ -73,6 +78,7 @@ func resourceAwsAppconfigApplicationCreate(d *schema.ResourceData, meta interfac func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig input := &appconfig.GetApplicationInput{ @@ -81,21 +87,21 @@ func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{ output, err := conn.GetApplication(input) - if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { log.Printf("[WARN] Appconfig Application (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting AppConfig Application (%s): %s", d.Id(), err) + return fmt.Errorf("error getting AppConfig Application (%s): %w", d.Id(), err) } if output == nil { return fmt.Errorf("error getting AppConfig Application (%s): empty response", d.Id()) } - appARN := arn.ARN{ + arn := arn.ARN{ AccountID: meta.(*AWSClient).accountid, Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, @@ -103,17 +109,25 @@ func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{ Service: "appconfig", }.String() - d.Set("arn", appARN) + d.Set("arn", arn) d.Set("name", output.Name) d.Set("description", output.Description) - tags, err := keyvaluetags.AppconfigListTags(conn, appARN) + tags, err := keyvaluetags.AppconfigListTags(conn, arn) + if err != nil { - return fmt.Errorf("error getting tags for AppConfig Application (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for AppConfig Application (%s): %w", d.Id(), err) + } + + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) } return nil @@ -122,35 +136,32 @@ func resourceAwsAppconfigApplicationRead(d *schema.ResourceData, meta interface{ func resourceAwsAppconfigApplicationUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn - if d.HasChange("tags") { - o, n := d.GetChange("tags") - if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + if d.HasChangesExcept("tags", "tags_all") { + + updateInput := &appconfig.UpdateApplicationInput{ + ApplicationId: aws.String(d.Id()), } - } - appDesc := d.Get("description").(string) - appName := d.Get("name").(string) + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } - updateInput := &appconfig.UpdateApplicationInput{ - ApplicationId: aws.String(d.Id()), - Description: aws.String(appDesc), - Name: aws.String(appName), - } + if d.HasChange("name") { + updateInput.Name = aws.String(d.Get("name").(string)) + } - if d.HasChange("description") { - _, n := d.GetChange("description") - updateInput.Description = aws.String(n.(string)) - } + _, err := conn.UpdateApplication(updateInput) - if d.HasChange("name") { - _, n := d.GetChange("name") - updateInput.Name = aws.String(n.(string)) + if err != nil { + return fmt.Errorf("error updating AppConfig Application(%s): %w", d.Id(), err) + } } - _, err := conn.UpdateApplication(updateInput) - if err != nil { - return fmt.Errorf("error updating AppConfig Application(%s): %s", d.Id(), err) + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig Application (%s) tags: %w", d.Get("arn").(string), err) + } } return resourceAwsAppconfigApplicationRead(d, meta) @@ -165,12 +176,12 @@ func resourceAwsAppconfigApplicationDelete(d *schema.ResourceData, meta interfac _, err := conn.DeleteApplication(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { return nil } if err != nil { - return fmt.Errorf("error deleting Appconfig Application (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Appconfig Application (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_appconfig_application_test.go b/aws/resource_aws_appconfig_application_test.go index 41ff1c82031..e0091036001 100644 --- a/aws/resource_aws_appconfig_application_test.go +++ b/aws/resource_aws_appconfig_application_test.go @@ -2,20 +2,21 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appconfig" + "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" ) func TestAccAWSAppConfigApplication_basic(t *testing.T) { - var application appconfig.GetApplicationOutput rName := acctest.RandomWithPrefix("tf-acc-test") - rDesc := acctest.RandomWithPrefix("desc") resourceName := "aws_appconfig_application.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), @@ -23,13 +24,12 @@ func TestAccAWSAppConfigApplication_basic(t *testing.T) { CheckDestroy: testAccCheckAppConfigApplicationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigApplicationName(rName, rDesc), + Config: testAccAWSAppConfigApplicationConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "appconfig", regexp.MustCompile(`application/[a-z0-9]{4,7}`)), resource.TestCheckResourceAttr(resourceName, "name", rName), - testAccCheckAWSAppConfigApplicationARN(resourceName, &application), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "description", rDesc), ), }, { @@ -42,10 +42,7 @@ func TestAccAWSAppConfigApplication_basic(t *testing.T) { } func TestAccAWSAppConfigApplication_disappears(t *testing.T) { - var application appconfig.GetApplicationOutput - rName := acctest.RandomWithPrefix("tf-acc-test") - rDesc := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_application.test" resource.ParallelTest(t, resource.TestCase{ @@ -55,10 +52,10 @@ func TestAccAWSAppConfigApplication_disappears(t *testing.T) { CheckDestroy: testAccCheckAppConfigApplicationDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigApplicationName(rName, rDesc), + Config: testAccAWSAppConfigApplicationConfigName(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), - testAccCheckAWSAppConfigApplicationDisappears(&application), + testAccCheckAWSAppConfigApplicationExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppconfigApplication(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -66,9 +63,86 @@ func TestAccAWSAppConfigApplication_disappears(t *testing.T) { }) } -func TestAccAWSAppConfigApplication_Tags(t *testing.T) { - var application appconfig.GetApplicationOutput +func TestAccAWSAppConfigApplication_updateName(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigApplicationConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + ), + }, + { + Config: testAccAWSAppConfigApplicationConfigName(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigApplication_updateDescription(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + description := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigApplicationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigApplicationConfigDescription(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigApplicationConfigDescription(rName, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Description Removal + Config: testAccAWSAppConfigApplicationConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigApplicationExists(resourceName), + ), + }, + }, + }) +} +func TestAccAWSAppConfigApplication_Tags(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_application.test" @@ -81,7 +155,7 @@ func TestAccAWSAppConfigApplication_Tags(t *testing.T) { { Config: testAccAWSAppConfigApplicationTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), @@ -94,7 +168,7 @@ func TestAccAWSAppConfigApplication_Tags(t *testing.T) { { Config: testAccAWSAppConfigApplicationTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -103,7 +177,7 @@ func TestAccAWSAppConfigApplication_Tags(t *testing.T) { { Config: testAccAWSAppConfigApplicationTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigApplicationExists(resourceName, &application), + testAccCheckAWSAppConfigApplicationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -126,12 +200,12 @@ func testAccCheckAppConfigApplicationDestroy(s *terraform.State) error { output, err := conn.GetApplication(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { continue } if err != nil { - return err + return fmt.Errorf("error reading AppConfig Application (%s): %w", rs.Primary.ID, err) } if output != nil { @@ -140,24 +214,9 @@ func testAccCheckAppConfigApplicationDestroy(s *terraform.State) error { } return nil - -} - -func testAccCheckAWSAppConfigApplicationDisappears(application *appconfig.GetApplicationOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).appconfigconn - - input := &appconfig.DeleteApplicationInput{ - ApplicationId: aws.String(*application.Id), - } - - _, err := conn.DeleteApplication(input) - - return err - } } -func testAccCheckAWSAppConfigApplicationExists(resourceName string, application *appconfig.GetApplicationOutput) resource.TestCheckFunc { +func testAccCheckAWSAppConfigApplicationExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -175,29 +234,34 @@ func testAccCheckAWSAppConfigApplicationExists(resourceName string, application } output, err := conn.GetApplication(input) + if err != nil { - return err + return fmt.Errorf("error reading AppConfig Application (%s): %w", rs.Primary.ID, err) } - *application = *output + if output == nil { + return fmt.Errorf("AppConfig Application (%s) not found", rs.Primary.ID) + } return nil } } -func testAccCheckAWSAppConfigApplicationARN(resourceName string, application *appconfig.GetApplicationOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s", aws.StringValue(application.Id)))(s) - } +func testAccAWSAppConfigApplicationConfigName(rName string) string { + return fmt.Sprintf(` +resource "aws_appconfig_application" "test" { + name = %[1]q +} +`, rName) } -func testAccAWSAppConfigApplicationName(rName, rDesc string) string { +func testAccAWSAppConfigApplicationConfigDescription(rName, description string) string { return fmt.Sprintf(` resource "aws_appconfig_application" "test" { - name = %[1]q - description = %[2]q + name = %q + description = %q } -`, rName, rDesc) +`, rName, description) } func testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1 string) string { diff --git a/aws/resource_aws_appconfig_environment.go b/aws/resource_aws_appconfig_environment.go index 3a66831f269..415bfce0c2f 100644 --- a/aws/resource_aws_appconfig_environment.go +++ b/aws/resource_aws_appconfig_environment.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/appconfig" + "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" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" @@ -20,48 +21,42 @@ func resourceAwsAppconfigEnvironment() *schema.Resource { Update: resourceAwsAppconfigEnvironmentUpdate, Delete: resourceAwsAppconfigEnvironmentDelete, Importer: &schema.ResourceImporter{ - State: resourceAwsAppconfigEnvironmentImport, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 64), - ), - }, "application_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(4, 7), - ), - }, - "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.All( - validation.StringLenBetween(0, 1024), - ), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(4, 7), }, - "tags": tagsSchema(), "arn": { Type: schema.TypeString, Computed: true, }, - "monitors": { - Type: schema.TypeList, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(0, 1024), + }, + "monitor": { + Type: schema.TypeSet, Optional: true, MaxItems: 5, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "alarm_arn": { Type: schema.TypeString, - Optional: true, + Required: true, ValidateFunc: validation.All( - validation.StringLenBetween(20, 2048), + validation.StringLenBetween(1, 2048), + validateArn, ), }, "alarm_role_arn": { @@ -69,107 +64,125 @@ func resourceAwsAppconfigEnvironment() *schema.Resource { Optional: true, ValidateFunc: validation.All( validation.StringLenBetween(20, 2048), + validateArn, ), }, }, }, }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchema(), + "tags_all": tagsSchemaComputed(), }, + CustomizeDiff: SetTagsDiff, } } func resourceAwsAppconfigEnvironmentCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + appId := d.Get("application_id").(string) input := &appconfig.CreateEnvironmentInput{ Name: aws.String(d.Get("name").(string)), - Description: aws.String(d.Get("description").(string)), - ApplicationId: aws.String(d.Get("application_id").(string)), - Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().AppconfigTags(), - Monitors: expandAppconfigEnvironmentMonitors(d.Get("monitors").([]interface{})), + ApplicationId: aws.String(appId), + Tags: tags.IgnoreAws().AppconfigTags(), } - environment, err := conn.CreateEnvironment(input) - if err != nil { - return fmt.Errorf("Error creating AppConfig Environment: %s", err) + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) } - d.SetId(aws.StringValue(environment.Id)) + if v, ok := d.GetOk("monitor"); ok && v.(*schema.Set).Len() > 0 { + input.Monitors = expandAppconfigEnvironmentMonitors(v.(*schema.Set).List()) + } - return resourceAwsAppconfigEnvironmentRead(d, meta) -} + environment, err := conn.CreateEnvironment(input) -func expandAppconfigEnvironmentMonitors(list []interface{}) []*appconfig.Monitor { - monitors := make([]*appconfig.Monitor, len(list)) - for i, monitorInterface := range list { - m := monitorInterface.(map[string]interface{}) - monitors[i] = &appconfig.Monitor{ - AlarmArn: aws.String(m["alarm_arn"].(string)), - AlarmRoleArn: aws.String(m["alarm_role_arn"].(string)), - } + if err != nil { + return fmt.Errorf("error creating AppConfig Environment for Application (%s): %w", appId, err) } - return monitors -} -func flattenAwsAppconfigEnvironmentMonitors(monitors []*appconfig.Monitor) []interface{} { - list := make([]interface{}, len(monitors)) - for i, monitor := range monitors { - list[i] = map[string]interface{}{ - "alarm_arn": aws.StringValue(monitor.AlarmArn), - "alarm_role_arn": aws.StringValue(monitor.AlarmRoleArn), - } + if environment == nil { + return fmt.Errorf("error creating AppConfig Environment for Application (%s): empty response", appId) } - return list + + d.SetId(fmt.Sprintf("%s:%s", aws.StringValue(environment.Id), aws.StringValue(environment.ApplicationId))) + + return resourceAwsAppconfigEnvironmentRead(d, meta) } func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - appID := d.Get("application_id").(string) + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(d.Id()) + + if err != nil { + return err + } input := &appconfig.GetEnvironmentInput{ ApplicationId: aws.String(appID), - EnvironmentId: aws.String(d.Id()), + EnvironmentId: aws.String(envID), } output, err := conn.GetEnvironment(input) - if !d.IsNewResource() && isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] Appconfig Environment (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Appconfig Environment (%s) for Application (%s) not found, removing from state", envID, appID) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting AppConfig Environment (%s): %s", d.Id(), err) + return fmt.Errorf("error getting AppConfig Environment (%s) for Application (%s): %w", envID, appID, err) } if output == nil { - return fmt.Errorf("error getting AppConfig Environment (%s): empty response", d.Id()) + return fmt.Errorf("error getting AppConfig Environment (%s) for Application (%s): empty response", envID, appID) } - d.Set("name", output.Name) - d.Set("description", output.Description) d.Set("application_id", output.ApplicationId) - d.Set("monitors", flattenAwsAppconfigEnvironmentMonitors(output.Monitors)) + d.Set("description", output.Description) + d.Set("name", output.Name) + d.Set("state", output.State) + + if err := d.Set("monitor", flattenAwsAppconfigEnvironmentMonitors(output.Monitors)); err != nil { + return fmt.Errorf("error setting monitor: %w", err) + } - environmentARN := arn.ARN{ + arn := arn.ARN{ AccountID: meta.(*AWSClient).accountid, Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, - Resource: fmt.Sprintf("application/%s/environment/%s", appID, d.Id()), + Resource: fmt.Sprintf("application/%s/environment/%s", appID, envID), Service: "appconfig", }.String() - d.Set("arn", environmentARN) - tags, err := keyvaluetags.AppconfigListTags(conn, environmentARN) + d.Set("arn", arn) + + tags, err := keyvaluetags.AppconfigListTags(conn, arn) + if err != nil { - return fmt.Errorf("error getting tags for AppConfig Environment (%s): %s", d.Id(), err) + return fmt.Errorf("error listing tags for AppConfig Environment (%s): %s", d.Id(), err) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) } return nil @@ -178,33 +191,42 @@ func resourceAwsAppconfigEnvironmentRead(d *schema.ResourceData, meta interface{ func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn - updateInput := &appconfig.UpdateEnvironmentInput{ - EnvironmentId: aws.String(d.Id()), - ApplicationId: aws.String(d.Get("application_id").(string)), - } + if d.HasChangesExcept("tags", "tags_all") { + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(d.Id()) - if d.HasChange("description") { - updateInput.Description = aws.String(d.Get("description").(string)) - } + if err != nil { + return err + } - if d.HasChange("name") { - updateInput.Name = aws.String(d.Get("name").(string)) - } + updateInput := &appconfig.UpdateEnvironmentInput{ + EnvironmentId: aws.String(envID), + ApplicationId: aws.String(appID), + } - if d.HasChange("tags") { - o, n := d.GetChange("tags") - if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating AppConfig (%s) tags: %s", d.Id(), err) + if d.HasChange("description") { + updateInput.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("name") { + updateInput.Name = aws.String(d.Get("name").(string)) } - } - if d.HasChange("monitors") { - updateInput.Monitors = expandAppconfigEnvironmentMonitors(d.Get("monitors").([]interface{})) + if d.HasChange("monitor") { + updateInput.Monitors = expandAppconfigEnvironmentMonitors(d.Get("monitor").(*schema.Set).List()) + } + + _, err = conn.UpdateEnvironment(updateInput) + + if err != nil { + return fmt.Errorf("error updating AppConfig Environment (%s) for Application (%s): %w", envID, appID, err) + } } - _, err := conn.UpdateEnvironment(updateInput) - if err != nil { - return fmt.Errorf("error updating AppConfig Environment(%s): %s", d.Id(), err) + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := keyvaluetags.AppconfigUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating AppConfig Environment (%s) tags: %w", d.Get("arn").(string), err) + } } return resourceAwsAppconfigEnvironmentRead(d, meta) @@ -213,32 +235,114 @@ func resourceAwsAppconfigEnvironmentUpdate(d *schema.ResourceData, meta interfac func resourceAwsAppconfigEnvironmentDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).appconfigconn + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(d.Id()) + + if err != nil { + return err + } + input := &appconfig.DeleteEnvironmentInput{ - EnvironmentId: aws.String(d.Id()), - ApplicationId: aws.String(d.Get("application_id").(string)), + EnvironmentId: aws.String(envID), + ApplicationId: aws.String(appID), } - _, err := conn.DeleteEnvironment(input) + _, err = conn.DeleteEnvironment(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { return nil } if err != nil { - return fmt.Errorf("error deleting Appconfig Environment (%s): %s", d.Id(), err) + return fmt.Errorf("error deleting Appconfig Environment (%s) for Application (%s): %w", envID, appID, err) } return nil } -func resourceAwsAppconfigEnvironmentImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - parts := strings.Split(d.Id(), "/") - if len(parts) != 2 { - return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'application-id/environment-id'", d.Id()) +func resourceAwsAppconfigEnvironmentParseID(id string) (string, string, error) { + parts := strings.Split(id, ":") + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%q), expected environmentID:applicationID", id) + } + + return parts[0], parts[1], nil +} + +func expandAppconfigEnvironmentMonitor(tfMap map[string]interface{}) *appconfig.Monitor { + if tfMap == nil { + return nil + } + + monitor := &appconfig.Monitor{} + + if v, ok := tfMap["alarm_arn"].(string); ok && v != "" { + monitor.AlarmArn = aws.String(v) } - d.SetId(parts[1]) - d.Set("application_id", parts[0]) + if v, ok := tfMap["alarm_role_arn"].(string); ok && v != "" { + monitor.AlarmRoleArn = aws.String(v) + } + + return monitor +} + +func expandAppconfigEnvironmentMonitors(tfList []interface{}) []*appconfig.Monitor { + // AppConfig API requires a 0 length slice instead of a nil value + // when updating from N monitors to 0/nil monitors + monitors := make([]*appconfig.Monitor, 0) + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + monitor := expandAppconfigEnvironmentMonitor(tfMap) + + if monitor == nil { + continue + } + + monitors = append(monitors, monitor) + } + + return monitors +} + +func flattenAwsAppconfigEnvironmentMonitor(monitor *appconfig.Monitor) map[string]interface{} { + if monitor == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := monitor.AlarmArn; v != nil { + tfMap["alarm_arn"] = aws.StringValue(v) + } + + if v := monitor.AlarmRoleArn; v != nil { + tfMap["alarm_role_arn"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenAwsAppconfigEnvironmentMonitors(monitors []*appconfig.Monitor) []interface{} { + if len(monitors) == 0 { + return nil + } + + var tfList []interface{} + + for _, monitor := range monitors { + if monitor == nil { + continue + } + + tfList = append(tfList, flattenAwsAppconfigEnvironmentMonitor(monitor)) + } - return []*schema.ResourceData{d}, nil + return tfList } diff --git a/aws/resource_aws_appconfig_environment_test.go b/aws/resource_aws_appconfig_environment_test.go index fff5d3affa1..3845bc9e41c 100644 --- a/aws/resource_aws_appconfig_environment_test.go +++ b/aws/resource_aws_appconfig_environment_test.go @@ -2,24 +2,22 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appconfig" + "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" ) func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { - var environment appconfig.GetEnvironmentOutput - roleName := acctest.RandomWithPrefix("tf-acc-test") - alarmName := acctest.RandomWithPrefix("tf-acc-test") - appName := acctest.RandomWithPrefix("tf-acc-test") - appDesc := acctest.RandomWithPrefix("desc") - envName := acctest.RandomWithPrefix("tf-acc-test") - envDesc := acctest.RandomWithPrefix("desc") + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_environment.test" + appResourceName := "aws_appconfig_application.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), @@ -27,18 +25,19 @@ func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigEnvironmentWithMonitors(roleName, alarmName, appName, appDesc, envName, envDesc), + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), - resource.TestCheckResourceAttr(resourceName, "name", envName), - testAccCheckAWSAppConfigEnvironmentARN(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "appconfig", regexp.MustCompile(`application/[a-z0-9]{4,7}/environment/[a-z0-9]{4,7}`)), + resource.TestCheckResourceAttrPair(resourceName, "application_id", appResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "0"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "state"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "description", envDesc), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigEnvironmentImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -47,12 +46,7 @@ func TestAccAWSAppConfigEnvironment_basic(t *testing.T) { } func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { - var environment appconfig.GetEnvironmentOutput - - appName := acctest.RandomWithPrefix("tf-acc-test") - appDesc := acctest.RandomWithPrefix("desc") - envName := acctest.RandomWithPrefix("tf-acc-test") - envDesc := acctest.RandomWithPrefix("desc") + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_environment.test" resource.ParallelTest(t, resource.TestCase{ @@ -62,10 +56,10 @@ func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAppConfigEnvironment(appName, appDesc, envName, envDesc), + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), - testAccCheckAWSAppConfigEnvironmentDisappears(&environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsAppconfigEnvironment(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -73,9 +67,185 @@ func TestAccAWSAppConfigEnvironment_disappears(t *testing.T) { }) } -func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { - var environment appconfig.GetEnvironmentOutput +func TestAccAWSAppConfigEnvironment_updateName(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_environment.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + ), + }, + { + Config: testAccAWSAppConfigEnvironmentConfigBasic(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func TestAccAWSAppConfigEnvironment_updateDescription(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + description := acctest.RandomWithPrefix("tf-acc-test-update") + resourceName := "aws_appconfig_environment.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentConfigDescription(rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigEnvironmentConfigDescription(rName, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Description Removal + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_Monitors(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_appconfig_environment.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentWithMonitors(rName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_arn", "aws_cloudwatch_metric_alarm.test.0", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigEnvironmentWithMonitors(rName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "2"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_arn", "aws_cloudwatch_metric_alarm.test.0", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_arn", "aws_cloudwatch_metric_alarm.test.1", "arn"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "monitor.*.alarm_role_arn", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Monitor Removal + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "monitor.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_MultipleEnvironments(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName1 := "aws_appconfig_environment.test" + resourceName2 := "aws_appconfig_environment.test2" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, appconfig.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAppConfigEnvironmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAppConfigEnvironmentConfigMultiple(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName1), + testAccCheckAWSAppConfigEnvironmentExists(resourceName2), + ), + }, + { + ResourceName: resourceName1, + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: resourceName2, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAppConfigEnvironmentConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAppConfigEnvironmentExists(resourceName1), + ), + }, + { + ResourceName: resourceName1, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_appconfig_environment.test" @@ -88,21 +258,20 @@ func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { { Config: testAccAWSAppConfigEnvironmentTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { ResourceName: resourceName, - ImportStateIdFunc: testAccAWSAppConfigEnvironmentImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, { Config: testAccAWSAppConfigEnvironmentTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -111,7 +280,7 @@ func TestAccAWSAppConfigEnvironment_Tags(t *testing.T) { { Config: testAccAWSAppConfigEnvironmentTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAppConfigEnvironmentExists(resourceName, &environment), + testAccCheckAWSAppConfigEnvironmentExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), @@ -128,43 +297,36 @@ func testAccCheckAppConfigEnvironmentDestroy(s *terraform.State) error { continue } + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(rs.Primary.ID) + + if err != nil { + return err + } + input := &appconfig.GetEnvironmentInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - EnvironmentId: aws.String(rs.Primary.ID), + ApplicationId: aws.String(appID), + EnvironmentId: aws.String(envID), } output, err := conn.GetEnvironment(input) - if isAWSErr(err, appconfig.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, appconfig.ErrCodeResourceNotFoundException) { continue } if err != nil { - return err + return fmt.Errorf("error reading AppConfig Environment (%s) for Application (%s): %w", envID, appID, err) } if output != nil { - return fmt.Errorf("AppConfig Environment (%s) still exists", rs.Primary.ID) + return fmt.Errorf("AppConfig Environment (%s) for Application (%s) still exists", envID, appID) } } return nil } -func testAccCheckAWSAppConfigEnvironmentDisappears(environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).appconfigconn - - _, err := conn.DeleteEnvironment(&appconfig.DeleteEnvironmentInput{ - ApplicationId: environment.ApplicationId, - EnvironmentId: environment.Id, - }) - - return err - } -} - -func testAccCheckAWSAppConfigEnvironmentExists(resourceName string, environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { +func testAccCheckAWSAppConfigEnvironmentExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -175,84 +337,64 @@ func testAccCheckAWSAppConfigEnvironmentExists(resourceName string, environment return fmt.Errorf("Resource (%s) ID not set", resourceName) } - conn := testAccProvider.Meta().(*AWSClient).appconfigconn + envID, appID, err := resourceAwsAppconfigEnvironmentParseID(rs.Primary.ID) - output, err := conn.GetEnvironment(&appconfig.GetEnvironmentInput{ - ApplicationId: aws.String(rs.Primary.Attributes["application_id"]), - EnvironmentId: aws.String(rs.Primary.ID), - }) if err != nil { return err } - *environment = *output + conn := testAccProvider.Meta().(*AWSClient).appconfigconn - return nil - } -} + input := &appconfig.GetEnvironmentInput{ + ApplicationId: aws.String(appID), + EnvironmentId: aws.String(envID), + } -func testAccCheckAWSAppConfigEnvironmentARN(resourceName string, environment *appconfig.GetEnvironmentOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - return testAccCheckResourceAttrRegionalARN(resourceName, "arn", "appconfig", fmt.Sprintf("application/%s/environment/%s", aws.StringValue(environment.ApplicationId), aws.StringValue(environment.Id)))(s) - } -} + output, err := conn.GetEnvironment(input) -func testAccAWSAppConfigEnvironmentWithMonitors(roleName, alarmName, appName, appDesc, envName, envDesc string) string { - return testAccAWSAppConfigMonitor_ServiceRole(roleName) + testAccAWSCloudWatchMetricAlarmConfig(alarmName) + testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` -resource "aws_appconfig_environment" "test" { - name = %[1]q - description = %[2]q - application_id = aws_appconfig_application.test.id + if err != nil { + return err + } - monitors { - alarm_arn = aws_cloudwatch_metric_alarm.test.arn - alarm_role_arn = aws_iam_role.test.arn - } -} -`, envName, envDesc) -} + if output == nil { + return fmt.Errorf("AppConfig Environment (%s) for Application (%s) not found", envID, appID) + } -func testAccAWSAppConfigEnvironment(appName, appDesc, envName, envDesc string) string { - return testAccAWSAppConfigApplicationName(appName, appDesc) + fmt.Sprintf(` -resource "aws_appconfig_environment" "test" { - name = %[1]q - description = %[2]q - application_id = aws_appconfig_application.test.id -} -`, envName, envDesc) + return nil + } } -func testAccAWSAppConfigEnvironmentTags1(rName, tagKey1, tagValue1 string) string { - return testAccAWSAppConfigApplicationTags1(rName, tagKey1, tagValue1) + fmt.Sprintf(` +func testAccAWSAppConfigEnvironmentConfigBasic(rName string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` resource "aws_appconfig_environment" "test" { - name = %[1]q + name = %q application_id = aws_appconfig_application.test.id - - tags = { - %[2]q = %[3]q - } } -`, rName, tagKey1, tagValue1) +`, rName)) } -func testAccAWSAppConfigEnvironmentTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccAWSAppConfigApplicationTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2) + fmt.Sprintf(` +func testAccAWSAppConfigEnvironmentConfigDescription(rName, description string) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` resource "aws_appconfig_environment" "test" { - name = %[1]q + name = %q + description = %q application_id = aws_appconfig_application.test.id - - tags = { - %[2]q = %[3]q - %[4]q = %[5]q - } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, description)) } -func testAccAWSAppConfigMonitor_ServiceRole(rName string) string { - return fmt.Sprintf(` +func testAccAWSAppConfigEnvironmentWithMonitors(rName string, count int) string { + return composeConfig( + testAccAWSAppConfigApplicationConfigName(rName), + fmt.Sprintf(` +data "aws_partition" "current" {} + resource "aws_iam_role" "test" { - name = "%s" + name = %[1]q assume_role_policy = <