From 5bd622b2f9e48284ee4d68d43b9ee79eb03c5495 Mon Sep 17 00:00:00 2001 From: "siddhartha.yashwanth" Date: Wed, 17 Apr 2024 15:23:26 +0100 Subject: [PATCH 1/7] Add Alarm Specification feature to auto scaling groups instance refresh preferences --- .changelog/36954.txt | 3 + internal/service/autoscaling/group.go | 30 ++++++ internal/service/autoscaling/group_test.go | 102 ++++++++++++++++++ .../docs/r/autoscaling_group.html.markdown | 2 + 4 files changed, 137 insertions(+) create mode 100644 .changelog/36954.txt diff --git a/.changelog/36954.txt b/.changelog/36954.txt new file mode 100644 index 00000000000..4b1fb3ff3bd --- /dev/null +++ b/.changelog/36954.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_autoscaling_group: Add `alarm_specification` to the `instance_refresh.preferences` configuration block +``` \ No newline at end of file diff --git a/internal/service/autoscaling/group.go b/internal/service/autoscaling/group.go index 27eafb9b4a9..41bbdc3a7c4 100644 --- a/internal/service/autoscaling/group.go +++ b/internal/service/autoscaling/group.go @@ -236,6 +236,18 @@ func resourceGroup() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "alarm_specification": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "alarms": { + Type: schema.TypeList, + }, + }, + }, + }, "checkpoint_delay": { Type: nullable.TypeNullableInt, Optional: true, @@ -3306,6 +3318,10 @@ func expandRefreshPreferences(tfMap map[string]interface{}) *awstypes.RefreshPre apiObject.AutoRollback = aws.Bool(v) } + if v, ok := tfMap["alarm_specification"].([]interface{}); ok && len(v) > 0 { + apiObject.AlarmSpecification = expandRefreshAlarmSpecification(v[0].(map[string]interface{})) + } + if v, ok := tfMap["checkpoint_delay"].(string); ok { if v, null, _ := nullable.Int(v).Value(); !null { apiObject.CheckpointDelay = aws.Int32(int32(v)) @@ -3345,6 +3361,20 @@ func expandRefreshPreferences(tfMap map[string]interface{}) *awstypes.RefreshPre return apiObject } +func expandRefreshAlarmSpecification(tfMap map[string]interface{}) *awstypes.AlarmSpecification { + if tfMap == nil { + return nil + } + + apiObject := &awstypes.AlarmSpecification{} + + if v, ok := tfMap["alarms"].([]interface{}); ok { + apiObject.Alarms = flex.ExpandStringValueList(v) + } + + return apiObject +} + func expandVPCZoneIdentifiers(tfList []interface{}) *string { vpcZoneIDs := make([]string, len(tfList)) diff --git a/internal/service/autoscaling/group_test.go b/internal/service/autoscaling/group_test.go index a29b8701b3a..63fd3ea4d86 100644 --- a/internal/service/autoscaling/group_test.go +++ b/internal/service/autoscaling/group_test.go @@ -1400,6 +1400,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1419,6 +1420,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1438,6 +1440,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1457,6 +1460,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1476,6 +1480,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1495,6 +1500,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", "25"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "5"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.0", "1"), @@ -1626,6 +1632,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_autoRollback(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "true"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1644,6 +1651,67 @@ func TestAccAutoScalingGroup_InstanceRefresh_autoRollback(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "true"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.min_healthy_percentage", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.scale_in_protected_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.skip_matching", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.standby_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.strategy", "Rolling"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.triggers.#", "0"), + ), + }, + }, + }) +} + +func TestAccAutoScalingGroup_InstanceRefresh_alarmSpecification(t *testing.T) { + ctx := acctest.Context(t) + var group awstypes.AutoScalingGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_autoscaling_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.AutoScalingServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccGroupConfig_instanceRefreshAlarmSpecification(rName, "t2.micro"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGroupExists(ctx, resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.#", "2"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.0", "my-alarm-1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.1", "my-alarm-2"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.min_healthy_percentage", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.scale_in_protected_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.skip_matching", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.standby_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.strategy", "Rolling"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.triggers.#", "0"), + ), + }, + { + Config: testAccGroupConfig_instanceRefreshAlarmSpecification(rName, "t3.micro"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGroupExists(ctx, resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.#", "2"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.0", "my-alarm-1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.1", "my-alarm-2"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -5268,6 +5336,40 @@ resource "aws_autoscaling_group" "test" { `, rName)) } +func testAccGroupConfig_instanceRefreshAlarmSpecification(rName, instanceType string) string { + return acctest.ConfigCompose(testAccGroupConfig_launchTemplateBase(rName, instanceType), fmt.Sprintf(` +resource "aws_autoscaling_group" "test" { + availability_zones = [data.aws_availability_zones.available.names[0]] + name = %[1]q + max_size = 2 + min_size = 1 + desired_capacity = 1 + + launch_template { + id = aws_launch_template.test.id + version = aws_launch_template.test.default_version + } + + instance_refresh { + strategy = "Rolling" + + preferences { + min_healthy_percentage = 0 + alarm_specification { + alarms = [ "my-alarm-1", "my-alarm-2" ] + } + } + } + + tag { + key = "Name" + value = %[1]q + propagate_at_launch = true + } +} +`, rName)) +} + func testAccGroupConfig_instanceRefreshFull(rName string) string { return acctest.ConfigCompose(testAccGroupConfig_launchConfigurationBase(rName, "t3.nano"), fmt.Sprintf(` resource "aws_autoscaling_group" "test" { diff --git a/website/docs/r/autoscaling_group.html.markdown b/website/docs/r/autoscaling_group.html.markdown index 80cf416c043..1ed55aab69d 100644 --- a/website/docs/r/autoscaling_group.html.markdown +++ b/website/docs/r/autoscaling_group.html.markdown @@ -666,6 +666,8 @@ This configuration block supports the following: - `min_healthy_percentage` - (Optional) Amount of capacity in the Auto Scaling group that must remain healthy during an instance refresh to allow the operation to continue, as a percentage of the desired capacity of the Auto Scaling group. Defaults to `90`. - `skip_matching` - (Optional) Replace instances that already have your desired configuration. Defaults to `false`. - `auto_rollback` - (Optional) Automatically rollback if instance refresh fails. Defaults to `false`. This option may only be set to `true` when specifying a `launch_template` or `mixed_instances_policy`. + - `alarm_specification` - (Optional) Alarm Specification for Instance Refresh. + - `alarms` - (Required) List of Cloudwatch alarms. If any of these alarms goes into ALARM state, Instance Refresh is failed. - `scale_in_protected_instances` - (Optional) Behavior when encountering instances protected from scale in are found. Available behaviors are `Refresh`, `Ignore`, and `Wait`. Default is `Ignore`. - `standby_instances` - (Optional) Behavior when encountering instances in the `Standby` state in are found. Available behaviors are `Terminate`, `Ignore`, and `Wait`. Default is `Ignore`. - `triggers` - (Optional) Set of additional property names that will trigger an Instance Refresh. A refresh will always be triggered by a change in any of `launch_configuration`, `launch_template`, or `mixed_instances_policy`. From 4099bffe53d9913aeafdca2b876a0fb3feda6cc9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Apr 2024 07:57:42 -0400 Subject: [PATCH 2/7] Fix terrafmt errors in acceptance test configuration. --- internal/service/autoscaling/group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/autoscaling/group_test.go b/internal/service/autoscaling/group_test.go index 63fd3ea4d86..a815bb21e89 100644 --- a/internal/service/autoscaling/group_test.go +++ b/internal/service/autoscaling/group_test.go @@ -5356,7 +5356,7 @@ resource "aws_autoscaling_group" "test" { preferences { min_healthy_percentage = 0 alarm_specification { - alarms = [ "my-alarm-1", "my-alarm-2" ] + alarms = [ "my-alarm-1", "my-alarm-2" ] } } } From 136f4754251c5af94b6a6f215c87ac439efdec19 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Apr 2024 07:58:53 -0400 Subject: [PATCH 3/7] Fix markdownlint 'MD007/ul-indent Unordered list indentation [Expected: 8; Actual: 6]'. --- website/docs/r/autoscaling_group.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/autoscaling_group.html.markdown b/website/docs/r/autoscaling_group.html.markdown index 1ed55aab69d..64f84a537bc 100644 --- a/website/docs/r/autoscaling_group.html.markdown +++ b/website/docs/r/autoscaling_group.html.markdown @@ -667,7 +667,7 @@ This configuration block supports the following: - `skip_matching` - (Optional) Replace instances that already have your desired configuration. Defaults to `false`. - `auto_rollback` - (Optional) Automatically rollback if instance refresh fails. Defaults to `false`. This option may only be set to `true` when specifying a `launch_template` or `mixed_instances_policy`. - `alarm_specification` - (Optional) Alarm Specification for Instance Refresh. - - `alarms` - (Required) List of Cloudwatch alarms. If any of these alarms goes into ALARM state, Instance Refresh is failed. + - `alarms` - (Required) List of Cloudwatch alarms. If any of these alarms goes into ALARM state, Instance Refresh is failed. - `scale_in_protected_instances` - (Optional) Behavior when encountering instances protected from scale in are found. Available behaviors are `Refresh`, `Ignore`, and `Wait`. Default is `Ignore`. - `standby_instances` - (Optional) Behavior when encountering instances in the `Standby` state in are found. Available behaviors are `Terminate`, `Ignore`, and `Wait`. Default is `Ignore`. - `triggers` - (Optional) Set of additional property names that will trigger an Instance Refresh. A refresh will always be triggered by a change in any of `launch_configuration`, `launch_template`, or `mixed_instances_policy`. From 538c779e50b40a4b4d63bd71e0c94f15a48d9ce0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Apr 2024 08:02:12 -0400 Subject: [PATCH 4/7] Cosmetics. --- internal/service/autoscaling/group.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/internal/service/autoscaling/group.go b/internal/service/autoscaling/group.go index 41bbdc3a7c4..cc04756a384 100644 --- a/internal/service/autoscaling/group.go +++ b/internal/service/autoscaling/group.go @@ -232,10 +232,6 @@ func resourceGroup() *schema.Resource { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "auto_rollback": { - Type: schema.TypeBool, - Optional: true, - }, "alarm_specification": { Type: schema.TypeList, MaxItems: 1, @@ -244,10 +240,17 @@ func resourceGroup() *schema.Resource { Schema: map[string]*schema.Schema{ "alarms": { Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, }, }, }, + "auto_rollback": { + Type: schema.TypeBool, + Optional: true, + }, "checkpoint_delay": { Type: nullable.TypeNullableInt, Optional: true, @@ -3314,12 +3317,12 @@ func expandRefreshPreferences(tfMap map[string]interface{}) *awstypes.RefreshPre apiObject := &awstypes.RefreshPreferences{} - if v, ok := tfMap["auto_rollback"].(bool); ok { - apiObject.AutoRollback = aws.Bool(v) + if v, ok := tfMap["alarm_specification"].([]interface{}); ok && len(v) > 0 { + apiObject.AlarmSpecification = expandAlarmSpecification(v[0].(map[string]interface{})) } - if v, ok := tfMap["alarm_specification"].([]interface{}); ok && len(v) > 0 { - apiObject.AlarmSpecification = expandRefreshAlarmSpecification(v[0].(map[string]interface{})) + if v, ok := tfMap["auto_rollback"].(bool); ok { + apiObject.AutoRollback = aws.Bool(v) } if v, ok := tfMap["checkpoint_delay"].(string); ok { @@ -3361,7 +3364,7 @@ func expandRefreshPreferences(tfMap map[string]interface{}) *awstypes.RefreshPre return apiObject } -func expandRefreshAlarmSpecification(tfMap map[string]interface{}) *awstypes.AlarmSpecification { +func expandAlarmSpecification(tfMap map[string]interface{}) *awstypes.AlarmSpecification { if tfMap == nil { return nil } From 8655a11f9dbd3319db3eb605286b03b0c2970e83 Mon Sep 17 00:00:00 2001 From: "siddhartha.yashwanth" Date: Wed, 17 Apr 2024 15:23:26 +0100 Subject: [PATCH 5/7] Add Alarm Specification feature to auto scaling groups instance refresh preferences --- .changelog/36954.txt | 3 + internal/service/autoscaling/group.go | 30 ++++++ internal/service/autoscaling/group_test.go | 102 ++++++++++++++++++ .../docs/r/autoscaling_group.html.markdown | 2 + 4 files changed, 137 insertions(+) create mode 100644 .changelog/36954.txt diff --git a/.changelog/36954.txt b/.changelog/36954.txt new file mode 100644 index 00000000000..4b1fb3ff3bd --- /dev/null +++ b/.changelog/36954.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_autoscaling_group: Add `alarm_specification` to the `instance_refresh.preferences` configuration block +``` \ No newline at end of file diff --git a/internal/service/autoscaling/group.go b/internal/service/autoscaling/group.go index 27eafb9b4a9..41bbdc3a7c4 100644 --- a/internal/service/autoscaling/group.go +++ b/internal/service/autoscaling/group.go @@ -236,6 +236,18 @@ func resourceGroup() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "alarm_specification": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "alarms": { + Type: schema.TypeList, + }, + }, + }, + }, "checkpoint_delay": { Type: nullable.TypeNullableInt, Optional: true, @@ -3306,6 +3318,10 @@ func expandRefreshPreferences(tfMap map[string]interface{}) *awstypes.RefreshPre apiObject.AutoRollback = aws.Bool(v) } + if v, ok := tfMap["alarm_specification"].([]interface{}); ok && len(v) > 0 { + apiObject.AlarmSpecification = expandRefreshAlarmSpecification(v[0].(map[string]interface{})) + } + if v, ok := tfMap["checkpoint_delay"].(string); ok { if v, null, _ := nullable.Int(v).Value(); !null { apiObject.CheckpointDelay = aws.Int32(int32(v)) @@ -3345,6 +3361,20 @@ func expandRefreshPreferences(tfMap map[string]interface{}) *awstypes.RefreshPre return apiObject } +func expandRefreshAlarmSpecification(tfMap map[string]interface{}) *awstypes.AlarmSpecification { + if tfMap == nil { + return nil + } + + apiObject := &awstypes.AlarmSpecification{} + + if v, ok := tfMap["alarms"].([]interface{}); ok { + apiObject.Alarms = flex.ExpandStringValueList(v) + } + + return apiObject +} + func expandVPCZoneIdentifiers(tfList []interface{}) *string { vpcZoneIDs := make([]string, len(tfList)) diff --git a/internal/service/autoscaling/group_test.go b/internal/service/autoscaling/group_test.go index a29b8701b3a..fd9b75e2cc6 100644 --- a/internal/service/autoscaling/group_test.go +++ b/internal/service/autoscaling/group_test.go @@ -1400,6 +1400,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1419,6 +1420,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1438,6 +1440,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1457,6 +1460,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1476,6 +1480,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1495,6 +1500,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", "25"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "5"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.0", "1"), @@ -1626,6 +1632,7 @@ func TestAccAutoScalingGroup_InstanceRefresh_autoRollback(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "true"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -1644,6 +1651,67 @@ func TestAccAutoScalingGroup_InstanceRefresh_autoRollback(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "true"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.min_healthy_percentage", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.scale_in_protected_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.skip_matching", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.standby_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.strategy", "Rolling"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.triggers.#", "0"), + ), + }, + }, + }) +} + +func TestAccAutoScalingGroup_InstanceRefresh_alarmSpecification(t *testing.T) { + ctx := acctest.Context(t) + var group awstypes.AutoScalingGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_autoscaling_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.AutoScalingServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccGroupConfig_instanceRefreshAlarmSpecification(rName, "t2.micro"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGroupExists(ctx, resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.#", "2"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.0", "my-alarm-1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.1", "my-alarm-2"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.min_healthy_percentage", "0"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.scale_in_protected_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.skip_matching", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.standby_instances", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.strategy", "Rolling"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.triggers.#", "0"), + ), + }, + { + Config: testAccGroupConfig_instanceRefreshAlarmSpecification(rName, "t3.micro"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGroupExists(ctx, resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.auto_rollback", "false"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.#", "2"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.0", "my-alarm-1"), + resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.alarm_specification.0.alarms.1", "my-alarm-2"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_delay", ""), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.checkpoint_percentages.#", "0"), resource.TestCheckResourceAttr(resourceName, "instance_refresh.0.preferences.0.instance_warmup", ""), @@ -5268,6 +5336,40 @@ resource "aws_autoscaling_group" "test" { `, rName)) } +func testAccGroupConfig_instanceRefreshAlarmSpecification(rName, instanceType string) string { + return acctest.ConfigCompose(testAccGroupConfig_launchTemplateBase(rName, instanceType), fmt.Sprintf(` +resource "aws_autoscaling_group" "test" { + availability_zones = [data.aws_availability_zones.available.names[0]] + name = %[1]q + max_size = 2 + min_size = 1 + desired_capacity = 1 + + launch_template { + id = aws_launch_template.test.id + version = aws_launch_template.test.default_version + } + + instance_refresh { + strategy = "Rolling" + + preferences { + min_healthy_percentage = 0 + alarm_specification { + alarms = ["my-alarm-1", "my-alarm-2"] + } + } + } + + tag { + key = "Name" + value = %[1]q + propagate_at_launch = true + } +} +`, rName)) +} + func testAccGroupConfig_instanceRefreshFull(rName string) string { return acctest.ConfigCompose(testAccGroupConfig_launchConfigurationBase(rName, "t3.nano"), fmt.Sprintf(` resource "aws_autoscaling_group" "test" { diff --git a/website/docs/r/autoscaling_group.html.markdown b/website/docs/r/autoscaling_group.html.markdown index 80cf416c043..1ed55aab69d 100644 --- a/website/docs/r/autoscaling_group.html.markdown +++ b/website/docs/r/autoscaling_group.html.markdown @@ -666,6 +666,8 @@ This configuration block supports the following: - `min_healthy_percentage` - (Optional) Amount of capacity in the Auto Scaling group that must remain healthy during an instance refresh to allow the operation to continue, as a percentage of the desired capacity of the Auto Scaling group. Defaults to `90`. - `skip_matching` - (Optional) Replace instances that already have your desired configuration. Defaults to `false`. - `auto_rollback` - (Optional) Automatically rollback if instance refresh fails. Defaults to `false`. This option may only be set to `true` when specifying a `launch_template` or `mixed_instances_policy`. + - `alarm_specification` - (Optional) Alarm Specification for Instance Refresh. + - `alarms` - (Required) List of Cloudwatch alarms. If any of these alarms goes into ALARM state, Instance Refresh is failed. - `scale_in_protected_instances` - (Optional) Behavior when encountering instances protected from scale in are found. Available behaviors are `Refresh`, `Ignore`, and `Wait`. Default is `Ignore`. - `standby_instances` - (Optional) Behavior when encountering instances in the `Standby` state in are found. Available behaviors are `Terminate`, `Ignore`, and `Wait`. Default is `Ignore`. - `triggers` - (Optional) Set of additional property names that will trigger an Instance Refresh. A refresh will always be triggered by a change in any of `launch_configuration`, `launch_template`, or `mixed_instances_policy`. From 7770c44524973110be38ec3780459ce667bfcb8f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Apr 2024 08:11:48 -0400 Subject: [PATCH 6/7] Fix terrafmt errors in acceptance test configuration. --- internal/service/autoscaling/group_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/service/autoscaling/group_test.go b/internal/service/autoscaling/group_test.go index a815bb21e89..d03daf98130 100644 --- a/internal/service/autoscaling/group_test.go +++ b/internal/service/autoscaling/group_test.go @@ -5355,8 +5355,9 @@ resource "aws_autoscaling_group" "test" { preferences { min_healthy_percentage = 0 + alarm_specification { - alarms = [ "my-alarm-1", "my-alarm-2" ] + alarms = ["my-alarm-1", "my-alarm-2"] } } } From 16fc36ac7c2d8bbc2bfcbc70433ad04968335e9d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Apr 2024 08:12:52 -0400 Subject: [PATCH 7/7] Fix 'resource aws_autoscaling_group: alarms: One of optional, required, or computed must be set'. --- internal/service/autoscaling/group.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/service/autoscaling/group.go b/internal/service/autoscaling/group.go index cc04756a384..dec7c42cf1e 100644 --- a/internal/service/autoscaling/group.go +++ b/internal/service/autoscaling/group.go @@ -239,7 +239,8 @@ func resourceGroup() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "alarms": { - Type: schema.TypeList, + Type: schema.TypeList, + Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, },