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