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