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 }