func resourceAwsElasticBeanstalkEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticbeanstalkconn

	envId := d.Id()
	waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string))
	if err != nil {
		return err
	}

	updateOpts := elasticbeanstalk.UpdateEnvironmentInput{
		EnvironmentId: aws.String(envId),
	}

	if d.HasChange("description") {
		updateOpts.Description = aws.String(d.Get("description").(string))
	}

	if d.HasChange("solution_stack_name") {
		updateOpts.SolutionStackName = aws.String(d.Get("solution_stack_name").(string))
	}

	if d.HasChange("setting") {
		o, n := d.GetChange("setting")
		if o == nil {
			o = &schema.Set{F: optionSettingValueHash}
		}
		if n == nil {
			n = &schema.Set{F: optionSettingValueHash}
		}

		os := o.(*schema.Set)
		ns := n.(*schema.Set)

		updateOpts.OptionSettings = extractOptionSettings(ns.Difference(os))
	}

	if d.HasChange("template_name") {
		updateOpts.TemplateName = aws.String(d.Get("template_name").(string))
	}

	log.Printf("[DEBUG] Elastic Beanstalk Environment update opts: %s", updateOpts)
	_, err = conn.UpdateEnvironment(&updateOpts)
	if err != nil {
		return err
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{"Launching", "Updating"},
		Target:     []string{"Ready"},
		Refresh:    environmentStateRefreshFunc(conn, d.Id()),
		Timeout:    waitForReadyTimeOut,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf(
			"Error waiting for Elastic Beanstalk Environment (%s) to become ready: %s",
			d.Id(), err)
	}

	return resourceAwsElasticBeanstalkEnvironmentRead(d, meta)
}
func resourceAwsElasticBeanstalkEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticbeanstalkconn

	envId := d.Id()

	var hasChange bool

	updateOpts := elasticbeanstalk.UpdateEnvironmentInput{
		EnvironmentId: aws.String(envId),
	}

	if d.HasChange("description") {
		hasChange = true
		updateOpts.Description = aws.String(d.Get("description").(string))
	}

	if d.HasChange("solution_stack_name") {
		hasChange = true
		if v, ok := d.GetOk("solution_stack_name"); ok {
			updateOpts.SolutionStackName = aws.String(v.(string))
		}
	}

	if d.HasChange("setting") {
		hasChange = true
		o, n := d.GetChange("setting")
		if o == nil {
			o = &schema.Set{F: optionSettingValueHash}
		}
		if n == nil {
			n = &schema.Set{F: optionSettingValueHash}
		}

		os := o.(*schema.Set)
		ns := n.(*schema.Set)

		updateOpts.OptionSettings = extractOptionSettings(ns.Difference(os))
	}

	if d.HasChange("template_name") {
		hasChange = true
		if v, ok := d.GetOk("template_name"); ok {
			updateOpts.TemplateName = aws.String(v.(string))
		}
	}

	if hasChange {
		// Get the current time to filter describeBeanstalkEvents messages
		t := time.Now()
		log.Printf("[DEBUG] Elastic Beanstalk Environment update opts: %s", updateOpts)
		_, err := conn.UpdateEnvironment(&updateOpts)
		if err != nil {
			return err
		}

		waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string))
		if err != nil {
			return err
		}
		pollInterval, err := time.ParseDuration(d.Get("poll_interval").(string))
		if err != nil {
			pollInterval = 0
			log.Printf("[WARN] Error parsing poll_interval, using default backoff")
		}

		stateConf := &resource.StateChangeConf{
			Pending:      []string{"Launching", "Updating"},
			Target:       []string{"Ready"},
			Refresh:      environmentStateRefreshFunc(conn, d.Id()),
			Timeout:      waitForReadyTimeOut,
			Delay:        10 * time.Second,
			PollInterval: pollInterval,
			MinTimeout:   3 * time.Second,
		}

		_, err = stateConf.WaitForState()
		if err != nil {
			return fmt.Errorf(
				"Error waiting for Elastic Beanstalk Environment (%s) to become ready: %s",
				d.Id(), err)
		}

		err = describeBeanstalkEvents(conn, d.Id(), t)
		if err != nil {
			return err
		}
	}

	return resourceAwsElasticBeanstalkEnvironmentRead(d, meta)
}
func resourceAwsElasticBeanstalkEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).elasticbeanstalkconn

	envId := d.Id()

	var hasChange bool

	updateOpts := elasticbeanstalk.UpdateEnvironmentInput{
		EnvironmentId: aws.String(envId),
	}

	if d.HasChange("description") {
		hasChange = true
		updateOpts.Description = aws.String(d.Get("description").(string))
	}

	if d.HasChange("solution_stack_name") {
		hasChange = true
		if v, ok := d.GetOk("solution_stack_name"); ok {
			updateOpts.SolutionStackName = aws.String(v.(string))
		}
	}

	if d.HasChange("setting") {
		hasChange = true
		o, n := d.GetChange("setting")
		if o == nil {
			o = &schema.Set{F: optionSettingValueHash}
		}
		if n == nil {
			n = &schema.Set{F: optionSettingValueHash}
		}

		os := o.(*schema.Set)
		ns := n.(*schema.Set)

		rm := extractOptionSettings(os.Difference(ns))
		add := extractOptionSettings(ns.Difference(os))

		// Additions and removals of options are done in a single API call, so we
		// can't do our normal "remove these" and then later "add these", re-adding
		// any updated settings.
		// Because of this, we need to remove any settings in the "removable"
		// settings that are also found in the "add" settings, otherwise they
		// conflict. Here we loop through all the initial removables from the set
		// difference, and delete from the slice any items found in both `add` and
		// `rm` above
		if len(add) > 0 {
			for i, r := range rm {
				for _, a := range add {
					// ResourceNames are optional. Some defaults come with it, some do
					// not. We need to guard against nil/empty in state as well as
					// nil/empty from the API
					if a.ResourceName != nil {
						if r.ResourceName == nil {
							continue
						}
						if *r.ResourceName != *a.ResourceName {
							continue
						}
					}
					if *r.Namespace == *a.Namespace && *r.OptionName == *a.OptionName {
						log.Printf("[DEBUG] Removing Beanstalk setting: (%s::%s)", *a.Namespace, *a.OptionName)
						rm = append(rm[:i], rm[i+1:]...)
					}
				}
			}
		}

		for _, elem := range rm {
			updateOpts.OptionsToRemove = append(updateOpts.OptionsToRemove, &elasticbeanstalk.OptionSpecification{
				Namespace:  elem.Namespace,
				OptionName: elem.OptionName,
			})
		}

		updateOpts.OptionSettings = add
	}

	if d.HasChange("template_name") {
		hasChange = true
		if v, ok := d.GetOk("template_name"); ok {
			updateOpts.TemplateName = aws.String(v.(string))
		}
	}

	if hasChange {
		// Get the current time to filter describeBeanstalkEvents messages
		t := time.Now()
		log.Printf("[DEBUG] Elastic Beanstalk Environment update opts: %s", updateOpts)
		_, err := conn.UpdateEnvironment(&updateOpts)
		if err != nil {
			return err
		}

		waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string))
		if err != nil {
			return err
		}
		pollInterval, err := time.ParseDuration(d.Get("poll_interval").(string))
		if err != nil {
			pollInterval = 0
			log.Printf("[WARN] Error parsing poll_interval, using default backoff")
		}

		stateConf := &resource.StateChangeConf{
			Pending:      []string{"Launching", "Updating"},
			Target:       []string{"Ready"},
			Refresh:      environmentStateRefreshFunc(conn, d.Id()),
			Timeout:      waitForReadyTimeOut,
			Delay:        10 * time.Second,
			PollInterval: pollInterval,
			MinTimeout:   3 * time.Second,
		}

		_, err = stateConf.WaitForState()
		if err != nil {
			return fmt.Errorf(
				"Error waiting for Elastic Beanstalk Environment (%s) to become ready: %s",
				d.Id(), err)
		}

		err = describeBeanstalkEvents(conn, d.Id(), t)
		if err != nil {
			return err
		}
	}

	return resourceAwsElasticBeanstalkEnvironmentRead(d, meta)
}