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(&registerInstancesOpts)
   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  }