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