github.com/aznashwan/terraform@v0.4.3-0.20151118032030-21f93ca4558d/builtin/providers/aws/resource_aws_elb.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "regexp" 8 "strings" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/ec2" 13 "github.com/aws/aws-sdk-go/service/elb" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 "github.com/hashicorp/terraform/helper/resource" 16 "github.com/hashicorp/terraform/helper/schema" 17 ) 18 19 func resourceAwsElb() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceAwsElbCreate, 22 Read: resourceAwsElbRead, 23 Update: resourceAwsElbUpdate, 24 Delete: resourceAwsElbDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "name": &schema.Schema{ 28 Type: schema.TypeString, 29 Optional: true, 30 Computed: true, 31 ForceNew: true, 32 ValidateFunc: validateElbName, 33 }, 34 35 "internal": &schema.Schema{ 36 Type: schema.TypeBool, 37 Optional: true, 38 ForceNew: true, 39 Computed: true, 40 }, 41 42 "cross_zone_load_balancing": &schema.Schema{ 43 Type: schema.TypeBool, 44 Optional: true, 45 }, 46 47 "availability_zones": &schema.Schema{ 48 Type: schema.TypeSet, 49 Elem: &schema.Schema{Type: schema.TypeString}, 50 Optional: true, 51 ForceNew: true, 52 Computed: true, 53 Set: schema.HashString, 54 }, 55 56 "instances": &schema.Schema{ 57 Type: schema.TypeSet, 58 Elem: &schema.Schema{Type: schema.TypeString}, 59 Optional: true, 60 Computed: true, 61 Set: schema.HashString, 62 }, 63 64 "security_groups": &schema.Schema{ 65 Type: schema.TypeSet, 66 Elem: &schema.Schema{Type: schema.TypeString}, 67 Optional: true, 68 Computed: true, 69 Set: schema.HashString, 70 }, 71 72 "source_security_group": &schema.Schema{ 73 Type: schema.TypeString, 74 Optional: true, 75 Computed: true, 76 }, 77 78 "source_security_group_id": &schema.Schema{ 79 Type: schema.TypeString, 80 Computed: true, 81 }, 82 83 "subnets": &schema.Schema{ 84 Type: schema.TypeSet, 85 Elem: &schema.Schema{Type: schema.TypeString}, 86 Optional: true, 87 ForceNew: true, 88 Computed: true, 89 Set: schema.HashString, 90 }, 91 92 "idle_timeout": &schema.Schema{ 93 Type: schema.TypeInt, 94 Optional: true, 95 Default: 60, 96 }, 97 98 "connection_draining": &schema.Schema{ 99 Type: schema.TypeBool, 100 Optional: true, 101 Default: false, 102 }, 103 104 "connection_draining_timeout": &schema.Schema{ 105 Type: schema.TypeInt, 106 Optional: true, 107 Default: 300, 108 }, 109 110 "access_logs": &schema.Schema{ 111 Type: schema.TypeSet, 112 Optional: true, 113 Elem: &schema.Resource{ 114 Schema: map[string]*schema.Schema{ 115 "interval": &schema.Schema{ 116 Type: schema.TypeInt, 117 Optional: true, 118 Default: 60, 119 }, 120 "bucket": &schema.Schema{ 121 Type: schema.TypeString, 122 Required: true, 123 }, 124 "bucket_prefix": &schema.Schema{ 125 Type: schema.TypeString, 126 Optional: true, 127 }, 128 }, 129 }, 130 Set: resourceAwsElbAccessLogsHash, 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.TypeSet, 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 Set: resourceAwsElbHealthCheckHash, 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 if _, err := elbconn.CreateLoadBalancer(elbOpts); err != nil { 260 return fmt.Errorf("Error creating ELB: %s", err) 261 } 262 263 // Assign the elb's unique identifier for use later 264 d.SetId(elbName) 265 log.Printf("[INFO] ELB ID: %s", d.Id()) 266 267 // Enable partial mode and record what we set 268 d.Partial(true) 269 d.SetPartial("name") 270 d.SetPartial("internal") 271 d.SetPartial("availability_zones") 272 d.SetPartial("listener") 273 d.SetPartial("security_groups") 274 d.SetPartial("subnets") 275 276 d.Set("tags", tagsToMapELB(tags)) 277 278 return resourceAwsElbUpdate(d, meta) 279 } 280 281 func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { 282 elbconn := meta.(*AWSClient).elbconn 283 elbName := d.Id() 284 285 // Retrieve the ELB properties for updating the state 286 describeElbOpts := &elb.DescribeLoadBalancersInput{ 287 LoadBalancerNames: []*string{aws.String(elbName)}, 288 } 289 290 describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) 291 if err != nil { 292 if isLoadBalancerNotFound(err) { 293 // The ELB is gone now, so just remove it from the state 294 d.SetId("") 295 return nil 296 } 297 298 return fmt.Errorf("Error retrieving ELB: %s", err) 299 } 300 if len(describeResp.LoadBalancerDescriptions) != 1 { 301 return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) 302 } 303 304 describeAttrsOpts := &elb.DescribeLoadBalancerAttributesInput{ 305 LoadBalancerName: aws.String(elbName), 306 } 307 describeAttrsResp, err := elbconn.DescribeLoadBalancerAttributes(describeAttrsOpts) 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 318 lbAttrs := describeAttrsResp.LoadBalancerAttributes 319 320 lb := describeResp.LoadBalancerDescriptions[0] 321 322 d.Set("name", *lb.LoadBalancerName) 323 d.Set("dns_name", *lb.DNSName) 324 d.Set("zone_id", *lb.CanonicalHostedZoneNameID) 325 d.Set("internal", *lb.Scheme == "internal") 326 d.Set("availability_zones", lb.AvailabilityZones) 327 d.Set("instances", flattenInstances(lb.Instances)) 328 d.Set("listener", flattenListeners(lb.ListenerDescriptions)) 329 d.Set("security_groups", lb.SecurityGroups) 330 if lb.SourceSecurityGroup != nil { 331 d.Set("source_security_group", lb.SourceSecurityGroup.GroupName) 332 333 // Manually look up the ELB Security Group ID, since it's not provided 334 var elbVpc string 335 if lb.VPCId != nil { 336 elbVpc = *lb.VPCId 337 } 338 sgId, err := sourceSGIdByName(meta, *lb.SourceSecurityGroup.GroupName, elbVpc) 339 if err != nil { 340 return fmt.Errorf("[WARN] Error looking up ELB Security Group ID: %s", err) 341 } else { 342 d.Set("source_security_group_id", sgId) 343 } 344 } 345 d.Set("subnets", lb.Subnets) 346 d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) 347 d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) 348 d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout) 349 if lbAttrs.AccessLog != nil { 350 if err := d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)); err != nil { 351 return err 352 } 353 } 354 355 resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{ 356 LoadBalancerNames: []*string{lb.LoadBalancerName}, 357 }) 358 359 var et []*elb.Tag 360 if len(resp.TagDescriptions) > 0 { 361 et = resp.TagDescriptions[0].Tags 362 } 363 d.Set("tags", tagsToMapELB(et)) 364 // There's only one health check, so save that to state as we 365 // currently can 366 if *lb.HealthCheck.Target != "" { 367 d.Set("health_check", flattenHealthCheck(lb.HealthCheck)) 368 } 369 370 return nil 371 } 372 373 func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { 374 elbconn := meta.(*AWSClient).elbconn 375 376 d.Partial(true) 377 378 if d.HasChange("listener") { 379 o, n := d.GetChange("listener") 380 os := o.(*schema.Set) 381 ns := n.(*schema.Set) 382 383 remove, _ := expandListeners(os.Difference(ns).List()) 384 add, _ := expandListeners(ns.Difference(os).List()) 385 386 if len(remove) > 0 { 387 ports := make([]*int64, 0, len(remove)) 388 for _, listener := range remove { 389 ports = append(ports, listener.LoadBalancerPort) 390 } 391 392 deleteListenersOpts := &elb.DeleteLoadBalancerListenersInput{ 393 LoadBalancerName: aws.String(d.Id()), 394 LoadBalancerPorts: ports, 395 } 396 397 _, err := elbconn.DeleteLoadBalancerListeners(deleteListenersOpts) 398 if err != nil { 399 return fmt.Errorf("Failure removing outdated ELB listeners: %s", err) 400 } 401 } 402 403 if len(add) > 0 { 404 createListenersOpts := &elb.CreateLoadBalancerListenersInput{ 405 LoadBalancerName: aws.String(d.Id()), 406 Listeners: add, 407 } 408 409 _, err := elbconn.CreateLoadBalancerListeners(createListenersOpts) 410 if err != nil { 411 return fmt.Errorf("Failure adding new or updated ELB listeners: %s", err) 412 } 413 } 414 415 d.SetPartial("listener") 416 } 417 418 // If we currently have instances, or did have instances, 419 // we want to figure out what to add and remove from the load 420 // balancer 421 if d.HasChange("instances") { 422 o, n := d.GetChange("instances") 423 os := o.(*schema.Set) 424 ns := n.(*schema.Set) 425 remove := expandInstanceString(os.Difference(ns).List()) 426 add := expandInstanceString(ns.Difference(os).List()) 427 428 if len(add) > 0 { 429 registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{ 430 LoadBalancerName: aws.String(d.Id()), 431 Instances: add, 432 } 433 434 _, err := elbconn.RegisterInstancesWithLoadBalancer(®isterInstancesOpts) 435 if err != nil { 436 return fmt.Errorf("Failure registering instances with ELB: %s", err) 437 } 438 } 439 if len(remove) > 0 { 440 deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{ 441 LoadBalancerName: aws.String(d.Id()), 442 Instances: remove, 443 } 444 445 _, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts) 446 if err != nil { 447 return fmt.Errorf("Failure deregistering instances from ELB: %s", err) 448 } 449 } 450 451 d.SetPartial("instances") 452 } 453 454 if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") { 455 attrs := elb.ModifyLoadBalancerAttributesInput{ 456 LoadBalancerName: aws.String(d.Get("name").(string)), 457 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 458 CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{ 459 Enabled: aws.Bool(d.Get("cross_zone_load_balancing").(bool)), 460 }, 461 ConnectionSettings: &elb.ConnectionSettings{ 462 IdleTimeout: aws.Int64(int64(d.Get("idle_timeout").(int))), 463 }, 464 }, 465 } 466 467 logs := d.Get("access_logs").(*schema.Set).List() 468 if len(logs) > 1 { 469 return fmt.Errorf("Only one access logs config per ELB is supported") 470 } else if len(logs) == 1 { 471 log := logs[0].(map[string]interface{}) 472 accessLog := &elb.AccessLog{ 473 Enabled: aws.Bool(true), 474 EmitInterval: aws.Int64(int64(log["interval"].(int))), 475 S3BucketName: aws.String(log["bucket"].(string)), 476 } 477 478 if log["bucket_prefix"] != "" { 479 accessLog.S3BucketPrefix = aws.String(log["bucket_prefix"].(string)) 480 } 481 482 attrs.LoadBalancerAttributes.AccessLog = accessLog 483 } else if len(logs) == 0 { 484 // disable access logs 485 attrs.LoadBalancerAttributes.AccessLog = &elb.AccessLog{ 486 Enabled: aws.Bool(false), 487 } 488 } 489 490 log.Printf("[DEBUG] ELB Modify Load Balancer Attributes Request: %#v", attrs) 491 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 492 if err != nil { 493 return fmt.Errorf("Failure configuring ELB attributes: %s", err) 494 } 495 496 d.SetPartial("cross_zone_load_balancing") 497 d.SetPartial("idle_timeout") 498 d.SetPartial("connection_draining_timeout") 499 } 500 501 // We have to do these changes separately from everything else since 502 // they have some weird undocumented rules. You can't set the timeout 503 // without having connection draining to true, so we set that to true, 504 // set the timeout, then reset it to false if requested. 505 if d.HasChange("connection_draining") || d.HasChange("connection_draining_timeout") { 506 // We do timeout changes first since they require us to set draining 507 // to true for a hot second. 508 if d.HasChange("connection_draining_timeout") { 509 attrs := elb.ModifyLoadBalancerAttributesInput{ 510 LoadBalancerName: aws.String(d.Get("name").(string)), 511 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 512 ConnectionDraining: &elb.ConnectionDraining{ 513 Enabled: aws.Bool(true), 514 Timeout: aws.Int64(int64(d.Get("connection_draining_timeout").(int))), 515 }, 516 }, 517 } 518 519 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 520 if err != nil { 521 return fmt.Errorf("Failure configuring ELB attributes: %s", err) 522 } 523 524 d.SetPartial("connection_draining_timeout") 525 } 526 527 // Then we always set connection draining even if there is no change. 528 // This lets us reset to "false" if requested even with a timeout 529 // change. 530 attrs := elb.ModifyLoadBalancerAttributesInput{ 531 LoadBalancerName: aws.String(d.Get("name").(string)), 532 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 533 ConnectionDraining: &elb.ConnectionDraining{ 534 Enabled: aws.Bool(d.Get("connection_draining").(bool)), 535 }, 536 }, 537 } 538 539 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 540 if err != nil { 541 return fmt.Errorf("Failure configuring ELB attributes: %s", err) 542 } 543 544 d.SetPartial("connection_draining") 545 } 546 547 if d.HasChange("health_check") { 548 vs := d.Get("health_check").(*schema.Set).List() 549 if len(vs) > 0 { 550 check := vs[0].(map[string]interface{}) 551 configureHealthCheckOpts := elb.ConfigureHealthCheckInput{ 552 LoadBalancerName: aws.String(d.Id()), 553 HealthCheck: &elb.HealthCheck{ 554 HealthyThreshold: aws.Int64(int64(check["healthy_threshold"].(int))), 555 UnhealthyThreshold: aws.Int64(int64(check["unhealthy_threshold"].(int))), 556 Interval: aws.Int64(int64(check["interval"].(int))), 557 Target: aws.String(check["target"].(string)), 558 Timeout: aws.Int64(int64(check["timeout"].(int))), 559 }, 560 } 561 _, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts) 562 if err != nil { 563 return fmt.Errorf("Failure configuring health check for ELB: %s", err) 564 } 565 d.SetPartial("health_check") 566 } 567 } 568 569 if d.HasChange("security_groups") { 570 groups := d.Get("security_groups").(*schema.Set).List() 571 572 applySecurityGroupsOpts := elb.ApplySecurityGroupsToLoadBalancerInput{ 573 LoadBalancerName: aws.String(d.Id()), 574 SecurityGroups: expandStringList(groups), 575 } 576 577 _, err := elbconn.ApplySecurityGroupsToLoadBalancer(&applySecurityGroupsOpts) 578 if err != nil { 579 return fmt.Errorf("Failure applying security groups to ELB: %s", err) 580 } 581 582 d.SetPartial("security_groups") 583 } 584 585 if err := setTagsELB(elbconn, d); err != nil { 586 return err 587 } 588 589 d.SetPartial("tags") 590 d.Partial(false) 591 592 return resourceAwsElbRead(d, meta) 593 } 594 595 func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error { 596 elbconn := meta.(*AWSClient).elbconn 597 598 log.Printf("[INFO] Deleting ELB: %s", d.Id()) 599 600 // Destroy the load balancer 601 deleteElbOpts := elb.DeleteLoadBalancerInput{ 602 LoadBalancerName: aws.String(d.Id()), 603 } 604 if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil { 605 return fmt.Errorf("Error deleting ELB: %s", err) 606 } 607 608 return nil 609 } 610 611 func resourceAwsElbHealthCheckHash(v interface{}) int { 612 var buf bytes.Buffer 613 m := v.(map[string]interface{}) 614 buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int))) 615 buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int))) 616 buf.WriteString(fmt.Sprintf("%s-", m["target"].(string))) 617 buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) 618 buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int))) 619 620 return hashcode.String(buf.String()) 621 } 622 623 func resourceAwsElbAccessLogsHash(v interface{}) int { 624 var buf bytes.Buffer 625 m := v.(map[string]interface{}) 626 buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) 627 buf.WriteString(fmt.Sprintf("%s-", 628 strings.ToLower(m["bucket"].(string)))) 629 if v, ok := m["bucket_prefix"]; ok { 630 buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(v.(string)))) 631 } 632 633 return hashcode.String(buf.String()) 634 } 635 636 func resourceAwsElbListenerHash(v interface{}) int { 637 var buf bytes.Buffer 638 m := v.(map[string]interface{}) 639 buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) 640 buf.WriteString(fmt.Sprintf("%s-", 641 strings.ToLower(m["instance_protocol"].(string)))) 642 buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) 643 buf.WriteString(fmt.Sprintf("%s-", 644 strings.ToLower(m["lb_protocol"].(string)))) 645 646 if v, ok := m["ssl_certificate_id"]; ok { 647 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 648 } 649 650 return hashcode.String(buf.String()) 651 } 652 653 func isLoadBalancerNotFound(err error) bool { 654 elberr, ok := err.(awserr.Error) 655 return ok && elberr.Code() == "LoadBalancerNotFound" 656 } 657 658 func validateElbName(v interface{}, k string) (ws []string, errors []error) { 659 value := v.(string) 660 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 661 errors = append(errors, fmt.Errorf( 662 "only alphanumeric characters and hyphens allowed in %q: %q", 663 k, value)) 664 } 665 if len(value) > 32 { 666 errors = append(errors, fmt.Errorf( 667 "%q cannot be longer than 32 characters: %q", k, value)) 668 } 669 if regexp.MustCompile(`^-`).MatchString(value) { 670 errors = append(errors, fmt.Errorf( 671 "%q cannot begin with a hyphen: %q", k, value)) 672 } 673 if regexp.MustCompile(`-$`).MatchString(value) { 674 errors = append(errors, fmt.Errorf( 675 "%q cannot end with a hyphen: %q", k, value)) 676 } 677 return 678 679 } 680 681 func sourceSGIdByName(meta interface{}, sg, vpcId string) (string, error) { 682 conn := meta.(*AWSClient).ec2conn 683 var filters []*ec2.Filter 684 var sgFilterName, sgFilterVPCID *ec2.Filter 685 sgFilterName = &ec2.Filter{ 686 Name: aws.String("group-name"), 687 Values: []*string{aws.String(sg)}, 688 } 689 690 if vpcId != "" { 691 sgFilterVPCID = &ec2.Filter{ 692 Name: aws.String("vpc-id"), 693 Values: []*string{aws.String(vpcId)}, 694 } 695 } 696 697 filters = append(filters, sgFilterName) 698 699 if sgFilterVPCID != nil { 700 filters = append(filters, sgFilterVPCID) 701 } 702 703 req := &ec2.DescribeSecurityGroupsInput{ 704 Filters: filters, 705 } 706 resp, err := conn.DescribeSecurityGroups(req) 707 if err != nil { 708 if ec2err, ok := err.(awserr.Error); ok { 709 if ec2err.Code() == "InvalidSecurityGroupID.NotFound" || 710 ec2err.Code() == "InvalidGroup.NotFound" { 711 resp = nil 712 err = nil 713 } 714 } 715 716 if err != nil { 717 log.Printf("Error on ELB SG look up: %s", err) 718 return "", err 719 } 720 } 721 722 if resp == nil || len(resp.SecurityGroups) == 0 { 723 return "", fmt.Errorf("No security groups found for name %s and vpc id %s", sg, vpcId) 724 } 725 726 group := resp.SecurityGroups[0] 727 return *group.GroupId, nil 728 }