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