diff --git a/.changelog/22550.txt b/.changelog/22550.txt new file mode 100644 index 00000000000..d6bb086d45d --- /dev/null +++ b/.changelog/22550.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_cloudwatch_event_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_cloudwatch_event_bus: 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) +``` \ No newline at end of file diff --git a/internal/service/events/bus.go b/internal/service/events/bus.go index d6ff5f48c09..9ef4416984f 100644 --- a/internal/service/events/bus.go +++ b/internal/service/events/bus.go @@ -69,6 +69,14 @@ func resourceBusCreate(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Creating EventBridge event bus: %v", input) _, err := conn.CreateEventBus(input) + + // Some partitions may not support tag-on-create + if input.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException)) { + log.Printf("[WARN] EventBridge Bus (%s) create failed (%s) with tags. Trying create without tags.", d.Id(), err) + input.Tags = nil + _, err = conn.CreateEventBus(input) + } + if err != nil { return fmt.Errorf("Creating EventBridge event bus (%s) failed: %w", eventBusName, err) } @@ -77,6 +85,20 @@ func resourceBusCreate(d *schema.ResourceData, meta interface{}) error { log.Printf("[INFO] EventBridge event bus (%s) created", d.Id()) + // Post-create tagging supported in some partitions + if input.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, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException)) { + log.Printf("[WARN] error adding tags after create for EventBridge Bus (%s): %s", d.Id(), err) + return resourceBusRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error creating EventBridge Bus (%s) tags: %w", d.Id(), err) + } + } + return resourceBusRead(d, meta) } @@ -106,9 +128,17 @@ func resourceBusRead(d *schema.ResourceData, meta interface{}) error { d.Set("name", output.Name) tags, err := ListTags(conn, aws.StringValue(output.Arn)) + + // ISO partitions may not support tagging, giving error + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException) { + log.Printf("[WARN] Unable to list tags for EventBridge Bus %s: %s", d.Id(), err) + return nil + } + if err != nil { return fmt.Errorf("error listing tags for EventBridge event bus (%s): %w", d.Id(), err) } + tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 @@ -130,8 +160,15 @@ func resourceBusUpdate(d *schema.ResourceData, meta interface{}) error { if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(conn, arn, o, n); err != nil { - return fmt.Errorf("error updating CloudwWatch Events event bus (%s) tags: %w", arn, err) + err := UpdateTags(conn, arn, o, n) + + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException) { + log.Printf("[WARN] Unable to update tags for EventBridge Bus %s: %s", d.Id(), err) + return resourceBusRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error updating EventBridge Bus tags: %w", err) } } diff --git a/internal/service/events/enum.go b/internal/service/events/consts.go similarity index 54% rename from internal/service/events/enum.go rename to internal/service/events/consts.go index dd7115d918e..855168620dd 100644 --- a/internal/service/events/enum.go +++ b/internal/service/events/consts.go @@ -1,5 +1,9 @@ package events +const ( + ErrCodeAccessDenied = "AccessDenied" +) + const ( DefaultEventBusName = "default" ) diff --git a/internal/service/events/rule.go b/internal/service/events/rule.go index c665298137e..7bbf773e476 100644 --- a/internal/service/events/rule.go +++ b/internal/service/events/rule.go @@ -120,23 +120,14 @@ func resourceRuleCreate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[DEBUG] Creating EventBridge Rule: %s", input) - // IAM Roles take some time to propagate - err = resource.Retry(tfiam.PropagationTimeout, func() *resource.RetryError { - _, err = conn.PutRule(input) - - if tfawserr.ErrMessageContains(err, "ValidationException", "cannot be assumed by principal") { - return resource.RetryableError(err) - } - - if err != nil { - return resource.NonRetryableError(err) - } - return nil - }) + err = retryPutRule(conn, input) - if tfresource.TimedOut(err) { - _, err = conn.PutRule(input) + // Some partitions may not support tag-on-create + if input.Tags != nil && (tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException)) { + log.Printf("[WARN] EventBridge Rule (%s) create failed (%s) with tags. Trying create without tags.", d.Id(), err) + input.Tags = nil + err = retryPutRule(conn, input) } if err != nil { @@ -145,6 +136,20 @@ func resourceRuleCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(RuleCreateResourceID(aws.StringValue(input.EventBusName), aws.StringValue(input.Name))) + // Post-create tagging supported in some partitions + if input.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, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException)) { + log.Printf("[WARN] error adding tags after create for EventBridge Rule (%s): %s", d.Id(), err) + return resourceRuleRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error creating EventBridge Rule (%s) tags: %w", name, err) + } + } + return resourceRuleRead(d, meta) } @@ -197,6 +202,12 @@ func resourceRuleRead(d *schema.ResourceData, meta interface{}) error { tags, err := ListTags(conn, arn) + // ISO partitions may not support tagging, giving error + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException) { + log.Printf("[WARN] Unable to list tags for EventBridge Rule %s: %s", d.Id(), err) + return nil + } + if err != nil { return fmt.Errorf("error listing tags for EventBridge Rule (%s): %w", arn, err) } @@ -257,8 +268,15 @@ func resourceRuleUpdate(d *schema.ResourceData, meta interface{}) error { if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(conn, arn, o, n); err != nil { - return fmt.Errorf("error updating CloudwWatch Event Rule (%s) tags: %w", arn, err) + err := UpdateTags(conn, arn, o, n) + + if tfawserr.ErrCodeContains(err, ErrCodeAccessDenied) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeInternalException) || tfawserr.ErrCodeContains(err, eventbridge.ErrCodeOperationDisabledException) { + log.Printf("[WARN] Unable to update tags for EventBridge Rule %s: %s", d.Id(), err) + return resourceRuleRead(d, meta) + } + + if err != nil { + return fmt.Errorf("error updating EventBridge Rule tags: %w", err) } } @@ -311,6 +329,28 @@ func resourceRuleDelete(d *schema.ResourceData, meta interface{}) error { return nil } +func retryPutRule(conn *eventbridge.EventBridge, input *eventbridge.PutRuleInput) error { + err := resource.Retry(tfiam.PropagationTimeout, func() *resource.RetryError { + _, err := conn.PutRule(input) + + if tfawserr.ErrMessageContains(err, "ValidationException", "cannot be assumed by principal") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if tfresource.TimedOut(err) { + _, err = conn.PutRule(input) + } + + return err +} + func buildPutRuleInputStruct(d *schema.ResourceData, name string) (*eventbridge.PutRuleInput, error) { input := eventbridge.PutRuleInput{ Name: aws.String(name),