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