github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/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/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	"github.com/hashicorp/aws-sdk-go/aws"
    14  	"github.com/hashicorp/aws-sdk-go/gen/autoscaling"
    15  )
    16  
    17  func resourceAwsAutoscalingGroup() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsAutoscalingGroupCreate,
    20  		Read:   resourceAwsAutoscalingGroupRead,
    21  		Update: resourceAwsAutoscalingGroupUpdate,
    22  		Delete: resourceAwsAutoscalingGroupDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  				ForceNew: true,
    29  			},
    30  
    31  			"launch_configuration": &schema.Schema{
    32  				Type:     schema.TypeString,
    33  				Required: true,
    34  			},
    35  
    36  			"desired_capacity": &schema.Schema{
    37  				Type:     schema.TypeInt,
    38  				Optional: true,
    39  				Computed: true,
    40  			},
    41  
    42  			"min_size": &schema.Schema{
    43  				Type:     schema.TypeInt,
    44  				Required: true,
    45  			},
    46  
    47  			"max_size": &schema.Schema{
    48  				Type:     schema.TypeInt,
    49  				Required: true,
    50  			},
    51  
    52  			"default_cooldown": &schema.Schema{
    53  				Type:     schema.TypeInt,
    54  				Optional: true,
    55  				Computed: true,
    56  				ForceNew: true,
    57  			},
    58  
    59  			"force_delete": &schema.Schema{
    60  				Type:     schema.TypeBool,
    61  				Optional: true,
    62  				Computed: true,
    63  				ForceNew: true,
    64  			},
    65  
    66  			"health_check_grace_period": &schema.Schema{
    67  				Type:     schema.TypeInt,
    68  				Optional: true,
    69  				Computed: true,
    70  				ForceNew: true,
    71  			},
    72  
    73  			"health_check_type": &schema.Schema{
    74  				Type:     schema.TypeString,
    75  				Optional: true,
    76  				Computed: true,
    77  				ForceNew: true,
    78  			},
    79  
    80  			"availability_zones": &schema.Schema{
    81  				Type:     schema.TypeSet,
    82  				Required: true,
    83  				ForceNew: true,
    84  				Elem:     &schema.Schema{Type: schema.TypeString},
    85  				Set: func(v interface{}) int {
    86  					return hashcode.String(v.(string))
    87  				},
    88  			},
    89  
    90  			"load_balancers": &schema.Schema{
    91  				Type:     schema.TypeSet,
    92  				Optional: true,
    93  				ForceNew: true,
    94  				Elem:     &schema.Schema{Type: schema.TypeString},
    95  				Set: func(v interface{}) int {
    96  					return hashcode.String(v.(string))
    97  				},
    98  			},
    99  
   100  			"vpc_zone_identifier": &schema.Schema{
   101  				Type:     schema.TypeSet,
   102  				Optional: true,
   103  				Computed: true,
   104  				ForceNew: true,
   105  				Elem:     &schema.Schema{Type: schema.TypeString},
   106  				Set: func(v interface{}) int {
   107  					return hashcode.String(v.(string))
   108  				},
   109  			},
   110  
   111  			"termination_policies": &schema.Schema{
   112  				Type:     schema.TypeSet,
   113  				Optional: true,
   114  				Computed: true,
   115  				ForceNew: true,
   116  				Elem:     &schema.Schema{Type: schema.TypeString},
   117  				Set: func(v interface{}) int {
   118  					return hashcode.String(v.(string))
   119  				},
   120  			},
   121  
   122  			"tag": autoscalingTagsSchema(),
   123  		},
   124  	}
   125  }
   126  
   127  func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
   128  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   129  
   130  	var autoScalingGroupOpts autoscaling.CreateAutoScalingGroupType
   131  	autoScalingGroupOpts.AutoScalingGroupName = aws.String(d.Get("name").(string))
   132  	autoScalingGroupOpts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
   133  	autoScalingGroupOpts.MinSize = aws.Integer(d.Get("min_size").(int))
   134  	autoScalingGroupOpts.MaxSize = aws.Integer(d.Get("max_size").(int))
   135  	autoScalingGroupOpts.AvailabilityZones = expandStringList(
   136  		d.Get("availability_zones").(*schema.Set).List())
   137  
   138  	if v, ok := d.GetOk("tag"); ok {
   139  		autoScalingGroupOpts.Tags = autoscalingTagsFromMap(
   140  			setToMapByKey(v.(*schema.Set), "key"), d.Get("name").(string))
   141  	}
   142  
   143  	if v, ok := d.GetOk("default_cooldown"); ok {
   144  		autoScalingGroupOpts.DefaultCooldown = aws.Integer(v.(int))
   145  	}
   146  
   147  	if v, ok := d.GetOk("health_check_type"); ok && v.(string) != "" {
   148  		autoScalingGroupOpts.HealthCheckType = aws.String(v.(string))
   149  	}
   150  
   151  	if v, ok := d.GetOk("desired_capacity"); ok {
   152  		autoScalingGroupOpts.DesiredCapacity = aws.Integer(v.(int))
   153  	}
   154  
   155  	if v, ok := d.GetOk("health_check_grace_period"); ok {
   156  		autoScalingGroupOpts.HealthCheckGracePeriod = aws.Integer(v.(int))
   157  	}
   158  
   159  	if v, ok := d.GetOk("load_balancers"); ok && v.(*schema.Set).Len() > 0 {
   160  		autoScalingGroupOpts.LoadBalancerNames = expandStringList(
   161  			v.(*schema.Set).List())
   162  	}
   163  
   164  	if v, ok := d.GetOk("vpc_zone_identifier"); ok && v.(*schema.Set).Len() > 0 {
   165  		exp := expandStringList(v.(*schema.Set).List())
   166  		autoScalingGroupOpts.VPCZoneIdentifier = aws.String(strings.Join(exp, ","))
   167  	}
   168  
   169  	if v, ok := d.GetOk("termination_policies"); ok && v.(*schema.Set).Len() > 0 {
   170  		autoScalingGroupOpts.TerminationPolicies = expandStringList(
   171  			v.(*schema.Set).List())
   172  	}
   173  
   174  	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts)
   175  	err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts)
   176  	if err != nil {
   177  		return fmt.Errorf("Error creating Autoscaling Group: %s", err)
   178  	}
   179  
   180  	d.SetId(d.Get("name").(string))
   181  	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())
   182  
   183  	return resourceAwsAutoscalingGroupRead(d, meta)
   184  }
   185  
   186  func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
   187  	g, err := getAwsAutoscalingGroup(d, meta)
   188  	if err != nil {
   189  		return err
   190  	}
   191  	if g == nil {
   192  		return nil
   193  	}
   194  
   195  	d.Set("availability_zones", g.AvailabilityZones)
   196  	d.Set("default_cooldown", g.DefaultCooldown)
   197  	d.Set("desired_capacity", g.DesiredCapacity)
   198  	d.Set("health_check_grace_period", g.HealthCheckGracePeriod)
   199  	d.Set("health_check_type", g.HealthCheckType)
   200  	d.Set("launch_configuration", g.LaunchConfigurationName)
   201  	d.Set("load_balancers", g.LoadBalancerNames)
   202  	d.Set("min_size", g.MinSize)
   203  	d.Set("max_size", g.MaxSize)
   204  	d.Set("name", g.AutoScalingGroupName)
   205  	d.Set("tag", g.Tags)
   206  	d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ","))
   207  	d.Set("termination_policies", g.TerminationPolicies)
   208  
   209  	return nil
   210  }
   211  
   212  func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   213  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   214  
   215  	opts := autoscaling.UpdateAutoScalingGroupType{
   216  		AutoScalingGroupName: aws.String(d.Id()),
   217  	}
   218  
   219  	if d.HasChange("desired_capacity") {
   220  		opts.DesiredCapacity = aws.Integer(d.Get("desired_capacity").(int))
   221  	}
   222  
   223  	if d.HasChange("launch_configuration") {
   224  		opts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
   225  	}
   226  
   227  	if d.HasChange("min_size") {
   228  		opts.MinSize = aws.Integer(d.Get("min_size").(int))
   229  	}
   230  
   231  	if d.HasChange("max_size") {
   232  		opts.MaxSize = aws.Integer(d.Get("max_size").(int))
   233  	}
   234  
   235  	if err := setAutoscalingTags(autoscalingconn, d); err != nil {
   236  		return err
   237  	} else {
   238  		d.SetPartial("tag")
   239  	}
   240  
   241  	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
   242  	err := autoscalingconn.UpdateAutoScalingGroup(&opts)
   243  	if err != nil {
   244  		d.Partial(true)
   245  		return fmt.Errorf("Error updating Autoscaling group: %s", err)
   246  	}
   247  
   248  	return resourceAwsAutoscalingGroupRead(d, meta)
   249  }
   250  
   251  func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
   252  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   253  
   254  	// Read the autoscaling group first. If it doesn't exist, we're done.
   255  	// We need the group in order to check if there are instances attached.
   256  	// If so, we need to remove those first.
   257  	g, err := getAwsAutoscalingGroup(d, meta)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	if g == nil {
   262  		return nil
   263  	}
   264  	if len(g.Instances) > 0 || *g.DesiredCapacity > 0 {
   265  		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
   266  			return err
   267  		}
   268  	}
   269  
   270  	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
   271  	deleteopts := autoscaling.DeleteAutoScalingGroupType{AutoScalingGroupName: aws.String(d.Id())}
   272  
   273  	// You can force an autoscaling group to delete
   274  	// even if it's in the process of scaling a resource.
   275  	// Normally, you would set the min-size and max-size to 0,0
   276  	// and then delete the group. This bypasses that and leaves
   277  	// resources potentially dangling.
   278  	if d.Get("force_delete").(bool) {
   279  		deleteopts.ForceDelete = aws.Boolean(true)
   280  	}
   281  
   282  	if err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil {
   283  		autoscalingerr, ok := err.(aws.APIError)
   284  		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
   285  			return nil
   286  		}
   287  		return err
   288  	}
   289  
   290  	return resource.Retry(5*time.Minute, func() error {
   291  		if g, _ = getAwsAutoscalingGroup(d, meta); g != nil {
   292  			return fmt.Errorf("Auto Scaling Group still exists")
   293  		}
   294  		return nil
   295  	})
   296  }
   297  
   298  func getAwsAutoscalingGroup(
   299  	d *schema.ResourceData,
   300  	meta interface{}) (*autoscaling.AutoScalingGroup, error) {
   301  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   302  
   303  	describeOpts := autoscaling.AutoScalingGroupNamesType{
   304  		AutoScalingGroupNames: []string{d.Id()},
   305  	}
   306  
   307  	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
   308  	describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts)
   309  	if err != nil {
   310  		autoscalingerr, ok := err.(aws.APIError)
   311  		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
   312  			d.SetId("")
   313  			return nil, nil
   314  		}
   315  
   316  		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
   317  	}
   318  
   319  	// Search for the autoscaling group
   320  	for idx, asc := range describeGroups.AutoScalingGroups {
   321  		if *asc.AutoScalingGroupName == d.Id() {
   322  			return &describeGroups.AutoScalingGroups[idx], nil
   323  		}
   324  	}
   325  
   326  	// ASG not found
   327  	d.SetId("")
   328  	return nil, nil
   329  }
   330  
   331  func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
   332  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   333  
   334  	// First, set the capacity to zero so the group will drain
   335  	log.Printf("[DEBUG] Reducing autoscaling group capacity to zero")
   336  	opts := autoscaling.UpdateAutoScalingGroupType{
   337  		AutoScalingGroupName: aws.String(d.Id()),
   338  		DesiredCapacity:      aws.Integer(0),
   339  		MinSize:              aws.Integer(0),
   340  		MaxSize:              aws.Integer(0),
   341  	}
   342  	if err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil {
   343  		return fmt.Errorf("Error setting capacity to zero to drain: %s", err)
   344  	}
   345  
   346  	// Next, wait for the autoscale group to drain
   347  	log.Printf("[DEBUG] Waiting for group to have zero instances")
   348  	return resource.Retry(10*time.Minute, func() error {
   349  		g, err := getAwsAutoscalingGroup(d, meta)
   350  		if err != nil {
   351  			return resource.RetryError{Err: err}
   352  		}
   353  		if g == nil {
   354  			return nil
   355  		}
   356  
   357  		if len(g.Instances) == 0 {
   358  			return nil
   359  		}
   360  
   361  		return fmt.Errorf("group still has %d instances", len(g.Instances))
   362  	})
   363  }