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