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