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 }