Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Set instance protection can fail as instance can be already deleted from Autoscaling Group #35071

Merged
3 changes: 3 additions & 0 deletions .changelog/35071.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_autoscaling_group: Fix `ValidationError: The instance ... is not part of Auto Scaling group ...` errors on resource Delete when disabling scale-in protection for instances that are already fully terminated
```
8 changes: 4 additions & 4 deletions internal/service/autoscaling/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func resourceAttachmentCreate(ctx context.Context, d *schema.ResourceData, meta
return conn.AttachLoadBalancersWithContext(ctx, input)
},
// ValidationError: Trying to update too many Load Balancers/Target Groups at once. The limit is 10
ErrCodeValidationError, "update too many")
errCodeValidationError, "update too many")

if err != nil {
return sdkdiag.AppendErrorf(diags, "attaching Auto Scaling Group (%s) load balancer (%s): %s", asgName, lbName, err)
Expand All @@ -80,7 +80,7 @@ func resourceAttachmentCreate(ctx context.Context, d *schema.ResourceData, meta
func() (interface{}, error) {
return conn.AttachLoadBalancerTargetGroupsWithContext(ctx, input)
},
ErrCodeValidationError, "update too many")
errCodeValidationError, "update too many")

if err != nil {
return sdkdiag.AppendErrorf(diags, "attaching Auto Scaling Group (%s) target group (%s): %s", asgName, d.Get("lb_target_group_arn").(string), err)
Expand Down Expand Up @@ -135,7 +135,7 @@ func resourceAttachmentDelete(ctx context.Context, d *schema.ResourceData, meta
func() (interface{}, error) {
return conn.DetachLoadBalancersWithContext(ctx, input)
},
ErrCodeValidationError, "update too many")
errCodeValidationError, "update too many")

if err != nil {
return sdkdiag.AppendErrorf(diags, "detaching Auto Scaling Group (%s) load balancer (%s): %s", asgName, lbName, err)
Expand All @@ -150,7 +150,7 @@ func resourceAttachmentDelete(ctx context.Context, d *schema.ResourceData, meta
func() (interface{}, error) {
return conn.DetachLoadBalancerTargetGroupsWithContext(ctx, input)
},
ErrCodeValidationError, "update too many")
errCodeValidationError, "update too many")

if err != nil {
return sdkdiag.AppendErrorf(diags, "detaching Auto Scaling Group (%s) target group (%s): %s", asgName, d.Get("lb_target_group_arn").(string), err)
Expand Down
4 changes: 2 additions & 2 deletions internal/service/autoscaling/attachment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ resource "aws_autoscaling_group" "test" {

func testAccAttachmentConfig_targetGroupBase(rName string, targetGroupCount int) string {
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
acctest.ConfigVPCWithSubnets(rName, 1),
fmt.Sprintf(`
resource "aws_lb_target_group" "test" {
Expand All @@ -231,7 +231,7 @@ resource "aws_lb_target_group" "test" {

resource "aws_launch_configuration" "test" {
name = %[1]q
image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
image_id = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = "t2.micro"
}

Expand Down
2 changes: 1 addition & 1 deletion internal/service/autoscaling/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
package autoscaling

const (
ErrCodeValidationError = "ValidationError"
errCodeValidationError = "ValidationError"
)
62 changes: 31 additions & 31 deletions internal/service/autoscaling/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import ( // nosemgrep:ci.semgrep.aws.multiple-service-imports
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb"
"github.com/hashicorp/terraform-provider-aws/internal/slices"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/types/nullable"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
Expand Down Expand Up @@ -1121,7 +1121,7 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
return conn.CreateAutoScalingGroupWithContext(ctx, createInput)
},
// ValidationError: You must use a valid fully-formed launch template. Value (tf-acc-test-6643732652421074386) for parameter iamInstanceProfile.name is invalid. Invalid IAM Instance Profile name
ErrCodeValidationError, "Invalid IAM Instance Profile")
errCodeValidationError, "Invalid IAM Instance Profile")

if err != nil {
return sdkdiag.AppendErrorf(diags, "creating Auto Scaling Group (%s): %s", asgName, err)
Expand All @@ -1135,7 +1135,7 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
func() (interface{}, error) {
return conn.PutLifecycleHookWithContext(ctx, input)
},
ErrCodeValidationError, "Unable to publish test message to notification target")
errCodeValidationError, "Unable to publish test message to notification target")

if err != nil {
return sdkdiag.AppendErrorf(diags, "creating Auto Scaling Group (%s) Lifecycle Hook: %s", d.Id(), err)
Expand Down Expand Up @@ -1476,7 +1476,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter

// API only supports adding or removing 10 at a time.
batchSize := 10
for _, chunk := range slices.Chunks(expandTrafficSourceIdentifiers(os.Difference(ns).List()), batchSize) {
for _, chunk := range tfslices.Chunks(expandTrafficSourceIdentifiers(os.Difference(ns).List()), batchSize) {
_, err := conn.DetachTrafficSourcesWithContext(ctx, &autoscaling.DetachTrafficSourcesInput{
AutoScalingGroupName: aws.String(d.Id()),
TrafficSources: chunk,
Expand All @@ -1491,7 +1491,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
}
}

for _, chunk := range slices.Chunks(expandTrafficSourceIdentifiers(ns.Difference(os).List()), batchSize) {
for _, chunk := range tfslices.Chunks(expandTrafficSourceIdentifiers(ns.Difference(os).List()), batchSize) {
_, err := conn.AttachTrafficSourcesWithContext(ctx, &autoscaling.AttachTrafficSourcesInput{
AutoScalingGroupName: aws.String(d.Id()),
TrafficSources: chunk,
Expand Down Expand Up @@ -1520,7 +1520,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter

// API only supports adding or removing 10 at a time.
batchSize := 10
for _, chunk := range slices.Chunks(flex.ExpandStringSet(os.Difference(ns)), batchSize) {
for _, chunk := range tfslices.Chunks(flex.ExpandStringSet(os.Difference(ns)), batchSize) {
_, err := conn.DetachLoadBalancersWithContext(ctx, &autoscaling.DetachLoadBalancersInput{
AutoScalingGroupName: aws.String(d.Id()),
LoadBalancerNames: chunk,
Expand All @@ -1535,7 +1535,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
}
}

for _, chunk := range slices.Chunks(flex.ExpandStringSet(ns.Difference(os)), batchSize) {
for _, chunk := range tfslices.Chunks(flex.ExpandStringSet(ns.Difference(os)), batchSize) {
_, err := conn.AttachLoadBalancersWithContext(ctx, &autoscaling.AttachLoadBalancersInput{
AutoScalingGroupName: aws.String(d.Id()),
LoadBalancerNames: chunk,
Expand Down Expand Up @@ -1564,7 +1564,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter

// API only supports adding or removing 10 at a time.
batchSize := 10
for _, chunk := range slices.Chunks(flex.ExpandStringSet(os.Difference(ns)), batchSize) {
for _, chunk := range tfslices.Chunks(flex.ExpandStringSet(os.Difference(ns)), batchSize) {
_, err := conn.DetachLoadBalancerTargetGroupsWithContext(ctx, &autoscaling.DetachLoadBalancerTargetGroupsInput{
AutoScalingGroupName: aws.String(d.Id()),
TargetGroupARNs: chunk,
Expand All @@ -1579,7 +1579,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
}
}

for _, chunk := range slices.Chunks(flex.ExpandStringSet(ns.Difference(os)), batchSize) {
for _, chunk := range tfslices.Chunks(flex.ExpandStringSet(ns.Difference(os)), batchSize) {
_, err := conn.AttachLoadBalancerTargetGroupsWithContext(ctx, &autoscaling.AttachLoadBalancerTargetGroupsInput{
AutoScalingGroupName: aws.String(d.Id()),
TargetGroupARNs: chunk,
Expand Down Expand Up @@ -1803,7 +1803,7 @@ func resourceGroupDelete(ctx context.Context, d *schema.ResourceData, meta inter
},
autoscaling.ErrCodeResourceInUseFault, autoscaling.ErrCodeScalingActivityInProgressFault)

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "not found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") {
return diags
}

Expand Down Expand Up @@ -1844,26 +1844,26 @@ func drainGroup(ctx context.Context, conn *autoscaling.AutoScaling, name string,
// no longer applies scale-in protection to new instances, but there's still
// old ones that have it.

const chunkSize = 50 // API limit.
for i, n := 0, len(instances); i < n; i += chunkSize {
j := i + chunkSize
if j > n {
j = n
}

var instanceIDs []string

for k := i; k < j; k++ {
instanceIDs = append(instanceIDs, aws.StringValue(instances[k].InstanceId))
}

const batchSize = 50 // API limit.
for _, chunk := range tfslices.Chunks(instances, batchSize) {
instanceIDs := tfslices.ApplyToAll(chunk, func(v *autoscaling.Instance) string {
return aws.StringValue(v.InstanceId)
})
input := &autoscaling.SetInstanceProtectionInput{
AutoScalingGroupName: aws.String(name),
InstanceIds: aws.StringSlice(instanceIDs),
ProtectedFromScaleIn: aws.Bool(false),
}

if _, err := conn.SetInstanceProtectionWithContext(ctx, input); err != nil {
_, err := conn.SetInstanceProtectionWithContext(ctx, input)

// Ignore ValidationError when instance is already fully terminated
// and is not a part of Auto Scaling Group anymore.
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not part of Auto Scaling group") {
continue
}

if err != nil {
return fmt.Errorf("disabling Auto Scaling Group (%s) scale-in protections: %w", name, err)
}
}
Expand Down Expand Up @@ -1892,7 +1892,7 @@ func deleteWarmPool(ctx context.Context, conn *autoscaling.AutoScaling, name str
},
autoscaling.ErrCodeResourceInUseFault, autoscaling.ErrCodeScalingActivityInProgressFault)

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "No warm pool found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "No warm pool found") {
return nil
}

Expand Down Expand Up @@ -2100,7 +2100,7 @@ func FindInstanceRefreshes(ctx context.Context, conn *autoscaling.AutoScaling, i
return !lastPage
})

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "not found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
Expand Down Expand Up @@ -2136,7 +2136,7 @@ func findLoadBalancerStates(ctx context.Context, conn *autoscaling.AutoScaling,
return !lastPage
})

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "not found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
Expand Down Expand Up @@ -2172,7 +2172,7 @@ func findLoadBalancerTargetGroupStates(ctx context.Context, conn *autoscaling.Au
return !lastPage
})

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "not found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
Expand Down Expand Up @@ -2207,7 +2207,7 @@ func findScalingActivities(ctx context.Context, conn *autoscaling.AutoScaling, i
return !lastPage
})

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "not found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
Expand Down Expand Up @@ -2248,7 +2248,7 @@ func findTrafficSourceStates(ctx context.Context, conn *autoscaling.AutoScaling,
return !lastPage
})

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "not found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
Expand Down Expand Up @@ -2293,7 +2293,7 @@ func findWarmPool(ctx context.Context, conn *autoscaling.AutoScaling, name strin
return !lastPage
})

if tfawserr.ErrMessageContains(err, ErrCodeValidationError, "not found") {
if tfawserr.ErrMessageContains(err, errCodeValidationError, "not found") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
Expand Down
20 changes: 10 additions & 10 deletions internal/service/autoscaling/group_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func TestAccAutoScalingGroupDataSource_tags(t *testing.T) {

func testAccGroupDataSourceConfig_basic(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
acctest.ConfigAvailableAZsNoOptIn(),
acctest.AvailableEC2InstanceTypeForAvailabilityZone("data.aws_availability_zones.available.names[0]", "t3.micro", "t2.micro"),
fmt.Sprintf(`
Expand Down Expand Up @@ -229,15 +229,15 @@ resource "aws_autoscaling_group" "no_match" {

resource "aws_launch_configuration" "test" {
name = %[1]q
image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
image_id = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = data.aws_ec2_instance_type_offering.available.instance_type
}
`, rName))
}

func testAccGroupDataSourceConfig_launchTemplate(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
acctest.ConfigAvailableAZsNoOptIn(),
acctest.AvailableEC2InstanceTypeForAvailabilityZone("data.aws_availability_zones.available.names[0]", "t3.micro", "t2.micro"),
fmt.Sprintf(`
Expand All @@ -260,15 +260,15 @@ resource "aws_autoscaling_group" "test" {

resource "aws_launch_template" "test" {
name = %[1]q
image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
image_id = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = data.aws_ec2_instance_type_offering.available.instance_type
}
`, rName))
}

func testAccGroupDataSourceConfig_mixedInstancesPolicy(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
acctest.ConfigAvailableAZsNoOptIn(),
acctest.AvailableEC2InstanceTypeForAvailabilityZone("data.aws_availability_zones.available.names[0]", "t3.micro", "t2.micro"),
fmt.Sprintf(`
Expand Down Expand Up @@ -313,15 +313,15 @@ resource "aws_autoscaling_group" "test" {

resource "aws_launch_template" "test" {
name = %[1]q
image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
image_id = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = data.aws_ec2_instance_type_offering.available.instance_type
}
`, rName))
}

func testAccGroupDataSourceConfig_warmPool(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
acctest.ConfigAvailableAZsNoOptIn(),
acctest.AvailableEC2InstanceTypeForAvailabilityZone("data.aws_availability_zones.available.names[0]", "t3.micro", "t2.micro"),
fmt.Sprintf(`
Expand Down Expand Up @@ -353,15 +353,15 @@ resource "aws_autoscaling_group" "test" {

resource "aws_launch_template" "test" {
name = %[1]q
image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
image_id = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = data.aws_ec2_instance_type_offering.available.instance_type
}
`, rName))
}

func testAccGroupDataSourceConfig_tags(rName string) string {
return acctest.ConfigCompose(
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
acctest.ConfigAvailableAZsNoOptIn(),
acctest.AvailableEC2InstanceTypeForAvailabilityZone("data.aws_availability_zones.available.names[0]", "t3.micro", "t2.micro"),
fmt.Sprintf(`
Expand Down Expand Up @@ -396,7 +396,7 @@ resource "aws_autoscaling_group" "test" {

resource "aws_launch_template" "test" {
name = %[1]q
image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
image_id = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = data.aws_ec2_instance_type_offering.available.instance_type
}
`, rName))
Expand Down
4 changes: 2 additions & 2 deletions internal/service/autoscaling/group_tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ func testAccCheckGroupTagExists(ctx context.Context, n string) resource.TestChec
func testAccGroupTagConfig_basic(key string, value string) string {
return acctest.ConfigCompose(
acctest.ConfigAvailableAZsNoOptInDefaultExclude(),
acctest.ConfigLatestAmazonLinuxHVMEBSAMI(),
acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI(),
fmt.Sprintf(`
resource "aws_launch_template" "test" {
name_prefix = "terraform-test-"
image_id = data.aws_ami.amzn-ami-minimal-hvm-ebs.id
image_id = data.aws_ami.amzn2-ami-minimal-hvm-ebs-x86_64.id
instance_type = "t2.nano"
}

Expand Down
Loading
Loading