github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_elb.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "strings" 8 "time" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/ec2" 13 "github.com/aws/aws-sdk-go/service/elb" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 "github.com/hashicorp/terraform/helper/resource" 16 "github.com/hashicorp/terraform/helper/schema" 17 ) 18 19 func resourceAwsElb() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceAwsElbCreate, 22 Read: resourceAwsElbRead, 23 Update: resourceAwsElbUpdate, 24 Delete: resourceAwsElbDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "name": &schema.Schema{ 28 Type: schema.TypeString, 29 Optional: true, 30 Computed: true, 31 ForceNew: true, 32 ValidateFunc: validateElbName, 33 }, 34 35 "internal": &schema.Schema{ 36 Type: schema.TypeBool, 37 Optional: true, 38 ForceNew: true, 39 Computed: true, 40 }, 41 42 "cross_zone_load_balancing": &schema.Schema{ 43 Type: schema.TypeBool, 44 Optional: true, 45 }, 46 47 "availability_zones": &schema.Schema{ 48 Type: schema.TypeSet, 49 Elem: &schema.Schema{Type: schema.TypeString}, 50 Optional: true, 51 Computed: true, 52 Set: schema.HashString, 53 }, 54 55 "instances": &schema.Schema{ 56 Type: schema.TypeSet, 57 Elem: &schema.Schema{Type: schema.TypeString}, 58 Optional: true, 59 Computed: true, 60 Set: schema.HashString, 61 }, 62 63 "security_groups": &schema.Schema{ 64 Type: schema.TypeSet, 65 Elem: &schema.Schema{Type: schema.TypeString}, 66 Optional: true, 67 Computed: true, 68 Set: schema.HashString, 69 }, 70 71 "source_security_group": &schema.Schema{ 72 Type: schema.TypeString, 73 Optional: true, 74 Computed: true, 75 }, 76 77 "source_security_group_id": &schema.Schema{ 78 Type: schema.TypeString, 79 Computed: true, 80 }, 81 82 "subnets": &schema.Schema{ 83 Type: schema.TypeSet, 84 Elem: &schema.Schema{Type: schema.TypeString}, 85 Optional: true, 86 Computed: true, 87 Set: schema.HashString, 88 }, 89 90 "idle_timeout": &schema.Schema{ 91 Type: schema.TypeInt, 92 Optional: true, 93 Default: 60, 94 }, 95 96 "connection_draining": &schema.Schema{ 97 Type: schema.TypeBool, 98 Optional: true, 99 Default: false, 100 }, 101 102 "connection_draining_timeout": &schema.Schema{ 103 Type: schema.TypeInt, 104 Optional: true, 105 Default: 300, 106 }, 107 108 "access_logs": &schema.Schema{ 109 Type: schema.TypeList, 110 Optional: true, 111 Elem: &schema.Resource{ 112 Schema: map[string]*schema.Schema{ 113 "interval": &schema.Schema{ 114 Type: schema.TypeInt, 115 Optional: true, 116 Default: 60, 117 }, 118 "bucket": &schema.Schema{ 119 Type: schema.TypeString, 120 Required: true, 121 }, 122 "bucket_prefix": &schema.Schema{ 123 Type: schema.TypeString, 124 Optional: true, 125 }, 126 }, 127 }, 128 }, 129 130 "listener": &schema.Schema{ 131 Type: schema.TypeSet, 132 Required: true, 133 Elem: &schema.Resource{ 134 Schema: map[string]*schema.Schema{ 135 "instance_port": &schema.Schema{ 136 Type: schema.TypeInt, 137 Required: true, 138 }, 139 140 "instance_protocol": &schema.Schema{ 141 Type: schema.TypeString, 142 Required: true, 143 }, 144 145 "lb_port": &schema.Schema{ 146 Type: schema.TypeInt, 147 Required: true, 148 }, 149 150 "lb_protocol": &schema.Schema{ 151 Type: schema.TypeString, 152 Required: true, 153 }, 154 155 "ssl_certificate_id": &schema.Schema{ 156 Type: schema.TypeString, 157 Optional: true, 158 }, 159 }, 160 }, 161 Set: resourceAwsElbListenerHash, 162 }, 163 164 "health_check": &schema.Schema{ 165 Type: schema.TypeList, 166 Optional: true, 167 Computed: true, 168 Elem: &schema.Resource{ 169 Schema: map[string]*schema.Schema{ 170 "healthy_threshold": &schema.Schema{ 171 Type: schema.TypeInt, 172 Required: true, 173 }, 174 175 "unhealthy_threshold": &schema.Schema{ 176 Type: schema.TypeInt, 177 Required: true, 178 }, 179 180 "target": &schema.Schema{ 181 Type: schema.TypeString, 182 Required: true, 183 }, 184 185 "interval": &schema.Schema{ 186 Type: schema.TypeInt, 187 Required: true, 188 }, 189 190 "timeout": &schema.Schema{ 191 Type: schema.TypeInt, 192 Required: true, 193 }, 194 }, 195 }, 196 }, 197 198 "dns_name": &schema.Schema{ 199 Type: schema.TypeString, 200 Computed: true, 201 }, 202 203 "zone_id": &schema.Schema{ 204 Type: schema.TypeString, 205 Computed: true, 206 }, 207 208 "tags": tagsSchema(), 209 }, 210 } 211 } 212 213 func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { 214 elbconn := meta.(*AWSClient).elbconn 215 216 // Expand the "listener" set to aws-sdk-go compat []*elb.Listener 217 listeners, err := expandListeners(d.Get("listener").(*schema.Set).List()) 218 if err != nil { 219 return err 220 } 221 222 var elbName string 223 if v, ok := d.GetOk("name"); ok { 224 elbName = v.(string) 225 } else { 226 elbName = resource.PrefixedUniqueId("tf-lb-") 227 d.Set("name", elbName) 228 } 229 230 tags := tagsFromMapELB(d.Get("tags").(map[string]interface{})) 231 // Provision the elb 232 elbOpts := &elb.CreateLoadBalancerInput{ 233 LoadBalancerName: aws.String(elbName), 234 Listeners: listeners, 235 Tags: tags, 236 } 237 238 if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) { 239 elbOpts.Scheme = aws.String("internal") 240 } 241 242 if v, ok := d.GetOk("availability_zones"); ok { 243 elbOpts.AvailabilityZones = expandStringList(v.(*schema.Set).List()) 244 } 245 246 if v, ok := d.GetOk("security_groups"); ok { 247 elbOpts.SecurityGroups = expandStringList(v.(*schema.Set).List()) 248 } 249 250 if v, ok := d.GetOk("subnets"); ok { 251 elbOpts.Subnets = expandStringList(v.(*schema.Set).List()) 252 } 253 254 log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts) 255 err = resource.Retry(1*time.Minute, func() *resource.RetryError { 256 _, err := elbconn.CreateLoadBalancer(elbOpts) 257 258 if err != nil { 259 if awsErr, ok := err.(awserr.Error); ok { 260 // Check for IAM SSL Cert error, eventual consistancy issue 261 if awsErr.Code() == "CertificateNotFound" { 262 return resource.RetryableError( 263 fmt.Errorf("[WARN] Error creating ELB Listener with SSL Cert, retrying: %s", err)) 264 } 265 } 266 return resource.NonRetryableError(err) 267 } 268 return nil 269 }) 270 271 if err != nil { 272 return err 273 } 274 275 // Assign the elb's unique identifier for use later 276 d.SetId(elbName) 277 log.Printf("[INFO] ELB ID: %s", d.Id()) 278 279 // Enable partial mode and record what we set 280 d.Partial(true) 281 d.SetPartial("name") 282 d.SetPartial("internal") 283 d.SetPartial("availability_zones") 284 d.SetPartial("listener") 285 d.SetPartial("security_groups") 286 d.SetPartial("subnets") 287 288 d.Set("tags", tagsToMapELB(tags)) 289 290 return resourceAwsElbUpdate(d, meta) 291 } 292 293 func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { 294 elbconn := meta.(*AWSClient).elbconn 295 elbName := d.Id() 296 297 // Retrieve the ELB properties for updating the state 298 describeElbOpts := &elb.DescribeLoadBalancersInput{ 299 LoadBalancerNames: []*string{aws.String(elbName)}, 300 } 301 302 describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) 303 if err != nil { 304 if isLoadBalancerNotFound(err) { 305 // The ELB is gone now, so just remove it from the state 306 d.SetId("") 307 return nil 308 } 309 310 return fmt.Errorf("Error retrieving ELB: %s", err) 311 } 312 if len(describeResp.LoadBalancerDescriptions) != 1 { 313 return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) 314 } 315 316 describeAttrsOpts := &elb.DescribeLoadBalancerAttributesInput{ 317 LoadBalancerName: aws.String(elbName), 318 } 319 describeAttrsResp, err := elbconn.DescribeLoadBalancerAttributes(describeAttrsOpts) 320 if err != nil { 321 if isLoadBalancerNotFound(err) { 322 // The ELB is gone now, so just remove it from the state 323 d.SetId("") 324 return nil 325 } 326 327 return fmt.Errorf("Error retrieving ELB: %s", err) 328 } 329 330 lbAttrs := describeAttrsResp.LoadBalancerAttributes 331 332 lb := describeResp.LoadBalancerDescriptions[0] 333 334 d.Set("name", *lb.LoadBalancerName) 335 d.Set("dns_name", *lb.DNSName) 336 d.Set("zone_id", *lb.CanonicalHostedZoneNameID) 337 d.Set("internal", *lb.Scheme == "internal") 338 d.Set("availability_zones", flattenStringList(lb.AvailabilityZones)) 339 d.Set("instances", flattenInstances(lb.Instances)) 340 d.Set("listener", flattenListeners(lb.ListenerDescriptions)) 341 d.Set("security_groups", flattenStringList(lb.SecurityGroups)) 342 if lb.SourceSecurityGroup != nil { 343 group := lb.SourceSecurityGroup.GroupName 344 if lb.SourceSecurityGroup.OwnerAlias != nil && *lb.SourceSecurityGroup.OwnerAlias != "" { 345 group = aws.String(*lb.SourceSecurityGroup.OwnerAlias + "/" + *lb.SourceSecurityGroup.GroupName) 346 } 347 d.Set("source_security_group", group) 348 349 // Manually look up the ELB Security Group ID, since it's not provided 350 var elbVpc string 351 if lb.VPCId != nil { 352 elbVpc = *lb.VPCId 353 sgId, err := sourceSGIdByName(meta, *lb.SourceSecurityGroup.GroupName, elbVpc) 354 if err != nil { 355 return fmt.Errorf("[WARN] Error looking up ELB Security Group ID: %s", err) 356 } else { 357 d.Set("source_security_group_id", sgId) 358 } 359 } 360 } 361 d.Set("subnets", flattenStringList(lb.Subnets)) 362 d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) 363 d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) 364 d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) 365 if lbAttrs.AccessLog != nil { 366 if err := d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)); err != nil { 367 return err 368 } 369 } 370 371 resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ 372 LoadBalancerNames: []*string{lb.LoadBalancerName}, 373 }) 374 375 var et []*elb.Tag 376 if len(resp.TagDescriptions) > 0 { 377 et = resp.TagDescriptions[0].Tags 378 } 379 d.Set("tags", tagsToMapELB(et)) 380 381 // There's only one health check, so save that to state as we 382 // currently can 383 if *lb.HealthCheck.Target != "" { 384 d.Set("health_check", flattenHealthCheck(lb.HealthCheck)) 385 } 386 387 return nil 388 } 389 390 func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { 391 elbconn := meta.(*AWSClient).elbconn 392 393 d.Partial(true) 394 395 if d.HasChange("listener") { 396 o, n := d.GetChange("listener") 397 os := o.(*schema.Set) 398 ns := n.(*schema.Set) 399 400 remove, _ := expandListeners(os.Difference(ns).List()) 401 add, _ := expandListeners(ns.Difference(os).List()) 402 403 if len(remove) > 0 { 404 ports := make([]*int64, 0, len(remove)) 405 for _, listener := range remove { 406 ports = append(ports, listener.LoadBalancerPort) 407 } 408 409 deleteListenersOpts := &elb.DeleteLoadBalancerListenersInput{ 410 LoadBalancerName: aws.String(d.Id()), 411 LoadBalancerPorts: ports, 412 } 413 414 log.Printf("[DEBUG] ELB Delete Listeners opts: %s", deleteListenersOpts) 415 _, err := elbconn.DeleteLoadBalancerListeners(deleteListenersOpts) 416 if err != nil { 417 return fmt.Errorf("Failure removing outdated ELB listeners: %s", err) 418 } 419 } 420 421 if len(add) > 0 { 422 createListenersOpts := &elb.CreateLoadBalancerListenersInput{ 423 LoadBalancerName: aws.String(d.Id()), 424 Listeners: add, 425 } 426 427 // Occasionally AWS will error with a 'duplicate listener', without any 428 // other listeners on the ELB. Retry here to eliminate that. 429 err := resource.Retry(1*time.Minute, func() *resource.RetryError { 430 log.Printf("[DEBUG] ELB Create Listeners opts: %s", createListenersOpts) 431 if _, err := elbconn.CreateLoadBalancerListeners(createListenersOpts); err != nil { 432 if awsErr, ok := err.(awserr.Error); ok { 433 if awsErr.Code() == "DuplicateListener" { 434 log.Printf("[DEBUG] Duplicate listener found for ELB (%s), retrying", d.Id()) 435 return resource.RetryableError(awsErr) 436 } 437 if awsErr.Code() == "CertificateNotFound" && strings.Contains(awsErr.Message(), "Server Certificate not found for the key: arn") { 438 log.Printf("[DEBUG] SSL Cert not found for given ARN, retrying") 439 return resource.RetryableError(awsErr) 440 } 441 } 442 443 // Didn't recognize the error, so shouldn't retry. 444 return resource.NonRetryableError(err) 445 } 446 // Successful creation 447 return nil 448 }) 449 if err != nil { 450 return fmt.Errorf("Failure adding new or updated ELB listeners: %s", err) 451 } 452 } 453 454 d.SetPartial("listener") 455 } 456 457 // If we currently have instances, or did have instances, 458 // we want to figure out what to add and remove from the load 459 // balancer 460 if d.HasChange("instances") { 461 o, n := d.GetChange("instances") 462 os := o.(*schema.Set) 463 ns := n.(*schema.Set) 464 remove := expandInstanceString(os.Difference(ns).List()) 465 add := expandInstanceString(ns.Difference(os).List()) 466 467 if len(add) > 0 { 468 registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{ 469 LoadBalancerName: aws.String(d.Id()), 470 Instances: add, 471 } 472 473 _, err := elbconn.RegisterInstancesWithLoadBalancer(®isterInstancesOpts) 474 if err != nil { 475 return fmt.Errorf("Failure registering instances with ELB: %s", err) 476 } 477 } 478 if len(remove) > 0 { 479 deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{ 480 LoadBalancerName: aws.String(d.Id()), 481 Instances: remove, 482 } 483 484 _, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts) 485 if err != nil { 486 return fmt.Errorf("Failure deregistering instances from ELB: %s", err) 487 } 488 } 489 490 d.SetPartial("instances") 491 } 492 493 if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") { 494 attrs := elb.ModifyLoadBalancerAttributesInput{ 495 LoadBalancerName: aws.String(d.Get("name").(string)), 496 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 497 CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{ 498 Enabled: aws.Bool(d.Get("cross_zone_load_balancing").(bool)), 499 }, 500 ConnectionSettings: &elb.ConnectionSettings{ 501 IdleTimeout: aws.Int64(int64(d.Get("idle_timeout").(int))), 502 }, 503 }, 504 } 505 506 logs := d.Get("access_logs").([]interface{}) 507 if len(logs) > 1 { 508 return fmt.Errorf("Only one access logs config per ELB is supported") 509 } else if len(logs) == 1 { 510 log := logs[0].(map[string]interface{}) 511 accessLog := &elb.AccessLog{ 512 Enabled: aws.Bool(true), 513 EmitInterval: aws.Int64(int64(log["interval"].(int))), 514 S3BucketName: aws.String(log["bucket"].(string)), 515 } 516 517 if log["bucket_prefix"] != "" { 518 accessLog.S3BucketPrefix = aws.String(log["bucket_prefix"].(string)) 519 } 520 521 attrs.LoadBalancerAttributes.AccessLog = accessLog 522 } else if len(logs) == 0 { 523 // disable access logs 524 attrs.LoadBalancerAttributes.AccessLog = &elb.AccessLog{ 525 Enabled: aws.Bool(false), 526 } 527 } 528 529 log.Printf("[DEBUG] ELB Modify Load Balancer Attributes Request: %#v", attrs) 530 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 531 if err != nil { 532 return fmt.Errorf("Failure configuring ELB attributes: %s", err) 533 } 534 535 d.SetPartial("cross_zone_load_balancing") 536 d.SetPartial("idle_timeout") 537 d.SetPartial("connection_draining_timeout") 538 } 539 540 // We have to do these changes separately from everything else since 541 // they have some weird undocumented rules. You can't set the timeout 542 // without having connection draining to true, so we set that to true, 543 // set the timeout, then reset it to false if requested. 544 if d.HasChange("connection_draining") || d.HasChange("connection_draining_timeout") { 545 // We do timeout changes first since they require us to set draining 546 // to true for a hot second. 547 if d.HasChange("connection_draining_timeout") { 548 attrs := elb.ModifyLoadBalancerAttributesInput{ 549 LoadBalancerName: aws.String(d.Get("name").(string)), 550 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 551 ConnectionDraining: &elb.ConnectionDraining{ 552 Enabled: aws.Bool(true), 553 Timeout: aws.Int64(int64(d.Get("connection_draining_timeout").(int))), 554 }, 555 }, 556 } 557 558 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 559 if err != nil { 560 return fmt.Errorf("Failure configuring ELB attributes: %s", err) 561 } 562 563 d.SetPartial("connection_draining_timeout") 564 } 565 566 // Then we always set connection draining even if there is no change. 567 // This lets us reset to "false" if requested even with a timeout 568 // change. 569 attrs := elb.ModifyLoadBalancerAttributesInput{ 570 LoadBalancerName: aws.String(d.Get("name").(string)), 571 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 572 ConnectionDraining: &elb.ConnectionDraining{ 573 Enabled: aws.Bool(d.Get("connection_draining").(bool)), 574 }, 575 }, 576 } 577 578 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 579 if err != nil { 580 return fmt.Errorf("Failure configuring ELB attributes: %s", err) 581 } 582 583 d.SetPartial("connection_draining") 584 } 585 586 if d.HasChange("health_check") { 587 hc := d.Get("health_check").([]interface{}) 588 if len(hc) > 1 { 589 return fmt.Errorf("Only one health check per ELB is supported") 590 } else if len(hc) > 0 { 591 check := hc[0].(map[string]interface{}) 592 configureHealthCheckOpts := elb.ConfigureHealthCheckInput{ 593 LoadBalancerName: aws.String(d.Id()), 594 HealthCheck: &elb.HealthCheck{ 595 HealthyThreshold: aws.Int64(int64(check["healthy_threshold"].(int))), 596 UnhealthyThreshold: aws.Int64(int64(check["unhealthy_threshold"].(int))), 597 Interval: aws.Int64(int64(check["interval"].(int))), 598 Target: aws.String(check["target"].(string)), 599 Timeout: aws.Int64(int64(check["timeout"].(int))), 600 }, 601 } 602 _, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts) 603 if err != nil { 604 return fmt.Errorf("Failure configuring health check for ELB: %s", err) 605 } 606 d.SetPartial("health_check") 607 } 608 } 609 610 if d.HasChange("security_groups") { 611 groups := d.Get("security_groups").(*schema.Set).List() 612 613 applySecurityGroupsOpts := elb.ApplySecurityGroupsToLoadBalancerInput{ 614 LoadBalancerName: aws.String(d.Id()), 615 SecurityGroups: expandStringList(groups), 616 } 617 618 _, err := elbconn.ApplySecurityGroupsToLoadBalancer(&applySecurityGroupsOpts) 619 if err != nil { 620 return fmt.Errorf("Failure applying security groups to ELB: %s", err) 621 } 622 623 d.SetPartial("security_groups") 624 } 625 626 if d.HasChange("availability_zones") { 627 o, n := d.GetChange("availability_zones") 628 os := o.(*schema.Set) 629 ns := n.(*schema.Set) 630 631 removed := expandStringList(os.Difference(ns).List()) 632 added := expandStringList(ns.Difference(os).List()) 633 634 if len(added) > 0 { 635 enableOpts := &elb.EnableAvailabilityZonesForLoadBalancerInput{ 636 LoadBalancerName: aws.String(d.Id()), 637 AvailabilityZones: added, 638 } 639 640 log.Printf("[DEBUG] ELB enable availability zones opts: %s", enableOpts) 641 _, err := elbconn.EnableAvailabilityZonesForLoadBalancer(enableOpts) 642 if err != nil { 643 return fmt.Errorf("Failure enabling ELB availability zones: %s", err) 644 } 645 } 646 647 if len(removed) > 0 { 648 disableOpts := &elb.DisableAvailabilityZonesForLoadBalancerInput{ 649 LoadBalancerName: aws.String(d.Id()), 650 AvailabilityZones: removed, 651 } 652 653 log.Printf("[DEBUG] ELB disable availability zones opts: %s", disableOpts) 654 _, err := elbconn.DisableAvailabilityZonesForLoadBalancer(disableOpts) 655 if err != nil { 656 return fmt.Errorf("Failure disabling ELB availability zones: %s", err) 657 } 658 } 659 660 d.SetPartial("availability_zones") 661 } 662 663 if d.HasChange("subnets") { 664 o, n := d.GetChange("subnets") 665 os := o.(*schema.Set) 666 ns := n.(*schema.Set) 667 668 removed := expandStringList(os.Difference(ns).List()) 669 added := expandStringList(ns.Difference(os).List()) 670 671 if len(added) > 0 { 672 attachOpts := &elb.AttachLoadBalancerToSubnetsInput{ 673 LoadBalancerName: aws.String(d.Id()), 674 Subnets: added, 675 } 676 677 log.Printf("[DEBUG] ELB attach subnets opts: %s", attachOpts) 678 _, err := elbconn.AttachLoadBalancerToSubnets(attachOpts) 679 if err != nil { 680 return fmt.Errorf("Failure adding ELB subnets: %s", err) 681 } 682 } 683 684 if len(removed) > 0 { 685 detachOpts := &elb.DetachLoadBalancerFromSubnetsInput{ 686 LoadBalancerName: aws.String(d.Id()), 687 Subnets: removed, 688 } 689 690 log.Printf("[DEBUG] ELB detach subnets opts: %s", detachOpts) 691 _, err := elbconn.DetachLoadBalancerFromSubnets(detachOpts) 692 if err != nil { 693 return fmt.Errorf("Failure removing ELB subnets: %s", err) 694 } 695 } 696 697 d.SetPartial("subnets") 698 } 699 700 if err := setTagsELB(elbconn, d); err != nil { 701 return err 702 } 703 704 d.SetPartial("tags") 705 d.Partial(false) 706 707 return resourceAwsElbRead(d, meta) 708 } 709 710 func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error { 711 elbconn := meta.(*AWSClient).elbconn 712 713 log.Printf("[INFO] Deleting ELB: %s", d.Id()) 714 715 // Destroy the load balancer 716 deleteElbOpts := elb.DeleteLoadBalancerInput{ 717 LoadBalancerName: aws.String(d.Id()), 718 } 719 if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil { 720 return fmt.Errorf("Error deleting ELB: %s", err) 721 } 722 723 return nil 724 } 725 726 func resourceAwsElbListenerHash(v interface{}) int { 727 var buf bytes.Buffer 728 m := v.(map[string]interface{}) 729 buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) 730 buf.WriteString(fmt.Sprintf("%s-", 731 strings.ToLower(m["instance_protocol"].(string)))) 732 buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) 733 buf.WriteString(fmt.Sprintf("%s-", 734 strings.ToLower(m["lb_protocol"].(string)))) 735 736 if v, ok := m["ssl_certificate_id"]; ok { 737 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 738 } 739 740 return hashcode.String(buf.String()) 741 } 742 743 func isLoadBalancerNotFound(err error) bool { 744 elberr, ok := err.(awserr.Error) 745 return ok && elberr.Code() == "LoadBalancerNotFound" 746 } 747 748 func sourceSGIdByName(meta interface{}, sg, vpcId string) (string, error) { 749 conn := meta.(*AWSClient).ec2conn 750 var filters []*ec2.Filter 751 var sgFilterName, sgFilterVPCID *ec2.Filter 752 sgFilterName = &ec2.Filter{ 753 Name: aws.String("group-name"), 754 Values: []*string{aws.String(sg)}, 755 } 756 757 if vpcId != "" { 758 sgFilterVPCID = &ec2.Filter{ 759 Name: aws.String("vpc-id"), 760 Values: []*string{aws.String(vpcId)}, 761 } 762 } 763 764 filters = append(filters, sgFilterName) 765 766 if sgFilterVPCID != nil { 767 filters = append(filters, sgFilterVPCID) 768 } 769 770 req := &ec2.DescribeSecurityGroupsInput{ 771 Filters: filters, 772 } 773 resp, err := conn.DescribeSecurityGroups(req) 774 if err != nil { 775 if ec2err, ok := err.(awserr.Error); ok { 776 if ec2err.Code() == "InvalidSecurityGroupID.NotFound" || 777 ec2err.Code() == "InvalidGroup.NotFound" { 778 resp = nil 779 err = nil 780 } 781 } 782 783 if err != nil { 784 log.Printf("Error on ELB SG look up: %s", err) 785 return "", err 786 } 787 } 788 789 if resp == nil || len(resp.SecurityGroups) == 0 { 790 return "", fmt.Errorf("No security groups found for name %s and vpc id %s", sg, vpcId) 791 } 792 793 group := resp.SecurityGroups[0] 794 return *group.GroupId, nil 795 }