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