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