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