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