Skip to content

Commit

Permalink
Merge pull request #34430 from sasidhar-aws/f-aws_autoscaling_group_m…
Browse files Browse the repository at this point in the history
…aintenance_policy

r/aws_autoscaling_group Instance maintenance policy enhancement
  • Loading branch information
ewbankkit authored Nov 16, 2023
2 parents c386a9b + 7354a50 commit dfd0f9b
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 2 deletions.
7 changes: 7 additions & 0 deletions .changelog/34430.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_autoscaling_group: Add `instance_maintenance_policy` configuration block
```

```release-note:enhancement
data-source/aws_autoscaling_group: Add `instance_maintenance_policy` attribute
```
86 changes: 86 additions & 0 deletions internal/service/autoscaling/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,37 @@ func ResourceGroup() *schema.Resource {
},
},
},
"instance_maintenance_policy": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
DiffSuppressFunc: instanceMaintenancePolicyDiffSupress,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"max_healthy_percentage": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.Any(
validation.IntBetween(100, 200),
validation.IntBetween(-1, -1),
),
// When value is -1, instance maintenance policy is removed, state file will not contain any value.
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return old == "" && new == "-1"
},
},
"min_healthy_percentage": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(-1, 100),
// When value is -1, instance maintenance policy is removed, state file will not contain any value.
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return old == "" && new == "-1"
},
},
},
},
},
"instance_refresh": {
Type: schema.TypeList,
MaxItems: 1,
Expand Down Expand Up @@ -892,6 +923,20 @@ func ResourceGroup() *schema.Resource {
}
}

func instanceMaintenancePolicyDiffSupress(k, old, new string, d *schema.ResourceData) bool {
o, n := d.GetChange("instance_maintenance_policy")
oList := o.([]interface{})
nList := n.([]interface{})

if len(oList) == 0 && len(nList) != 0 {
tfMap := nList[0].(map[string]interface{})
if int64(tfMap["min_healthy_percentage"].(int)) == -1 || int64(tfMap["max_healthy_percentage"].(int)) == -1 {
return true
}
}
return false
}

func launchTemplateCustomDiff(baseAttribute, subAttribute string) schema.CustomizeDiffFunc {
return func(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error {
if diff.HasChange(subAttribute) {
Expand Down Expand Up @@ -1019,6 +1064,10 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
createInput.HealthCheckGracePeriod = aws.Int64(int64(v.(int)))
}

if v, ok := d.GetOk("instance_maintenance_policy"); ok {
createInput.InstanceMaintenancePolicy = expandInstanceMaintenancePolicy(v.([]interface{}))
}

if v, ok := d.GetOk("launch_configuration"); ok {
createInput.LaunchConfigurationName = aws.String(v.(string))
}
Expand Down Expand Up @@ -1203,6 +1252,9 @@ func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interfa
}
d.Set("health_check_grace_period", g.HealthCheckGracePeriod)
d.Set("health_check_type", g.HealthCheckType)
if err := d.Set("instance_maintenance_policy", flattenInstanceMaintenancePolicy(g.InstanceMaintenancePolicy)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting instance_maintenance_policy: %s", err)
}
d.Set("launch_configuration", g.LaunchConfigurationName)
if g.LaunchTemplate != nil {
if err := d.Set("launch_template", []interface{}{flattenLaunchTemplateSpecification(g.LaunchTemplate)}); err != nil {
Expand Down Expand Up @@ -1332,6 +1384,10 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
input.HealthCheckType = aws.String(d.Get("health_check_type").(string))
}

if d.HasChange("instance_maintenance_policy") {
input.InstanceMaintenancePolicy = expandInstanceMaintenancePolicy(d.Get("instance_maintenance_policy").([]interface{}))
}

if d.HasChange("launch_configuration") {
if v, ok := d.GetOk("launch_configuration"); ok {
input.LaunchConfigurationName = aws.String(v.(string))
Expand Down Expand Up @@ -3481,6 +3537,36 @@ func flattenInstancesDistribution(apiObject *autoscaling.InstancesDistribution)
return tfMap
}

func expandInstanceMaintenancePolicy(l []interface{}) *autoscaling.InstanceMaintenancePolicy {
if len(l) == 0 {
//Empty InstanceMaintenancePolicy block will reset already assigned values
return &autoscaling.InstanceMaintenancePolicy{
MinHealthyPercentage: aws.Int64(-1),
MaxHealthyPercentage: aws.Int64(-1),
}
}

tfMap := l[0].(map[string]interface{})

return &autoscaling.InstanceMaintenancePolicy{
MinHealthyPercentage: aws.Int64(int64(tfMap["min_healthy_percentage"].(int))),
MaxHealthyPercentage: aws.Int64(int64(tfMap["max_healthy_percentage"].(int))),
}
}

func flattenInstanceMaintenancePolicy(instanceMaintenancePolicy *autoscaling.InstanceMaintenancePolicy) []interface{} {
if instanceMaintenancePolicy == nil {
return []interface{}{}
}

m := map[string]interface{}{
"min_healthy_percentage": instanceMaintenancePolicy.MinHealthyPercentage,
"max_healthy_percentage": instanceMaintenancePolicy.MaxHealthyPercentage,
}

return []interface{}{m}
}

func flattenLaunchTemplate(apiObject *autoscaling.LaunchTemplate) map[string]interface{} {
if apiObject == nil {
return nil
Expand Down
19 changes: 19 additions & 0 deletions internal/service/autoscaling/group_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ func DataSourceGroup() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"instance_maintenance_policy": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"max_healthy_percentage": {
Type: schema.TypeInt,
Computed: true,
},
"min_healthy_percentage": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
"launch_configuration": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -577,6 +593,9 @@ func dataSourceGroupRead(ctx context.Context, d *schema.ResourceData, meta inter
d.Set("enabled_metrics", flattenEnabledMetrics(group.EnabledMetrics))
d.Set("health_check_grace_period", group.HealthCheckGracePeriod)
d.Set("health_check_type", group.HealthCheckType)
if err := d.Set("instance_maintenance_policy", flattenInstanceMaintenancePolicy(group.InstanceMaintenancePolicy)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting instance_maintenance_policy: %s", err)
}
d.Set("launch_configuration", group.LaunchConfigurationName)
if group.LaunchTemplate != nil {
if err := d.Set("launch_template", []interface{}{flattenLaunchTemplateSpecification(group.LaunchTemplate)}); err != nil {
Expand Down
11 changes: 9 additions & 2 deletions internal/service/autoscaling/group_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func TestAccAutoScalingGroupDataSource_basic(t *testing.T) {
resource.TestCheckResourceAttrPair(datasourceName, "enabled_metrics.#", resourceName, "enabled_metrics.#"),
resource.TestCheckResourceAttrPair(datasourceName, "health_check_grace_period", resourceName, "health_check_grace_period"),
resource.TestCheckResourceAttrPair(datasourceName, "health_check_type", resourceName, "health_check_type"),
resource.TestCheckResourceAttrPair(datasourceName, "instance_maintenance_policy.#", resourceName, "instance_maintenance_policy.#"),
resource.TestCheckResourceAttrPair(datasourceName, "instance_maintenance_policy.0.min_healthy_percentage", resourceName, "instance_maintenance_policy.0.min_healthy_percentage"),
resource.TestCheckResourceAttrPair(datasourceName, "instance_maintenance_policy.0.max_healthy_percentage", resourceName, "instance_maintenance_policy.0.max_healthy_percentage"),
resource.TestCheckResourceAttrPair(datasourceName, "launch_configuration", resourceName, "launch_configuration"),
resource.TestCheckResourceAttrPair(datasourceName, "launch_template.#", resourceName, "launch_template.#"),
resource.TestCheckResourceAttrPair(datasourceName, "load_balancers.#", resourceName, "load_balancers.#"),
Expand Down Expand Up @@ -203,8 +206,12 @@ resource "aws_autoscaling_group" "test" {
desired_capacity = 0
enabled_metrics = ["GroupDesiredCapacity"]
force_delete = true
launch_configuration = aws_launch_configuration.test.name
availability_zones = [data.aws_availability_zones.available.names[0], data.aws_availability_zones.available.names[1]]
instance_maintenance_policy {
min_healthy_percentage = 90
max_healthy_percentage = 120
}
launch_configuration = aws_launch_configuration.test.name
availability_zones = [data.aws_availability_zones.available.names[0], data.aws_availability_zones.available.names[1]]
}
resource "aws_autoscaling_group" "no_match" {
Expand Down
161 changes: 161 additions & 0 deletions internal/service/autoscaling/group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func TestAccAutoScalingGroup_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "health_check_grace_period", "300"),
resource.TestCheckResourceAttr(resourceName, "health_check_type", "EC2"),
resource.TestCheckResourceAttr(resourceName, "initial_lifecycle_hook.#", "0"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "0"),
resource.TestCheckResourceAttr(resourceName, "instance_refresh.#", "0"),
resource.TestCheckResourceAttrPair(resourceName, "launch_configuration", "aws_launch_configuration.test", "name"),
resource.TestCheckResourceAttr(resourceName, "launch_template.#", "0"),
Expand Down Expand Up @@ -474,6 +475,150 @@ func TestAccAutoScalingGroup_vpcUpdates(t *testing.T) {
})
}

func TestAccAutoScalingGroup_withInstanceMaintenancePolicyAfterCreation(t *testing.T) {
ctx := acctest.Context(t)
var group autoscaling.Group
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, autoscaling.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGroupDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccGroupConfig_simple(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "0"),
),
},
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, 90, 120),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.min_healthy_percentage", "90"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.max_healthy_percentage", "120"),
),
},
// To validate update instance_maintenance_policy argument
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, 80, 130),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.min_healthy_percentage", "80"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.max_healthy_percentage", "130"),
),
},
//To validate removing the instance_maintenance_policy argument
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, -1, -1),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "0"),
),
},
{
Config: testAccGroupConfig_simple(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "0"),
),
},
testAccGroupImportStep(resourceName),
},
})
}

func TestAccAutoScalingGroup_withInstanceMaintenancePolicyAtCreation(t *testing.T) {
ctx := acctest.Context(t)
var group autoscaling.Group
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, autoscaling.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGroupDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, 90, 120),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.min_healthy_percentage", "90"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.max_healthy_percentage", "120"),
),
},
//To validate update instance_maintenance_policy min_healthy_percentage argument
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, 90, 130),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.min_healthy_percentage", "90"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.max_healthy_percentage", "130"),
),
},
//To validate update instance_maintenance_policy max_healthy_percentage argument
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, 80, 130),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.min_healthy_percentage", "80"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.max_healthy_percentage", "130"),
),
},
{
Config: testAccGroupConfig_simple(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "0"),
),
},
testAccGroupImportStep(resourceName),
},
})
}

func TestAccAutoScalingGroup_withInstanceMaintenancePolicyNegativeValues(t *testing.T) {
ctx := acctest.Context(t)
var group autoscaling.Group
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, autoscaling.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckGroupDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, -1, -1),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "0"),
),
},
{
Config: testAccGroupConfig_instanceMaintenancePolicy(rName, 90, 120),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupExists(ctx, resourceName, &group),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.#", "1"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.min_healthy_percentage", "90"),
resource.TestCheckResourceAttr(resourceName, "instance_maintenance_policy.0.max_healthy_percentage", "120"),
),
},
testAccGroupImportStep(resourceName),
},
})
}

func TestAccAutoScalingGroup_withLoadBalancer(t *testing.T) {
ctx := acctest.Context(t)
var group autoscaling.Group
Expand Down Expand Up @@ -3999,6 +4144,22 @@ resource "aws_autoscaling_group" "test" {
`, rName, defaultInstanceWarmup))
}

func testAccGroupConfig_instanceMaintenancePolicy(rName string, min_percentage int, max_percentage int) string {
return acctest.ConfigCompose(testAccGroupConfig_launchConfigurationBase(rName, "t2.micro"), fmt.Sprintf(`
resource "aws_autoscaling_group" "test" {
availability_zones = [data.aws_availability_zones.available.names[0]]
max_size = 0
min_size = 0
name = %[1]q
instance_maintenance_policy {
min_healthy_percentage = %[2]d
max_healthy_percentage = %[3]d
}
launch_configuration = aws_launch_configuration.test.name
}
`, rName, min_percentage, max_percentage))
}

func testAccGroupConfig_nameGenerated(rName string) string {
return acctest.ConfigCompose(testAccGroupConfig_launchConfigurationBase(rName, "t2.micro"), `
resource "aws_autoscaling_group" "test" {
Expand Down
3 changes: 3 additions & 0 deletions website/docs/d/autoscaling_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ interpolation.
* `health_check_grace_period` - The amount of time, in seconds, that Amazon EC2 Auto Scaling waits before checking the health status of an EC2 instance that has come into service.
* `health_check_type` - Service to use for the health checks. The valid values are EC2 and ELB.
* `id` - Name of the Auto Scaling Group.
* `instance_maintenance_policy` - Instance maintenance policy for the group.
* `min_healthy_percentage` - Specifies the lower limit on the number of instances that must be in the InService state with a healthy status during an instance replacement activity.
* `max_healthy_percentage` - Specifies the upper limit on the number of instances that are in the InService or Pending state with a healthy status during an instance replacement activity.
* `launch_configuration` - The name of the associated launch configuration.
* `launch_template` - List of launch templates for the group.
* `id` - ID of the launch template.
Expand Down
Loading

0 comments on commit dfd0f9b

Please sign in to comment.