diff --git a/.changelog/22551.txt b/.changelog/22551.txt new file mode 100644 index 00000000000..f5640efa5a7 --- /dev/null +++ b/.changelog/22551.txt @@ -0,0 +1,27 @@ +```release-note:enhancement +resource/aws_lb_listener: Attempt `tags`-on-create, fallback to tag after create, and allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO) +``` + +```release-note:enhancement +data-source/aws_lb_listener: Allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO) +``` + +```release-note:enhancement +resource/aws_lb_listener_rule: Attempt `tags`-on-create, fallback to tag after create, and allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO) +``` + +```release-note:enhancement +resource/aws_lb: Attempt `tags`-on-create, fallback to tag after create, and allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO) +``` + +```release-note:enhancement +data-source/aws_lb: Allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO) +``` + +```release-note:enhancement +resource/aws_lb_target_group: Attempt `tags`-on-create, fallback to tag after create, and allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO) +``` + +```release-note:enhancement +data-source/aws_lb_target_group: Allow some `tags` errors to be non-fatal to support non-standard AWS partitions (i.e., ISO) +``` \ No newline at end of file diff --git a/internal/service/elbv2/consts.go b/internal/service/elbv2/consts.go new file mode 100644 index 00000000000..e649941274b --- /dev/null +++ b/internal/service/elbv2/consts.go @@ -0,0 +1,5 @@ +package elbv2 + +const ( + ErrCodeAccessDenied = "AccessDenied" +) diff --git a/internal/service/elbv2/listener.go b/internal/service/elbv2/listener.go index 8e2296b67d1..a0b81addb60 100644 --- a/internal/service/elbv2/listener.go +++ b/internal/service/elbv2/listener.go @@ -432,38 +432,36 @@ func resourceListenerCreate(d *schema.ResourceData, meta interface{}) error { } } - var output *elbv2.CreateListenerOutput - - err := resource.Retry(loadBalancerListenerCreateTimeout, func() *resource.RetryError { - var err error - - output, err = conn.CreateListener(params) - - if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeCertificateNotFoundException) { - return resource.RetryableError(err) - } - - if err != nil { - return resource.NonRetryableError(err) - } - - return nil - }) + output, err := retryListenerCreate(conn, params) - if tfresource.TimedOut(err) { - output, err = conn.CreateListener(params) + // Some partitions may not support tag-on-create + if params.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + log.Printf("[WARN] ELBv2 Listener (%s) create failed (%s) with tags. Trying create without tags.", lbArn, err) + params.Tags = nil + output, err = retryListenerCreate(conn, params) } if err != nil { return fmt.Errorf("error creating ELBv2 Listener (%s): %w", lbArn, err) } - if output == nil || len(output.Listeners) == 0 { - return fmt.Errorf("error creating ELBv2 Listener: no listeners returned in response") - } - d.SetId(aws.StringValue(output.Listeners[0].ListenerArn)) + // Post-create tagging supported in some partitions + if params.Tags == nil && len(tags) > 0 { + err := UpdateTags(conn, d.Id(), nil, tags) + + if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + // if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be) + log.Printf("[WARN] error adding tags after create for ELBv2 Listener (%s): %s", d.Id(), err) + return resourceListenerRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error creating ELBv2 Listener (%s) tags: %w", d.Id(), err) + } + } + return resourceListenerRead(d, meta) } @@ -536,6 +534,11 @@ func resourceListenerRead(d *schema.ResourceData, meta interface{}) error { tags, err := ListTags(conn, d.Id()) + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to list tags for ELBv2 Listener %s: %s", d.Id(), err) + return nil + } + if err != nil { return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err) } @@ -639,6 +642,12 @@ func resourceListenerUpdate(d *schema.ResourceData, meta interface{}) error { err = UpdateTags(conn, d.Id(), o, n) } + // ISO partitions may not support tagging, giving error + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to update tags for ELBv2 Listener %s: %s", d.Id(), err) + return resourceListenerRead(d, meta) + } + if err != nil { return fmt.Errorf("error updating LB (%s) tags: %w", d.Id(), err) } @@ -660,6 +669,40 @@ func resourceListenerDelete(d *schema.ResourceData, meta interface{}) error { return nil } +func retryListenerCreate(conn *elbv2.ELBV2, params *elbv2.CreateListenerInput) (*elbv2.CreateListenerOutput, error) { + var output *elbv2.CreateListenerOutput + + err := resource.Retry(loadBalancerListenerCreateTimeout, func() *resource.RetryError { + var err error + + output, err = conn.CreateListener(params) + + if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeCertificateNotFoundException) { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + output, err = conn.CreateListener(params) + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.Listeners) == 0 { + return nil, fmt.Errorf("error creating ELBv2 Listener: no listeners returned in response") + } + + return output, nil +} + func expandLbListenerActions(l []interface{}) ([]*elbv2.Action, error) { if len(l) == 0 { return nil, nil diff --git a/internal/service/elbv2/listener_data_source.go b/internal/service/elbv2/listener_data_source.go index 7d360830608..70849de04f5 100644 --- a/internal/service/elbv2/listener_data_source.go +++ b/internal/service/elbv2/listener_data_source.go @@ -3,10 +3,12 @@ package elbv2 import ( "errors" "fmt" + "log" "sort" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -339,6 +341,11 @@ func dataSourceListenerRead(d *schema.ResourceData, meta interface{}) error { tags, err := ListTags(conn, d.Id()) + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to list tags for ELBv2 Listener %s: %s", d.Id(), err) + return nil + } + if err != nil { return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err) } diff --git a/internal/service/elbv2/listener_rule.go b/internal/service/elbv2/listener_rule.go index e1be12c9790..6ea3d3e9367 100644 --- a/internal/service/elbv2/listener_rule.go +++ b/internal/service/elbv2/listener_rule.go @@ -510,51 +510,36 @@ func resourceListenerRuleCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("error creating LB Listener Rule for Listener (%s): %w", listenerArn, err) } - var resp *elbv2.CreateRuleOutput - if v, ok := d.GetOk("priority"); ok { - var err error - params.Priority = aws.Int64(int64(v.(int))) - resp, err = conn.CreateRule(params) - if err != nil { - return fmt.Errorf("Error creating LB Listener Rule: %w", err) - } - } else { - var priority int64 - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - var err error - priority, err = highestListenerRulePriority(conn, listenerArn) - if err != nil { - return resource.NonRetryableError(err) - } - params.Priority = aws.Int64(priority + 1) - resp, err = conn.CreateRule(params) - if err != nil { - if tfawserr.ErrMessageContains(err, elbv2.ErrCodePriorityInUseException, "") { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil - }) - if tfresource.TimedOut(err) { - priority, err = highestListenerRulePriority(conn, listenerArn) - if err != nil { - return fmt.Errorf("Error getting highest listener rule priority: %w", err) - } - params.Priority = aws.Int64(priority + 1) - resp, err = conn.CreateRule(params) - } - if err != nil { - return fmt.Errorf("Error creating LB Listener Rule: %w", err) - } + resp, err := retryListenerRuleCreate(conn, d, params, listenerArn) + + // Some partitions may not support tag-on-create + if params.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + log.Printf("[WARN] ELBv2 Listener Rule (%s) create failed (%s) with tags. Trying create without tags.", listenerArn, err) + params.Tags = nil + resp, err = retryListenerRuleCreate(conn, d, params, listenerArn) } - if resp == nil || len(resp.Rules) == 0 { - return errors.New("Error creating LB Listener Rule: no rules returned in response") + if err != nil { + return fmt.Errorf("Error creating LB Listener Rule: %w", err) } d.SetId(aws.StringValue(resp.Rules[0].RuleArn)) + // Post-create tagging supported in some partitions + if params.Tags == nil && len(tags) > 0 { + err := UpdateTags(conn, d.Id(), nil, tags) + + if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + // if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be) + log.Printf("[WARN] error adding tags after create for ELBv2 Listener Rule (%s): %s", d.Id(), err) + return resourceListenerRuleRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error creating ELBv2 Listener Rule (%s) tags: %w", d.Id(), err) + } + } + return resourceListenerRuleRead(d, meta) } @@ -600,23 +585,6 @@ func resourceListenerRuleRead(d *schema.ResourceData, meta interface{}) error { d.Set("arn", rule.RuleArn) - tags, err := ListTags(conn, d.Id()) - - if err != nil { - return fmt.Errorf("error listing tags for (%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_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) - } - // The listener arn isn't in the response but can be derived from the rule arn d.Set("listener_arn", ListenerARNFromRuleARN(aws.StringValue(rule.RuleArn))) @@ -796,6 +764,29 @@ func resourceListenerRuleRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("error setting condition: %w", err) } + // tags at the end because, if not supported, will skip the rest of Read + tags, err := ListTags(conn, d.Id()) + + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to list tags for ELBv2 Listener Rule %s: %s", d.Id(), err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing tags for (%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_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) + } + return nil } @@ -874,6 +865,12 @@ func resourceListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error err = UpdateTags(conn, d.Id(), o, n) } + // ISO partitions may not support tagging, giving error + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to update tags for ELBv2 Listener Rule %s: %s", d.Id(), err) + return resourceListenerRuleRead(d, meta) + } + if err != nil { return fmt.Errorf("error updating LB (%s) tags: %w", d.Id(), err) } @@ -894,6 +891,57 @@ func resourceListenerRuleDelete(d *schema.ResourceData, meta interface{}) error return nil } +func retryListenerRuleCreate(conn *elbv2.ELBV2, d *schema.ResourceData, params *elbv2.CreateRuleInput, listenerARN string) (*elbv2.CreateRuleOutput, error) { + var resp *elbv2.CreateRuleOutput + if v, ok := d.GetOk("priority"); ok { + var err error + params.Priority = aws.Int64(int64(v.(int))) + resp, err = conn.CreateRule(params) + + if err != nil { + return nil, err + } + } else { + var priority int64 + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + var err error + priority, err = highestListenerRulePriority(conn, listenerARN) + if err != nil { + return resource.NonRetryableError(err) + } + params.Priority = aws.Int64(priority + 1) + resp, err = conn.CreateRule(params) + if err != nil { + if tfawserr.ErrMessageContains(err, elbv2.ErrCodePriorityInUseException, "") { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + if tfresource.TimedOut(err) { + priority, err = highestListenerRulePriority(conn, listenerARN) + if err != nil { + return nil, fmt.Errorf("getting highest listener rule (%s) priority: %w", listenerARN, err) + } + params.Priority = aws.Int64(priority + 1) + resp, err = conn.CreateRule(params) + } + + if err != nil { + return nil, err + } + } + + if resp == nil || len(resp.Rules) == 0 { + return nil, fmt.Errorf("creating LB Listener Rule (%s): no rules returned in response", listenerARN) + } + + return resp, nil +} + func validListenerRulePriority(v interface{}, k string) (ws []string, errors []error) { value := v.(int) if value < listenerRulePriorityMin || (value > listenerRulePriorityMax && value != listenerRulePriorityDefault) { diff --git a/internal/service/elbv2/listener_rule_test.go b/internal/service/elbv2/listener_rule_test.go index a8a608cf9c7..4add96bcb0b 100644 --- a/internal/service/elbv2/listener_rule_test.go +++ b/internal/service/elbv2/listener_rule_test.go @@ -500,6 +500,10 @@ func TestAccELBV2ListenerRule_priority(t *testing.T) { lbName := fmt.Sprintf("testrule-basic-%s", sdkacctest.RandString(13)) targetGroupName := fmt.Sprintf("testtargetgroup-%s", sdkacctest.RandString(10)) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -1037,6 +1041,10 @@ func TestAccELBV2ListenerRule_conditionUpdateMixed(t *testing.T) { resourceName := "aws_lb_listener_rule.static" frontEndListenerResourceName := "aws_lb_listener.front_end" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), diff --git a/internal/service/elbv2/listener_test.go b/internal/service/elbv2/listener_test.go index 856b48fe9de..7a54b4db786 100644 --- a/internal/service/elbv2/listener_test.go +++ b/internal/service/elbv2/listener_test.go @@ -179,7 +179,7 @@ func TestAccELBV2Listener_forwardWeighted(t *testing.T) { }) } -func TestAccELBV2Listener_basicUdp(t *testing.T) { +func TestAccELBV2Listener_Protocol_upd(t *testing.T) { var conf elbv2.Listener resourceName := "aws_lb_listener.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -251,7 +251,7 @@ func TestAccELBV2Listener_backwardsCompatibility(t *testing.T) { }) } -func TestAccELBV2Listener_https(t *testing.T) { +func TestAccELBV2Listener_Protocol_https(t *testing.T) { var conf elbv2.Listener key := acctest.TLSRSAPrivateKeyPEM(2048) resourceName := "aws_lb_listener.test" @@ -297,6 +297,10 @@ func TestAccELBV2Listener_LoadBalancerARN_gatewayLoadBalancer(t *testing.T) { lbResourceName := "aws_lb.test" resourceName := "aws_lb_listener.test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: testAccErrorCheckSkipELBV2(t), @@ -323,6 +327,10 @@ func TestAccELBV2Listener_Protocol_tls(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_lb_listener.test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -552,7 +560,7 @@ func TestAccELBV2Listener_DefaultAction_order(t *testing.T) { } // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/6171 -func TestAccELBV2Listener_DefaultActionOrder_recreates(t *testing.T) { +func TestAccELBV2Listener_DefaultAction_orderRecreates(t *testing.T) { var listener elbv2.Listener key := acctest.TLSRSAPrivateKeyPEM(2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(key, "example.com") diff --git a/internal/service/elbv2/load_balancer.go b/internal/service/elbv2/load_balancer.go index 6b771c4e5f4..fda101db1dc 100644 --- a/internal/service/elbv2/load_balancer.go +++ b/internal/service/elbv2/load_balancer.go @@ -359,6 +359,14 @@ func resourceLoadBalancerCreate(d *schema.ResourceData, meta interface{}) error log.Printf("[DEBUG] ALB create configuration: %#v", elbOpts) resp, err := conn.CreateLoadBalancer(elbOpts) + + // Some partitions may not support tag-on-create + if elbOpts.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + log.Printf("[WARN] ELBv2 Load Balancer (%s) create failed (%s) with tags. Trying create without tags.", name, err) + elbOpts.Tags = nil + resp, err = conn.CreateLoadBalancer(elbOpts) + } + if err != nil { return fmt.Errorf("error creating %s Load Balancer: %w", d.Get("load_balancer_type").(string), err) } @@ -376,6 +384,21 @@ func resourceLoadBalancerCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("error waiting for Load Balancer (%s) to be active: %w", d.Get("name").(string), err) } + // Post-create tagging supported in some partitions + if elbOpts.Tags == nil && len(tags) > 0 { + err := UpdateTags(conn, d.Id(), nil, tags) + + // if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be) + if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + log.Printf("[WARN] error adding tags after create for ELBv2 Load Balancer (%s): %s", d.Id(), err) + return resourceLoadBalancerUpdate(d, meta) + } + + if err != nil { + return fmt.Errorf("error creating ELBv2 Load Balancer (%s) tags: %w", d.Id(), err) + } + } + return resourceLoadBalancerUpdate(d, meta) } @@ -410,33 +433,6 @@ func resourceLoadBalancerRead(d *schema.ResourceData, meta interface{}) error { func resourceLoadBalancerUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).ELBV2Conn - if d.HasChange("tags_all") { - o, n := d.GetChange("tags_all") - - err := resource.Retry(loadBalancerTagPropagationTimeout, func() *resource.RetryError { - err := UpdateTags(conn, d.Id(), o, n) - - if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeLoadBalancerNotFoundException) { - log.Printf("[DEBUG] Retrying tagging of LB (%s) after error: %s", d.Id(), err) - return resource.RetryableError(err) - } - - if err != nil { - return resource.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - err = UpdateTags(conn, d.Id(), o, n) - } - - if err != nil { - return fmt.Errorf("error updating LB (%s) tags: %w", d.Id(), err) - } - } - attributes := make([]*elbv2.LoadBalancerAttribute, 0) if d.HasChange("access_logs") { @@ -587,6 +583,45 @@ func resourceLoadBalancerUpdate(d *schema.ResourceData, meta interface{}) error } } + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + err := resource.Retry(loadBalancerTagPropagationTimeout, func() *resource.RetryError { + err := UpdateTags(conn, d.Id(), o, n) + + if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeLoadBalancerNotFoundException) { + log.Printf("[DEBUG] Retrying tagging of LB (%s) after error: %s", d.Id(), err) + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + err = UpdateTags(conn, d.Id(), o, n) + } + + // ISO partitions may not support tagging, giving error + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to update tags for ELBv2 Load Balancer %s: %s", d.Id(), err) + + _, err := waitLoadBalancerActive(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return fmt.Errorf("error waiting for Load Balancer (%s) to be active: %w", d.Get("name").(string), err) + } + + return resourceListenerRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error updating LB (%s) tags: %w", d.Id(), err) + } + } + _, err := waitLoadBalancerActive(conn, d.Id(), d.Timeout(schema.TimeoutUpdate)) if err != nil { return fmt.Errorf("error waiting for Load Balancer (%s) to be active: %w", d.Get("name").(string), err) @@ -789,23 +824,6 @@ func flattenResource(d *schema.ResourceData, meta interface{}, lb *elbv2.LoadBal return fmt.Errorf("error setting subnet_mapping: %w", err) } - tags, err := ListTags(conn, d.Id()) - - if err != nil { - return fmt.Errorf("error listing tags for (%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_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) - } - attributesResp, err := conn.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ LoadBalancerArn: aws.String(d.Id()), }) @@ -865,6 +883,28 @@ func flattenResource(d *schema.ResourceData, meta interface{}, lb *elbv2.LoadBal return fmt.Errorf("error setting access_logs: %w", err) } + tags, err := ListTags(conn, d.Id()) + + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to list tags for ELBv2 Load Balancer %s: %s", d.Id(), err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing tags for (%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_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) + } + return nil } diff --git a/internal/service/elbv2/load_balancer_data_source.go b/internal/service/elbv2/load_balancer_data_source.go index 4c1df97c5c5..0b09b6a9e65 100644 --- a/internal/service/elbv2/load_balancer_data_source.go +++ b/internal/service/elbv2/load_balancer_data_source.go @@ -2,6 +2,7 @@ package elbv2 import ( "fmt" + "log" "strconv" "github.com/aws/aws-sdk-go/aws" @@ -251,16 +252,6 @@ func dataSourceLoadBalancerRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("error setting subnet_mapping: %w", err) } - tags, err := ListTags(conn, d.Id()) - - if err != nil { - return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err) - } - - if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) - } - attributesResp, err := conn.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{ LoadBalancerArn: aws.String(d.Id()), }) @@ -313,5 +304,20 @@ func dataSourceLoadBalancerRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("error setting access_logs: %w", err) } + tags, err := ListTags(conn, d.Id()) + + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to list tags for ELBv2 Load Balancer %s: %s", d.Id(), err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing tags for (%s): %w", d.Id(), err) + } + + if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + return nil } diff --git a/internal/service/elbv2/load_balancer_test.go b/internal/service/elbv2/load_balancer_test.go index 5a1fd1b210a..b8ac2e4fb55 100644 --- a/internal/service/elbv2/load_balancer_test.go +++ b/internal/service/elbv2/load_balancer_test.go @@ -487,6 +487,10 @@ func TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateCrossZone(t *testing.T) lbName := fmt.Sprintf("testAccAWSlb-nlbcz-%s", sdkacctest.RandString(10)) resourceName := "aws_lb.lb_test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -528,6 +532,10 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_updateHTTP2(t *testing.T) lbName := fmt.Sprintf("testAccAWSalb-http2-%s", sdkacctest.RandString(10)) resourceName := "aws_lb.lb_test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -568,6 +576,10 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_updateDropInvalidHeaderFie var pre, mid, post elbv2.LoadBalancer lbName := fmt.Sprintf("testAccAWSalb-headers-%s", sdkacctest.RandString(10)) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -609,6 +621,10 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_updateDeletionProtection(t lbName := fmt.Sprintf("testAccAWSalb-basic-%s", sdkacctest.RandString(10)) resourceName := "aws_lb.lb_test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -650,6 +666,10 @@ func TestAccELBV2LoadBalancer_ApplicationLoadBalancer_updateWafFailOpen(t *testi lbName := fmt.Sprintf("testAccAWSalb-basic-%s", sdkacctest.RandString(10)) resourceName := "aws_lb.lb_test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -698,6 +718,10 @@ func TestAccELBV2LoadBalancer_updatedSecurityGroups(t *testing.T) { lbName := fmt.Sprintf("testAccAWSlb-basic-%s", sdkacctest.RandString(10)) resourceName := "aws_lb.lb_test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -728,6 +752,10 @@ func TestAccELBV2LoadBalancer_updatedSubnets(t *testing.T) { lbName := fmt.Sprintf("testAccAWSlb-basic-%s", sdkacctest.RandString(10)) resourceName := "aws_lb.lb_test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -823,6 +851,10 @@ func TestAccELBV2LoadBalancer_ALB_accessLogs(t *testing.T) { lbName := fmt.Sprintf("testAccAWSlbaccesslog-%s", sdkacctest.RandString(4)) resourceName := "aws_lb.test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -911,6 +943,10 @@ func TestAccELBV2LoadBalancer_ALBAccessLogs_prefix(t *testing.T) { lbName := fmt.Sprintf("testAccAWSlbaccesslog-%s", sdkacctest.RandString(4)) resourceName := "aws_lb.test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -981,6 +1017,10 @@ func TestAccELBV2LoadBalancer_NLB_accessLogs(t *testing.T) { lbName := fmt.Sprintf("testAccAWSlbaccesslog-%s", sdkacctest.RandString(4)) resourceName := "aws_lb.test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -1069,6 +1109,10 @@ func TestAccELBV2LoadBalancer_NLBAccessLogs_prefix(t *testing.T) { lbName := fmt.Sprintf("testAccAWSlbaccesslog-%s", sdkacctest.RandString(4)) resourceName := "aws_lb.test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), @@ -1164,6 +1208,10 @@ func TestAccELBV2LoadBalancer_updateDesyncMitigationMode(t *testing.T) { lbName := fmt.Sprintf("testaccawsalb-desync-%s", sdkacctest.RandString(4)) resourceName := "aws_lb.lb_test" + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elbv2.EndpointsID), diff --git a/internal/service/elbv2/target_group.go b/internal/service/elbv2/target_group.go index 2ee94e1e503..aca80c25cdb 100644 --- a/internal/service/elbv2/target_group.go +++ b/internal/service/elbv2/target_group.go @@ -376,6 +376,14 @@ func resourceTargetGroupCreate(d *schema.ResourceData, meta interface{}) error { } resp, err := conn.CreateTargetGroup(params) + + // Some partitions may not support tag-on-create + if params.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + log.Printf("[WARN] ELBv2 Target Group (%s) create failed (%s) with tags. Trying create without tags.", groupName, err) + params.Tags = nil + resp, err = conn.CreateTargetGroup(params) + } + if err != nil { return fmt.Errorf("error creating LB Target Group: %w", err) } @@ -522,6 +530,21 @@ func resourceTargetGroupCreate(d *schema.ResourceData, meta interface{}) error { } } + // Post-create tagging supported in some partitions + if params.Tags == nil && len(tags) > 0 { + err := UpdateTags(conn, d.Id(), nil, tags) + + // if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be) + if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException)) { + log.Printf("[WARN] error adding tags after create for ELBv2 Target Group (%s): %s", d.Id(), err) + return resourceTargetGroupRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error creating ELBv2 Target Group (%s) tags: %w", d.Id(), err) + } + } + return resourceTargetGroupRead(d, meta) } @@ -582,33 +605,6 @@ func resourceTargetGroupRead(d *schema.ResourceData, meta interface{}) error { func resourceTargetGroupUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).ELBV2Conn - if d.HasChange("tags_all") { - o, n := d.GetChange("tags_all") - - err := resource.Retry(loadBalancerTagPropagationTimeout, func() *resource.RetryError { - err := UpdateTags(conn, d.Id(), o, n) - - if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeTargetGroupNotFoundException) { - log.Printf("[DEBUG] Retrying tagging of LB (%s)", d.Id()) - return resource.RetryableError(err) - } - - if err != nil { - return resource.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - err = UpdateTags(conn, d.Id(), o, n) - } - - if err != nil { - return fmt.Errorf("error updating LB Target Group (%s) tags: %w", d.Id(), err) - } - } - if d.HasChange("health_check") { var params *elbv2.ModifyTargetGroupInput healthChecks := d.Get("health_check").([]interface{}) @@ -777,6 +773,39 @@ func resourceTargetGroupUpdate(d *schema.ResourceData, meta interface{}) error { } } + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + err := resource.Retry(loadBalancerTagPropagationTimeout, func() *resource.RetryError { + err := UpdateTags(conn, d.Id(), o, n) + + if tfawserr.ErrCodeEquals(err, elbv2.ErrCodeTargetGroupNotFoundException) { + log.Printf("[DEBUG] Retrying tagging of LB (%s)", d.Id()) + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + err = UpdateTags(conn, d.Id(), o, n) + } + + // ISO partitions may not support tagging, giving error + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to update tags for ELBv2 Target Group %s: %s", d.Id(), err) + return resourceTargetGroupRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error updating LB Target Group (%s) tags: %w", d.Id(), err) + } + } + return resourceTargetGroupRead(d, meta) } @@ -956,6 +985,11 @@ func flattenTargetGroupResource(d *schema.ResourceData, meta interface{}, target tags, err := ListTags(conn, d.Id()) + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to list tags for ELBv2 Target Group %s: %s", d.Id(), err) + return nil + } + if err != nil { return fmt.Errorf("error listing tags for LB Target Group (%s): %w", d.Id(), err) } diff --git a/internal/service/elbv2/target_group_data_source.go b/internal/service/elbv2/target_group_data_source.go index fe1bdcb8d9e..b31eaf644cb 100644 --- a/internal/service/elbv2/target_group_data_source.go +++ b/internal/service/elbv2/target_group_data_source.go @@ -2,10 +2,12 @@ package elbv2 import ( "fmt" + "log" "strconv" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elbv2" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -266,6 +268,11 @@ func dataSourceTargetGroupRead(d *schema.ResourceData, meta interface{}) error { tags, err := ListTags(conn, d.Id()) + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeInvalidConfigurationRequestException) || tfawserr.ErrCodeContains(err, elbv2.ErrCodeOperationNotPermittedException) { + log.Printf("[WARN] Unable to list tags for ELBv2 Target Group %s: %s", d.Id(), err) + return nil + } + if err != nil { return fmt.Errorf("error listing tags for LB Target Group (%s): %w", d.Id(), err) }