github.com/meteor/terraform@v0.6.15-0.20210412225145-79ec4bc057c6/builtin/providers/aws/resource_aws_autoscaling_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/hashicorp/errwrap"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	"github.com/aws/aws-sdk-go/aws/awserr"
    15  	"github.com/aws/aws-sdk-go/service/autoscaling"
    16  	"github.com/aws/aws-sdk-go/service/elb"
    17  	"github.com/aws/aws-sdk-go/service/elbv2"
    18  )
    19  
    20  func resourceAwsAutoscalingGroup() *schema.Resource {
    21  	return &schema.Resource{
    22  		Create: resourceAwsAutoscalingGroupCreate,
    23  		Read:   resourceAwsAutoscalingGroupRead,
    24  		Update: resourceAwsAutoscalingGroupUpdate,
    25  		Delete: resourceAwsAutoscalingGroupDelete,
    26  		Importer: &schema.ResourceImporter{
    27  			State: schema.ImportStatePassthrough,
    28  		},
    29  
    30  		Schema: map[string]*schema.Schema{
    31  			"name": &schema.Schema{
    32  				Type:          schema.TypeString,
    33  				Optional:      true,
    34  				Computed:      true,
    35  				ForceNew:      true,
    36  				ConflictsWith: []string{"name_prefix"},
    37  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    38  					// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1862-L1873
    39  					value := v.(string)
    40  					if len(value) > 255 {
    41  						errors = append(errors, fmt.Errorf(
    42  							"%q cannot be longer than 255 characters", k))
    43  					}
    44  					return
    45  				},
    46  			},
    47  			"name_prefix": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				ForceNew: true,
    51  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    52  					value := v.(string)
    53  					if len(value) > 229 {
    54  						errors = append(errors, fmt.Errorf(
    55  							"%q cannot be longer than 229 characters, name is limited to 255", k))
    56  					}
    57  					return
    58  				},
    59  			},
    60  
    61  			"launch_configuration": {
    62  				Type:     schema.TypeString,
    63  				Required: true,
    64  			},
    65  
    66  			"desired_capacity": {
    67  				Type:     schema.TypeInt,
    68  				Optional: true,
    69  				Computed: true,
    70  			},
    71  
    72  			"min_elb_capacity": {
    73  				Type:     schema.TypeInt,
    74  				Optional: true,
    75  			},
    76  
    77  			"min_size": {
    78  				Type:     schema.TypeInt,
    79  				Required: true,
    80  			},
    81  
    82  			"max_size": {
    83  				Type:     schema.TypeInt,
    84  				Required: true,
    85  			},
    86  
    87  			"default_cooldown": {
    88  				Type:     schema.TypeInt,
    89  				Optional: true,
    90  				Computed: true,
    91  			},
    92  
    93  			"force_delete": {
    94  				Type:     schema.TypeBool,
    95  				Optional: true,
    96  				Default:  false,
    97  			},
    98  
    99  			"health_check_grace_period": {
   100  				Type:     schema.TypeInt,
   101  				Optional: true,
   102  				Default:  300,
   103  			},
   104  
   105  			"health_check_type": {
   106  				Type:     schema.TypeString,
   107  				Optional: true,
   108  				Computed: true,
   109  			},
   110  
   111  			"availability_zones": {
   112  				Type:     schema.TypeSet,
   113  				Optional: true,
   114  				Computed: true,
   115  				Elem:     &schema.Schema{Type: schema.TypeString},
   116  				Set:      schema.HashString,
   117  			},
   118  
   119  			"placement_group": {
   120  				Type:     schema.TypeString,
   121  				Optional: true,
   122  			},
   123  
   124  			"load_balancers": {
   125  				Type:     schema.TypeSet,
   126  				Optional: true,
   127  				Computed: true,
   128  				Elem:     &schema.Schema{Type: schema.TypeString},
   129  				Set:      schema.HashString,
   130  			},
   131  
   132  			"vpc_zone_identifier": {
   133  				Type:     schema.TypeSet,
   134  				Optional: true,
   135  				Computed: true,
   136  				Elem:     &schema.Schema{Type: schema.TypeString},
   137  				Set:      schema.HashString,
   138  			},
   139  
   140  			"termination_policies": {
   141  				Type:     schema.TypeList,
   142  				Optional: true,
   143  				Elem:     &schema.Schema{Type: schema.TypeString},
   144  			},
   145  
   146  			"wait_for_capacity_timeout": {
   147  				Type:     schema.TypeString,
   148  				Optional: true,
   149  				Default:  "10m",
   150  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   151  					value := v.(string)
   152  					duration, err := time.ParseDuration(value)
   153  					if err != nil {
   154  						errors = append(errors, fmt.Errorf(
   155  							"%q cannot be parsed as a duration: %s", k, err))
   156  					}
   157  					if duration < 0 {
   158  						errors = append(errors, fmt.Errorf(
   159  							"%q must be greater than zero", k))
   160  					}
   161  					return
   162  				},
   163  			},
   164  
   165  			"wait_for_elb_capacity": {
   166  				Type:     schema.TypeInt,
   167  				Optional: true,
   168  			},
   169  
   170  			"enabled_metrics": {
   171  				Type:     schema.TypeSet,
   172  				Optional: true,
   173  				Elem:     &schema.Schema{Type: schema.TypeString},
   174  				Set:      schema.HashString,
   175  			},
   176  
   177  			"suspended_processes": {
   178  				Type:     schema.TypeSet,
   179  				Optional: true,
   180  				Elem:     &schema.Schema{Type: schema.TypeString},
   181  				Set:      schema.HashString,
   182  			},
   183  
   184  			"delete_per_machine_timeout": {
   185  				Type:     schema.TypeString,
   186  				Optional: true,
   187  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   188  					value := v.(string)
   189  					duration, err := time.ParseDuration(value)
   190  					if err != nil {
   191  						errors = append(errors, fmt.Errorf(
   192  							"%q cannot be parsed as a duration: %s", k, err))
   193  					}
   194  					if duration < 0 {
   195  						errors = append(errors, fmt.Errorf(
   196  							"%q must be greater than zero", k))
   197  					}
   198  					return
   199  				},
   200  			},
   201  
   202  			"metrics_granularity": {
   203  				Type:     schema.TypeString,
   204  				Optional: true,
   205  				Default:  "1Minute",
   206  			},
   207  
   208  			"protect_from_scale_in": {
   209  				Type:     schema.TypeBool,
   210  				Optional: true,
   211  				Default:  false,
   212  			},
   213  
   214  			"target_group_arns": {
   215  				Type:     schema.TypeSet,
   216  				Optional: true,
   217  				Computed: true,
   218  				Elem:     &schema.Schema{Type: schema.TypeString},
   219  				Set:      schema.HashString,
   220  			},
   221  
   222  			"arn": {
   223  				Type:     schema.TypeString,
   224  				Computed: true,
   225  			},
   226  
   227  			"initial_lifecycle_hook": {
   228  				Type:     schema.TypeSet,
   229  				Optional: true,
   230  				Elem: &schema.Resource{
   231  					Schema: map[string]*schema.Schema{
   232  						"name": {
   233  							Type:     schema.TypeString,
   234  							Required: true,
   235  						},
   236  						"default_result": {
   237  							Type:     schema.TypeString,
   238  							Optional: true,
   239  							Computed: true,
   240  						},
   241  						"heartbeat_timeout": {
   242  							Type:     schema.TypeInt,
   243  							Optional: true,
   244  						},
   245  						"lifecycle_transition": {
   246  							Type:     schema.TypeString,
   247  							Required: true,
   248  						},
   249  						"notification_metadata": {
   250  							Type:     schema.TypeString,
   251  							Optional: true,
   252  						},
   253  						"notification_target_arn": {
   254  							Type:     schema.TypeString,
   255  							Optional: true,
   256  						},
   257  						"role_arn": {
   258  							Type:     schema.TypeString,
   259  							Optional: true,
   260  						},
   261  					},
   262  				},
   263  			},
   264  
   265  			"tag": autoscalingTagsSchema(),
   266  		},
   267  	}
   268  }
   269  
   270  func generatePutLifecycleHookInputs(asgName string, cfgs []interface{}) []autoscaling.PutLifecycleHookInput {
   271  	res := make([]autoscaling.PutLifecycleHookInput, 0, len(cfgs))
   272  
   273  	for _, raw := range cfgs {
   274  		cfg := raw.(map[string]interface{})
   275  
   276  		input := autoscaling.PutLifecycleHookInput{
   277  			AutoScalingGroupName: &asgName,
   278  			LifecycleHookName:    aws.String(cfg["name"].(string)),
   279  		}
   280  
   281  		if v, ok := cfg["default_result"]; ok && v.(string) != "" {
   282  			input.DefaultResult = aws.String(v.(string))
   283  		}
   284  
   285  		if v, ok := cfg["heartbeat_timeout"]; ok && v.(int) > 0 {
   286  			input.HeartbeatTimeout = aws.Int64(int64(v.(int)))
   287  		}
   288  
   289  		if v, ok := cfg["lifecycle_transition"]; ok && v.(string) != "" {
   290  			input.LifecycleTransition = aws.String(v.(string))
   291  		}
   292  
   293  		if v, ok := cfg["notification_metadata"]; ok && v.(string) != "" {
   294  			input.NotificationMetadata = aws.String(v.(string))
   295  		}
   296  
   297  		if v, ok := cfg["notification_target_arn"]; ok && v.(string) != "" {
   298  			input.NotificationTargetARN = aws.String(v.(string))
   299  		}
   300  
   301  		if v, ok := cfg["role_arn"]; ok && v.(string) != "" {
   302  			input.RoleARN = aws.String(v.(string))
   303  		}
   304  
   305  		res = append(res, input)
   306  	}
   307  
   308  	return res
   309  }
   310  
   311  func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
   312  	conn := meta.(*AWSClient).autoscalingconn
   313  
   314  	var asgName string
   315  	if v, ok := d.GetOk("name"); ok {
   316  		asgName = v.(string)
   317  	} else {
   318  		if v, ok := d.GetOk("name_prefix"); ok {
   319  			asgName = resource.PrefixedUniqueId(v.(string))
   320  		} else {
   321  			asgName = resource.PrefixedUniqueId("tf-asg-")
   322  		}
   323  		d.Set("name", asgName)
   324  	}
   325  
   326  	createOpts := autoscaling.CreateAutoScalingGroupInput{
   327  		AutoScalingGroupName:             aws.String(asgName),
   328  		LaunchConfigurationName:          aws.String(d.Get("launch_configuration").(string)),
   329  		NewInstancesProtectedFromScaleIn: aws.Bool(d.Get("protect_from_scale_in").(bool)),
   330  	}
   331  	updateOpts := autoscaling.UpdateAutoScalingGroupInput{
   332  		AutoScalingGroupName: aws.String(asgName),
   333  	}
   334  
   335  	initialLifecycleHooks := d.Get("initial_lifecycle_hook").(*schema.Set).List()
   336  	twoPhases := len(initialLifecycleHooks) > 0
   337  
   338  	minSize := aws.Int64(int64(d.Get("min_size").(int)))
   339  	maxSize := aws.Int64(int64(d.Get("max_size").(int)))
   340  
   341  	if twoPhases {
   342  		createOpts.MinSize = aws.Int64(int64(0))
   343  		createOpts.MaxSize = aws.Int64(int64(0))
   344  
   345  		updateOpts.MinSize = minSize
   346  		updateOpts.MaxSize = maxSize
   347  
   348  		if v, ok := d.GetOk("desired_capacity"); ok {
   349  			updateOpts.DesiredCapacity = aws.Int64(int64(v.(int)))
   350  		}
   351  	} else {
   352  		createOpts.MinSize = minSize
   353  		createOpts.MaxSize = maxSize
   354  
   355  		if v, ok := d.GetOk("desired_capacity"); ok {
   356  			createOpts.DesiredCapacity = aws.Int64(int64(v.(int)))
   357  		}
   358  	}
   359  
   360  	// Availability Zones are optional if VPC Zone Identifer(s) are specified
   361  	if v, ok := d.GetOk("availability_zones"); ok && v.(*schema.Set).Len() > 0 {
   362  		createOpts.AvailabilityZones = expandStringList(v.(*schema.Set).List())
   363  	}
   364  
   365  	if v, ok := d.GetOk("tag"); ok {
   366  		createOpts.Tags = autoscalingTagsFromMap(
   367  			setToMapByKey(v.(*schema.Set), "key"), d.Get("name").(string))
   368  	}
   369  
   370  	if v, ok := d.GetOk("default_cooldown"); ok {
   371  		createOpts.DefaultCooldown = aws.Int64(int64(v.(int)))
   372  	}
   373  
   374  	if v, ok := d.GetOk("health_check_type"); ok && v.(string) != "" {
   375  		createOpts.HealthCheckType = aws.String(v.(string))
   376  	}
   377  
   378  	if v, ok := d.GetOk("health_check_grace_period"); ok {
   379  		createOpts.HealthCheckGracePeriod = aws.Int64(int64(v.(int)))
   380  	}
   381  
   382  	if v, ok := d.GetOk("placement_group"); ok {
   383  		createOpts.PlacementGroup = aws.String(v.(string))
   384  	}
   385  
   386  	if v, ok := d.GetOk("load_balancers"); ok && v.(*schema.Set).Len() > 0 {
   387  		createOpts.LoadBalancerNames = expandStringList(
   388  			v.(*schema.Set).List())
   389  	}
   390  
   391  	if v, ok := d.GetOk("vpc_zone_identifier"); ok && v.(*schema.Set).Len() > 0 {
   392  		createOpts.VPCZoneIdentifier = expandVpcZoneIdentifiers(v.(*schema.Set).List())
   393  	}
   394  
   395  	if v, ok := d.GetOk("termination_policies"); ok && len(v.([]interface{})) > 0 {
   396  		createOpts.TerminationPolicies = expandStringList(v.([]interface{}))
   397  	}
   398  
   399  	if v, ok := d.GetOk("target_group_arns"); ok && len(v.(*schema.Set).List()) > 0 {
   400  		createOpts.TargetGroupARNs = expandStringList(v.(*schema.Set).List())
   401  	}
   402  
   403  	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", createOpts)
   404  	_, err := conn.CreateAutoScalingGroup(&createOpts)
   405  	if err != nil {
   406  		return fmt.Errorf("Error creating AutoScaling Group: %s", err)
   407  	}
   408  
   409  	d.SetId(d.Get("name").(string))
   410  	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())
   411  
   412  	if twoPhases {
   413  		for _, hook := range generatePutLifecycleHookInputs(asgName, initialLifecycleHooks) {
   414  			if err = resourceAwsAutoscalingLifecycleHookPutOp(conn, &hook); err != nil {
   415  				return fmt.Errorf("Error creating initial lifecycle hooks: %s", err)
   416  			}
   417  		}
   418  
   419  		_, err = conn.UpdateAutoScalingGroup(&updateOpts)
   420  		if err != nil {
   421  			return fmt.Errorf("Error setting AutoScaling Group initial capacity: %s", err)
   422  		}
   423  	}
   424  
   425  	if err := waitForASGCapacity(d, meta, capacitySatisfiedCreate); err != nil {
   426  		return err
   427  	}
   428  
   429  	if _, ok := d.GetOk("suspended_processes"); ok {
   430  		suspendedProcessesErr := enableASGSuspendedProcesses(d, conn)
   431  		if suspendedProcessesErr != nil {
   432  			return suspendedProcessesErr
   433  		}
   434  	}
   435  
   436  	if _, ok := d.GetOk("enabled_metrics"); ok {
   437  		metricsErr := enableASGMetricsCollection(d, conn)
   438  		if metricsErr != nil {
   439  			return metricsErr
   440  		}
   441  	}
   442  
   443  	return resourceAwsAutoscalingGroupRead(d, meta)
   444  }
   445  
   446  func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
   447  	conn := meta.(*AWSClient).autoscalingconn
   448  
   449  	g, err := getAwsAutoscalingGroup(d.Id(), conn)
   450  	if err != nil {
   451  		return err
   452  	}
   453  	if g == nil {
   454  		log.Printf("[INFO] Autoscaling Group %q not found", d.Id())
   455  		d.SetId("")
   456  		return nil
   457  	}
   458  
   459  	d.Set("availability_zones", flattenStringList(g.AvailabilityZones))
   460  	d.Set("default_cooldown", g.DefaultCooldown)
   461  	d.Set("arn", g.AutoScalingGroupARN)
   462  	d.Set("desired_capacity", g.DesiredCapacity)
   463  	d.Set("health_check_grace_period", g.HealthCheckGracePeriod)
   464  	d.Set("health_check_type", g.HealthCheckType)
   465  	d.Set("launch_configuration", g.LaunchConfigurationName)
   466  	d.Set("load_balancers", flattenStringList(g.LoadBalancerNames))
   467  
   468  	if err := d.Set("suspended_processes", flattenAsgSuspendedProcesses(g.SuspendedProcesses)); err != nil {
   469  		log.Printf("[WARN] Error setting suspended_processes for %q: %s", d.Id(), err)
   470  	}
   471  	if err := d.Set("target_group_arns", flattenStringList(g.TargetGroupARNs)); err != nil {
   472  		log.Printf("[ERR] Error setting target groups: %s", err)
   473  	}
   474  	d.Set("min_size", g.MinSize)
   475  	d.Set("max_size", g.MaxSize)
   476  	d.Set("placement_group", g.PlacementGroup)
   477  	d.Set("name", g.AutoScalingGroupName)
   478  	d.Set("tag", autoscalingTagDescriptionsToSlice(g.Tags))
   479  	d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ","))
   480  	d.Set("protect_from_scale_in", g.NewInstancesProtectedFromScaleIn)
   481  
   482  	// If no termination polices are explicitly configured and the upstream state
   483  	// is only using the "Default" policy, clear the state to make it consistent
   484  	// with the default AWS create API behavior.
   485  	_, ok := d.GetOk("termination_policies")
   486  	if !ok && len(g.TerminationPolicies) == 1 && *g.TerminationPolicies[0] == "Default" {
   487  		d.Set("termination_policies", []interface{}{})
   488  	} else {
   489  		d.Set("termination_policies", flattenStringList(g.TerminationPolicies))
   490  	}
   491  
   492  	if g.EnabledMetrics != nil {
   493  		if err := d.Set("enabled_metrics", flattenAsgEnabledMetrics(g.EnabledMetrics)); err != nil {
   494  			log.Printf("[WARN] Error setting metrics for (%s): %s", d.Id(), err)
   495  		}
   496  		d.Set("metrics_granularity", g.EnabledMetrics[0].Granularity)
   497  	}
   498  
   499  	return nil
   500  }
   501  
   502  func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   503  	conn := meta.(*AWSClient).autoscalingconn
   504  	shouldWaitForCapacity := false
   505  
   506  	opts := autoscaling.UpdateAutoScalingGroupInput{
   507  		AutoScalingGroupName: aws.String(d.Id()),
   508  	}
   509  
   510  	opts.NewInstancesProtectedFromScaleIn = aws.Bool(d.Get("protect_from_scale_in").(bool))
   511  
   512  	if d.HasChange("default_cooldown") {
   513  		opts.DefaultCooldown = aws.Int64(int64(d.Get("default_cooldown").(int)))
   514  	}
   515  
   516  	if d.HasChange("desired_capacity") {
   517  		opts.DesiredCapacity = aws.Int64(int64(d.Get("desired_capacity").(int)))
   518  		shouldWaitForCapacity = true
   519  	}
   520  
   521  	if d.HasChange("launch_configuration") {
   522  		opts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
   523  	}
   524  
   525  	if d.HasChange("min_size") {
   526  		opts.MinSize = aws.Int64(int64(d.Get("min_size").(int)))
   527  		shouldWaitForCapacity = true
   528  	}
   529  
   530  	if d.HasChange("max_size") {
   531  		opts.MaxSize = aws.Int64(int64(d.Get("max_size").(int)))
   532  	}
   533  
   534  	if d.HasChange("health_check_grace_period") {
   535  		opts.HealthCheckGracePeriod = aws.Int64(int64(d.Get("health_check_grace_period").(int)))
   536  	}
   537  
   538  	if d.HasChange("health_check_type") {
   539  		opts.HealthCheckGracePeriod = aws.Int64(int64(d.Get("health_check_grace_period").(int)))
   540  		opts.HealthCheckType = aws.String(d.Get("health_check_type").(string))
   541  	}
   542  
   543  	if d.HasChange("vpc_zone_identifier") {
   544  		opts.VPCZoneIdentifier = expandVpcZoneIdentifiers(d.Get("vpc_zone_identifier").(*schema.Set).List())
   545  	}
   546  
   547  	if d.HasChange("availability_zones") {
   548  		if v, ok := d.GetOk("availability_zones"); ok && v.(*schema.Set).Len() > 0 {
   549  			opts.AvailabilityZones = expandStringList(v.(*schema.Set).List())
   550  		}
   551  	}
   552  
   553  	if d.HasChange("placement_group") {
   554  		opts.PlacementGroup = aws.String(d.Get("placement_group").(string))
   555  	}
   556  
   557  	if d.HasChange("termination_policies") {
   558  		// If the termination policy is set to null, we need to explicitly set
   559  		// it back to "Default", or the API won't reset it for us.
   560  		if v, ok := d.GetOk("termination_policies"); ok && len(v.([]interface{})) > 0 {
   561  			opts.TerminationPolicies = expandStringList(v.([]interface{}))
   562  		} else {
   563  			log.Printf("[DEBUG] Explicitly setting null termination policy to 'Default'")
   564  			opts.TerminationPolicies = aws.StringSlice([]string{"Default"})
   565  		}
   566  	}
   567  
   568  	if err := setAutoscalingTags(conn, d); err != nil {
   569  		return err
   570  	} else {
   571  		d.SetPartial("tag")
   572  	}
   573  
   574  	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
   575  	_, err := conn.UpdateAutoScalingGroup(&opts)
   576  	if err != nil {
   577  		d.Partial(true)
   578  		return fmt.Errorf("Error updating Autoscaling group: %s", err)
   579  	}
   580  
   581  	if d.HasChange("load_balancers") {
   582  
   583  		o, n := d.GetChange("load_balancers")
   584  		if o == nil {
   585  			o = new(schema.Set)
   586  		}
   587  		if n == nil {
   588  			n = new(schema.Set)
   589  		}
   590  
   591  		os := o.(*schema.Set)
   592  		ns := n.(*schema.Set)
   593  		remove := expandStringList(os.Difference(ns).List())
   594  		add := expandStringList(ns.Difference(os).List())
   595  
   596  		if len(remove) > 0 {
   597  			_, err := conn.DetachLoadBalancers(&autoscaling.DetachLoadBalancersInput{
   598  				AutoScalingGroupName: aws.String(d.Id()),
   599  				LoadBalancerNames:    remove,
   600  			})
   601  			if err != nil {
   602  				return fmt.Errorf("[WARN] Error updating Load Balancers for AutoScaling Group (%s), error: %s", d.Id(), err)
   603  			}
   604  		}
   605  
   606  		if len(add) > 0 {
   607  			_, err := conn.AttachLoadBalancers(&autoscaling.AttachLoadBalancersInput{
   608  				AutoScalingGroupName: aws.String(d.Id()),
   609  				LoadBalancerNames:    add,
   610  			})
   611  			if err != nil {
   612  				return fmt.Errorf("[WARN] Error updating Load Balancers for AutoScaling Group (%s), error: %s", d.Id(), err)
   613  			}
   614  		}
   615  	}
   616  
   617  	if d.HasChange("target_group_arns") {
   618  
   619  		o, n := d.GetChange("target_group_arns")
   620  		if o == nil {
   621  			o = new(schema.Set)
   622  		}
   623  		if n == nil {
   624  			n = new(schema.Set)
   625  		}
   626  
   627  		os := o.(*schema.Set)
   628  		ns := n.(*schema.Set)
   629  		remove := expandStringList(os.Difference(ns).List())
   630  		add := expandStringList(ns.Difference(os).List())
   631  
   632  		if len(remove) > 0 {
   633  			_, err := conn.DetachLoadBalancerTargetGroups(&autoscaling.DetachLoadBalancerTargetGroupsInput{
   634  				AutoScalingGroupName: aws.String(d.Id()),
   635  				TargetGroupARNs:      remove,
   636  			})
   637  			if err != nil {
   638  				return fmt.Errorf("[WARN] Error updating Load Balancers Target Groups for AutoScaling Group (%s), error: %s", d.Id(), err)
   639  			}
   640  		}
   641  
   642  		if len(add) > 0 {
   643  			_, err := conn.AttachLoadBalancerTargetGroups(&autoscaling.AttachLoadBalancerTargetGroupsInput{
   644  				AutoScalingGroupName: aws.String(d.Id()),
   645  				TargetGroupARNs:      add,
   646  			})
   647  			if err != nil {
   648  				return fmt.Errorf("[WARN] Error updating Load Balancers Target Groups for AutoScaling Group (%s), error: %s", d.Id(), err)
   649  			}
   650  		}
   651  	}
   652  
   653  	if shouldWaitForCapacity {
   654  		if err := waitForASGCapacity(d, meta, capacitySatisfiedUpdate); err != nil {
   655  			return errwrap.Wrapf("Error waiting for AutoScaling Group Capacity: {{err}}", err)
   656  		}
   657  	}
   658  
   659  	if d.HasChange("enabled_metrics") {
   660  		if err := updateASGMetricsCollection(d, conn); err != nil {
   661  			return errwrap.Wrapf("Error updating AutoScaling Group Metrics collection: {{err}}", err)
   662  		}
   663  	}
   664  
   665  	if d.HasChange("suspended_processes") {
   666  		if err := updateASGSuspendedProcesses(d, conn); err != nil {
   667  			return errwrap.Wrapf("Error updating AutoScaling Group Suspended Processes: {{err}}", err)
   668  		}
   669  	}
   670  
   671  	return resourceAwsAutoscalingGroupRead(d, meta)
   672  }
   673  
   674  func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
   675  	conn := meta.(*AWSClient).autoscalingconn
   676  
   677  	// Read the autoscaling group first. If it doesn't exist, we're done.
   678  	// We need the group in order to check if there are instances attached.
   679  	// If so, we need to remove those first.
   680  	g, err := getAwsAutoscalingGroup(d.Id(), conn)
   681  	if err != nil {
   682  		return err
   683  	}
   684  	if g == nil {
   685  		log.Printf("[INFO] Autoscaling Group %q not found", d.Id())
   686  		d.SetId("")
   687  		return nil
   688  	}
   689  	if len(g.Instances) > 0 || *g.DesiredCapacity > 0 {
   690  		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
   691  			return err
   692  		}
   693  	}
   694  
   695  	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
   696  	deleteopts := autoscaling.DeleteAutoScalingGroupInput{
   697  		AutoScalingGroupName: aws.String(d.Id()),
   698  		ForceDelete:          aws.Bool(d.Get("force_delete").(bool)),
   699  	}
   700  
   701  	// We retry the delete operation to handle InUse/InProgress errors coming
   702  	// from scaling operations. We should be able to sneak in a delete in between
   703  	// scaling operations within 5m.
   704  	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
   705  		if _, err := conn.DeleteAutoScalingGroup(&deleteopts); err != nil {
   706  			if awserr, ok := err.(awserr.Error); ok {
   707  				switch awserr.Code() {
   708  				case "InvalidGroup.NotFound":
   709  					// Already gone? Sure!
   710  					return nil
   711  				case "ResourceInUse", "ScalingActivityInProgress":
   712  					// These are retryable
   713  					return resource.RetryableError(awserr)
   714  				}
   715  			}
   716  			// Didn't recognize the error, so shouldn't retry.
   717  			return resource.NonRetryableError(err)
   718  		}
   719  		// Successful delete
   720  		return nil
   721  	})
   722  	if err != nil {
   723  		return err
   724  	}
   725  
   726  	return resource.Retry(5*time.Minute, func() *resource.RetryError {
   727  		if g, _ = getAwsAutoscalingGroup(d.Id(), conn); g != nil {
   728  			return resource.RetryableError(
   729  				fmt.Errorf("Auto Scaling Group still exists"))
   730  		}
   731  		return nil
   732  	})
   733  }
   734  
   735  func getAwsAutoscalingGroup(
   736  	asgName string,
   737  	conn *autoscaling.AutoScaling) (*autoscaling.Group, error) {
   738  
   739  	describeOpts := autoscaling.DescribeAutoScalingGroupsInput{
   740  		AutoScalingGroupNames: []*string{aws.String(asgName)},
   741  	}
   742  
   743  	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
   744  	describeGroups, err := conn.DescribeAutoScalingGroups(&describeOpts)
   745  	if err != nil {
   746  		autoscalingerr, ok := err.(awserr.Error)
   747  		if ok && autoscalingerr.Code() == "InvalidGroup.NotFound" {
   748  			return nil, nil
   749  		}
   750  
   751  		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
   752  	}
   753  
   754  	// Search for the autoscaling group
   755  	for idx, asc := range describeGroups.AutoScalingGroups {
   756  		if *asc.AutoScalingGroupName == asgName {
   757  			return describeGroups.AutoScalingGroups[idx], nil
   758  		}
   759  	}
   760  
   761  	return nil, nil
   762  }
   763  
   764  func resourceAwsAutoscalingScaleDownToCount(d *schema.ResourceData, count int64, conn *autoscaling.AutoScaling) error {
   765  	log.Printf("[DEBUG] Reducing autoscaling group capacity to %d", count)
   766  	opts := autoscaling.UpdateAutoScalingGroupInput{
   767  		AutoScalingGroupName: aws.String(d.Id()),
   768  		MinSize:              aws.Int64(0),
   769  		DesiredCapacity:      aws.Int64(count),
   770  		MaxSize:              aws.Int64(count),
   771  	}
   772  	if _, err := conn.UpdateAutoScalingGroup(&opts); err != nil {
   773  		return fmt.Errorf("Error setting capacity to %d to drain: %s", count, err)
   774  	}
   775  	return nil
   776  }
   777  
   778  func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
   779  	conn := meta.(*AWSClient).autoscalingconn
   780  
   781  	if d.Get("force_delete").(bool) {
   782  		log.Printf("[DEBUG] Skipping ASG drain, force_delete was set.")
   783  		return nil
   784  	}
   785  
   786  	if delay, ok := d.GetOk("delete_per_machine_timeout"); ok {
   787  		g, err := getAwsAutoscalingGroup(d.Id(), conn)
   788  		if err != nil {
   789  			return err
   790  		}
   791  
   792  		delayDuration, err := time.ParseDuration(delay.(string))
   793  		if err != nil {
   794  			return err
   795  		}
   796  
   797  		count := *g.DesiredCapacity
   798  
   799  		for count > 0 {
   800  			count--
   801  			log.Printf("[INFO] AutoScaling Group scale down to %d: %v", count, d.Id())
   802  
   803  			if err := resourceAwsAutoscalingScaleDownToCount(d, count, conn); err != nil {
   804  				return err
   805  			}
   806  
   807  			// Wait for the scale-down to occur. Note that *each step* waits
   808  			// wait_for_capacity_timeout.
   809  			if err := waitForASGScaleDown(d, meta, int(count)); err != nil {
   810  				return err
   811  			}
   812  
   813  			if count != 0 {
   814  				time.Sleep(delayDuration)
   815  			}
   816  		}
   817  	} else {
   818  		if err := resourceAwsAutoscalingScaleDownToCount(d, 0, conn); err != nil {
   819  			return err
   820  		}
   821  	}
   822  
   823  	// Next, wait for the autoscale group to drain
   824  	log.Printf("[DEBUG] Waiting for group to have 0 instances")
   825  	return resource.Retry(10*time.Minute, func() *resource.RetryError {
   826  		g, err := getAwsAutoscalingGroup(d.Id(), conn)
   827  		if err != nil {
   828  			return resource.NonRetryableError(err)
   829  		}
   830  		if g == nil {
   831  			log.Printf("[INFO] Autoscaling Group %q not found", d.Id())
   832  			d.SetId("")
   833  			return nil
   834  		}
   835  
   836  		if len(g.Instances) == 0 {
   837  			return nil
   838  		}
   839  
   840  		return resource.RetryableError(
   841  			fmt.Errorf("group still has %d instances", len(g.Instances)))
   842  	})
   843  }
   844  
   845  func enableASGSuspendedProcesses(d *schema.ResourceData, conn *autoscaling.AutoScaling) error {
   846  	props := &autoscaling.ScalingProcessQuery{
   847  		AutoScalingGroupName: aws.String(d.Id()),
   848  		ScalingProcesses:     expandStringList(d.Get("suspended_processes").(*schema.Set).List()),
   849  	}
   850  
   851  	_, err := conn.SuspendProcesses(props)
   852  	if err != nil {
   853  		return err
   854  	}
   855  
   856  	return nil
   857  }
   858  
   859  func enableASGMetricsCollection(d *schema.ResourceData, conn *autoscaling.AutoScaling) error {
   860  	props := &autoscaling.EnableMetricsCollectionInput{
   861  		AutoScalingGroupName: aws.String(d.Id()),
   862  		Granularity:          aws.String(d.Get("metrics_granularity").(string)),
   863  		Metrics:              expandStringList(d.Get("enabled_metrics").(*schema.Set).List()),
   864  	}
   865  
   866  	log.Printf("[INFO] Enabling metrics collection for the ASG: %s", d.Id())
   867  	_, metricsErr := conn.EnableMetricsCollection(props)
   868  	if metricsErr != nil {
   869  		return metricsErr
   870  	}
   871  
   872  	return nil
   873  }
   874  
   875  func updateASGSuspendedProcesses(d *schema.ResourceData, conn *autoscaling.AutoScaling) error {
   876  	o, n := d.GetChange("suspended_processes")
   877  	if o == nil {
   878  		o = new(schema.Set)
   879  	}
   880  	if n == nil {
   881  		n = new(schema.Set)
   882  	}
   883  
   884  	os := o.(*schema.Set)
   885  	ns := n.(*schema.Set)
   886  
   887  	resumeProcesses := os.Difference(ns)
   888  	if resumeProcesses.Len() != 0 {
   889  		props := &autoscaling.ScalingProcessQuery{
   890  			AutoScalingGroupName: aws.String(d.Id()),
   891  			ScalingProcesses:     expandStringList(resumeProcesses.List()),
   892  		}
   893  
   894  		_, err := conn.ResumeProcesses(props)
   895  		if err != nil {
   896  			return fmt.Errorf("Error Resuming Processes for ASG %q: %s", d.Id(), err)
   897  		}
   898  	}
   899  
   900  	suspendedProcesses := ns.Difference(os)
   901  	if suspendedProcesses.Len() != 0 {
   902  		props := &autoscaling.ScalingProcessQuery{
   903  			AutoScalingGroupName: aws.String(d.Id()),
   904  			ScalingProcesses:     expandStringList(suspendedProcesses.List()),
   905  		}
   906  
   907  		_, err := conn.SuspendProcesses(props)
   908  		if err != nil {
   909  			return fmt.Errorf("Error Suspending Processes for ASG %q: %s", d.Id(), err)
   910  		}
   911  	}
   912  
   913  	return nil
   914  
   915  }
   916  
   917  func updateASGMetricsCollection(d *schema.ResourceData, conn *autoscaling.AutoScaling) error {
   918  
   919  	o, n := d.GetChange("enabled_metrics")
   920  	if o == nil {
   921  		o = new(schema.Set)
   922  	}
   923  	if n == nil {
   924  		n = new(schema.Set)
   925  	}
   926  
   927  	os := o.(*schema.Set)
   928  	ns := n.(*schema.Set)
   929  
   930  	disableMetrics := os.Difference(ns)
   931  	if disableMetrics.Len() != 0 {
   932  		props := &autoscaling.DisableMetricsCollectionInput{
   933  			AutoScalingGroupName: aws.String(d.Id()),
   934  			Metrics:              expandStringList(disableMetrics.List()),
   935  		}
   936  
   937  		_, err := conn.DisableMetricsCollection(props)
   938  		if err != nil {
   939  			return fmt.Errorf("Failure to Disable metrics collection types for ASG %s: %s", d.Id(), err)
   940  		}
   941  	}
   942  
   943  	enabledMetrics := ns.Difference(os)
   944  	if enabledMetrics.Len() != 0 {
   945  		props := &autoscaling.EnableMetricsCollectionInput{
   946  			AutoScalingGroupName: aws.String(d.Id()),
   947  			Metrics:              expandStringList(enabledMetrics.List()),
   948  			Granularity:          aws.String(d.Get("metrics_granularity").(string)),
   949  		}
   950  
   951  		_, err := conn.EnableMetricsCollection(props)
   952  		if err != nil {
   953  			return fmt.Errorf("Failure to Enable metrics collection types for ASG %s: %s", d.Id(), err)
   954  		}
   955  	}
   956  
   957  	return nil
   958  }
   959  
   960  // getELBInstanceStates returns a mapping of the instance states of all the ELBs attached to the
   961  // provided ASG.
   962  //
   963  // Note that this is the instance state function for ELB Classic.
   964  //
   965  // Nested like: lbName -> instanceId -> instanceState
   966  func getELBInstanceStates(g *autoscaling.Group, meta interface{}) (map[string]map[string]string, error) {
   967  	lbInstanceStates := make(map[string]map[string]string)
   968  	elbconn := meta.(*AWSClient).elbconn
   969  
   970  	for _, lbName := range g.LoadBalancerNames {
   971  		lbInstanceStates[*lbName] = make(map[string]string)
   972  		opts := &elb.DescribeInstanceHealthInput{LoadBalancerName: lbName}
   973  		r, err := elbconn.DescribeInstanceHealth(opts)
   974  		if err != nil {
   975  			return nil, err
   976  		}
   977  		for _, is := range r.InstanceStates {
   978  			if is.InstanceId == nil || is.State == nil {
   979  				continue
   980  			}
   981  			lbInstanceStates[*lbName][*is.InstanceId] = *is.State
   982  		}
   983  	}
   984  
   985  	return lbInstanceStates, nil
   986  }
   987  
   988  // getTargetGroupInstanceStates returns a mapping of the instance states of
   989  // all the ALB target groups attached to the provided ASG.
   990  //
   991  // Note that this is the instance state function for Application Load
   992  // Balancing (aka ELBv2).
   993  //
   994  // Nested like: targetGroupARN -> instanceId -> instanceState
   995  func getTargetGroupInstanceStates(g *autoscaling.Group, meta interface{}) (map[string]map[string]string, error) {
   996  	targetInstanceStates := make(map[string]map[string]string)
   997  	elbv2conn := meta.(*AWSClient).elbv2conn
   998  
   999  	for _, targetGroupARN := range g.TargetGroupARNs {
  1000  		targetInstanceStates[*targetGroupARN] = make(map[string]string)
  1001  		opts := &elbv2.DescribeTargetHealthInput{TargetGroupArn: targetGroupARN}
  1002  		r, err := elbv2conn.DescribeTargetHealth(opts)
  1003  		if err != nil {
  1004  			return nil, err
  1005  		}
  1006  		for _, desc := range r.TargetHealthDescriptions {
  1007  			if desc.Target == nil || desc.Target.Id == nil || desc.TargetHealth == nil || desc.TargetHealth.State == nil {
  1008  				continue
  1009  			}
  1010  			targetInstanceStates[*targetGroupARN][*desc.Target.Id] = *desc.TargetHealth.State
  1011  		}
  1012  	}
  1013  
  1014  	return targetInstanceStates, nil
  1015  }
  1016  
  1017  func expandVpcZoneIdentifiers(list []interface{}) *string {
  1018  	strs := make([]string, len(list))
  1019  	for _, s := range list {
  1020  		strs = append(strs, s.(string))
  1021  	}
  1022  	return aws.String(strings.Join(strs, ","))
  1023  }