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